Merged options into Dan-in-CA latest version

Improved options handling code: simplified passing of options, factored
out option UI details into data file, added new options for name and
station name length, and brought logging options into the main options
page.  Fixed some IE rendering issues and made tooltips a full toggle
as well.
This commit is contained in:
Jonathan Marsh 2013-09-28 20:19:10 -07:00
parent eb47890e1d
commit be16ebe3e3
15 changed files with 378 additions and 305 deletions

View File

@ -12,6 +12,23 @@ UPDATES
===========
***********
September 28 2013 (jonathanmarsh)
--------------
Additions, bug fixes:<br/>
1. Improved options handling and passing logic
2. Added a "System Name" option to help users distinguish between multiple systems
3. Configurable station name length (increased default to 32)
4. Added logging options to options page
September 23 2013
--------------
Additions, bug fixes:<br/>
1. Added a new revisions page to the native web interface.
2. Modified the home.js file to show time zone info in the last run log near the bottom of the page.
3. Fixed a bug in concurrent mode that kept a station running after it's duration had expired.
4. Fixed a bug that would cause an exception (freeze the program) after the number of expansion boards was changed in Options.
5. Fixed a bug that would stop a running station and clear scheduled stations when the number of expansion boards was changed in Options.
September 10 2013
--------------
Additions, bug fixes:<br/>

View File

@ -1 +1,3 @@
<meta name=viewport content="width=640">
<meta name=viewport content="width=640">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="./static/images/icons/favicon.ico" rel="icon" type="image/x-icon"/>

18
data/options.txt Normal file
View File

@ -0,0 +1,18 @@
var opts=[
["System name","string","name","Unique name of this OpenSprinkler system."],
["HTTP port","int","htp", "HTTP port (effective after reboot)."],
["Location","string","loc", "City name or zip code. Use comma or + in place of space."],
["Time zone","int","tz", "Example: GMT-4:00, GMT+5:30 (effective after reboot)."],
["Sequential","boolean","seq", "Sequential or concurrent running mode"],
["Extension boards","int","nbrd", "Number of extension boards"],
["Station delay","int","sdt", "Station delay time (in seconds), between 0 and 240."],
["Master station","int","mas", "Select master station"],
["Master on adjust","int","mton", "Master on delay (in seconds), between +0 and +60."],
["Master off adjust","int","mtoff", "Master off delay (in seconds), between -60 and +60."],
["Use rain sensor","boolean","urs", "Use rain sensor"],
["Normally open","boolean","rst", "Rain sensor type"],
["Water level (%)","int","wl", "Water level, between 0% and 250%."],
["Enable logging","boolean","lg", "Log all events - note that repetitive writing to an SD card can shorten its lifespan."],
["Maximum log entries","int","lr", "Length of log to keep, 0=no limits."],
["Ignore password","boolean","ipas", "Ignore web password"]
];

349
ospi.py
View File

@ -1,9 +1,13 @@
#!/usr/bin/python
"""Updated 10/September/2013."""
"""Updated 19/September/2013."""
import re, os, json, time, base64, thread # standard Python modules
import web # the Web.py module. See webpy.org (Enables the OpenSprinkler web interface)
import gv # 'global vars' An empty module, used for storing vars (as attributes), that need to be 'global' across threads and between functions and classes.
import RPi.GPIO as GPIO # Required for accessing General Purpose Input Output pins on Raspberry Pi
#### Revision information ####
gv.ver = 183
gv.rev = 135
gv.rev_date = '23/September/2013'
#### urls is a feature of web.py. When a GET request is recieved , the corrisponding class is exicuted.
urls = [
@ -27,6 +31,7 @@ urls = [
'/lo', 'log_options',
'/rp', 'run_now',
'/ttu', 'toggle_temp',
'/rev', 'show_revision',
]
#### Import ospi_addon module (ospi_addon.py) if it exists. ####
@ -121,7 +126,7 @@ def prog_match(prog):
return 1 # Program matched
return 0
def schedule_stations():
def schedule_stations(stations):
"""Schedule stattions/valves/zones to run."""
if gv.sd['rd'] or (gv.sd['urs'] and gv.sd['rs']): # If rain delay or rain detected by sensor
rain = True
@ -129,7 +134,7 @@ def schedule_stations():
rain = False
accumulate_time = gv.now
if gv.sd['seq']: #sequential mode, stations run one after another
for b in range(gv.sd['nbrd']):
for b in range(len(stations)):
for s in range(8):
sid = b*8 + s # station index
if gv.rs[sid][2]: # if station has a duration value
@ -139,20 +144,22 @@ def schedule_stations():
gv.rs[sid][1] = accumulate_time # set new stop time
accumulate_time += gv.sd['sdt'] # add station delay
else:
gv.sbits[b] = gv.sbits[b]&~2**s
gv.sbits[b] = gv.sbits[b]&~1<<s
gv.ps[s] = [0,0]
else: # concurrent mode, stations allowed to run in parallel
for b in range(gv.sd['nbrd']):
for b in range(len(stations)):
for s in range(8):
sid = b*8 + s # station index
if not stations[b]&1<<s: # skip stations not in prog
continue
if gv.rs[sid][2]: # if station has a duration value
if not rain or gv.sd['ir'][b]&1<<s: # if no rain or station ignores rain
gv.rs[sid][0] = accumulate_time # set start time
gv.rs[sid][1] = accumulate_time + gv.rs[sid][2] # Stop time = Start time + duration
gv.rs[sid][0] = gv.now #accumulate_time # set start time
gv.rs[sid][1] = (gv.now + gv.rs[sid][2]) # set stop time
else: # if rain and station does not ignore, clear station from display
gv.sbits[b] = gv.sbits[b]&~2**s
gv.ps[s] = [0,0]
gv.ps[s] = [0,0]
gv.sd['bsy'] = 1
return
@ -188,7 +195,6 @@ def main_loop(): # Runs in a seperate thread
print 'Starting main loop \n'
last_min = 0
while True: # infinite loop
match = 0
gv.now = time.time()+((gv.sd['tz']/4)-12)*3600 # Current time based on UTC time from the Pi adjusted by the Time Zone setting from options. updated once per second.
if gv.sd['en'] and not gv.sd['mm'] and (not gv.sd['bsy'] or not gv.sd['seq']): # and not gv.sd['rd']:
lt = time.gmtime(gv.now)
@ -196,31 +202,40 @@ def main_loop(): # Runs in a seperate thread
last_min = (lt[3]*60)+lt[4]
for i, p in enumerate(gv.pd): # get both index and prog item
if prog_match(p) and p[0] and p[6]: # check if program time matches current time, is active, and has a duration
for b in range(gv.sd['nbrd']): # check each station
duration = p[6]*gv.sd['wl']/100 # program duration scaled by "water level"
for b in range(len(p[7:7+gv.sd['nbrd']])): # check each station for boards listed in program up to number of boards in Options
for s in range(8):
sid = b*8+s # station index
if sid+1 == gv.sd['mas']: continue # skip if this is master valve
if sid+1 == gv.sd['mas']: continue # skip if this is master station
if gv.srvals[sid]: continue # skip if currently on
if p[7+b]&1<<s: # if this station is scheduled in this program
gv.rs[sid][2] = p[6]*gv.sd['wl']/100 # duration scaled by water level
gv.rs[sid][3] = i+1 # store program number
gv.ps[sid][0] = i+1 # store program number for display
gv.ps[sid][1] = gv.rs[sid][2] # duration
match = True
if match:
schedule_stations() # turns on gv.sd['bsy']
if gv.sd['seq']: # sequential mode
gv.rs[sid][2] = duration # p[6]*gv.sd['wl']/100 # store duration scaled by water level
gv.rs[sid][3] = i+1 # store program number for scheduling
gv.ps[sid][0] = i+1 # store program number for display
gv.ps[sid][1] = duration
else: # concurrent mode
if duration < gv.rs[sid][2]: # If duration is shortter than any already set for this station
continue
else:
gv.rs[sid][2] = duration
gv.rs[sid][3] = i+1 # store program number
gv.ps[sid][0] = i+1 # store program number for display
gv.ps[sid][1] = duration
schedule_stations(p[7:7+gv.sd['nbrd']]) # turns on gv.sd['bsy']
if gv.sd['bsy']:
for b in range(gv.sd['nbrd']):
for b in range(gv.sd['nbrd']): # Check each station once a second
for s in range(8):
sid = b*8 + s # station index
if gv.srvals[sid]: # if this station is on
if gv.now >= gv.rs[sid][1]: # check if time is up
gv.srvals[sid] = 0
set_output()
gv.sbits[b] = gv.sbits[b]&~2**s
if gv.sd['mas']-1 != sid: # if not master, fill out log
gv.sbits[b] = gv.sbits[b]&~2**s
gv.ps[sid] = [0,0]
gv.lrun[0] = sid
gv.lrun[1] = gv.rs[sid][3]
@ -228,29 +243,28 @@ def main_loop(): # Runs in a seperate thread
gv.lrun[3] = gv.now
log_run()
gv.pon = None # Program has ended
elif gv.sd['mas']-1 == sid:
gv.sbits[b] = gv.sbits[b]&~2**s
gv.rs[sid] = [0,0,0,0]
else: # if this station is not yet on
if gv.now >= gv.rs[sid][0] and gv.now < gv.rs[sid][1]:
if gv.sd['mas']-1 != sid: # if not master
gv.srvals[sid] = 1 # station is turned on
set_output()
gv.sbits[b] = gv.sbits[b]|2**s # Set display to on
gv.sbits[b] = gv.sbits[b]|1<<s # Set display to on
gv.ps[sid][0] = gv.rs[sid][3]
gv.ps[sid][1] = gv.rs[sid][2]
gv.ps[sid][1] = gv.rs[sid][2]+1 ### testing display
if gv.sd['mas'] and gv.sd['mo'][b]&1<<(s-(s/8)*80):# Master settings
masid = gv.sd['mas'] - 1 # master index
gv.rs[masid][0] = gv.rs[sid][0] + gv.sd['mton']
gv.rs[masid][1] = gv.rs[sid][1] + gv.sd['mtoff']
gv.rs[masid][3] = gv.rs[sid][3]
elif gv.sd['mas'] == sid+1:
gv.sbits[b] = gv.sbits[b]|2**sid #(gv.sd['mas'] - 1)
gv.sbits[b] = gv.sbits[b]|1<<sid #(gv.sd['mas'] - 1)
gv.srvals[masid] = 1
set_output()
for s in range(gv.sd['nst']):
if gv.rs[s][1]: # if any station is running
if gv.rs[s][1]: # if any station is scheduled
#if gv.srvals[s]: # if any station is on
program_running = True
gv.pon = gv.rs[s][3] # Store number of running program
break
@ -260,13 +274,11 @@ def main_loop(): # Runs in a seperate thread
if program_running:
if gv.sd['urs'] and gv.sd['rs']: # Stop stations if use rain sensor and rain detected.
stop_onrain() #### Should clear schedule for stations that do not ignore rain ####
for idx in range(len(gv.ps)): # loop through program schedule (gv.ps)
if gv.ps[idx][1] == 0: # skip stations with no duration
for idx in range(len(gv.rs)): # loop through program schedule (gv.ps) #### MAYBE SB gv.rs
if gv.rs[idx][2] == 0: # skip stations with no duration
continue
if gv.srvals[idx]: # If station is on, decrement time remaining
if gv.srvals[idx]: # If station is on, decrement time remaining display
gv.ps[idx][1] -= 1
if gv.ps[idx][1] == 0:
gv.ps[idx][0] = 0
if not program_running:
gv.srvals = [0]*(gv.sd['nst'])
@ -379,6 +391,7 @@ def to_sec(d=0, h=0, m=0, s=0):
#### Global vars #####
try:
sdf = open('./data/sd.json', 'r') ## A config file ##
gv.sd = json.load(sdf) #Settings Dictionary. A set of vars kept in memory and persisted in a file
@ -393,13 +406,17 @@ try:
if not 'lr' in gv.sd: gv.sd['lr'] = 100
if not 'seq' in gv.sd: gv.sd['seq'] = 1
if not 'tu' in gv.sd: gv.sd['tu'] = "C"
if not 'ir' in gv.sd: gv.sd['ir'] = [0]
if not 'ir' in gv.sd: gv.sd['ir'] = [0]#*gv.sd['nbrd']
if not 'loc' in gv.sd: gv.sd['loc'] = ""
if not 'snlen' in gv.sd: gv.sd['snlen'] = 32
if not 'name' in gv.sd: gv.sd['name'] = "OpenSprinkler Pi"
except IOError: # If file does not exist, create with defaults.
gv.sd = ({"en": 1, "seq": 1, "mnp": 32, "ir": [0], "rsn": 0, "htp": 8080, "nst": 8,
"rdst": 0, "loc": "", "tz": 48, "rs": 0, "rd": 0, "mton": 0,
"lr": "100", "sdt": 0, "mas": 0, "wl": 100, "bsy": 0, "lg": "",
"urs": 0, "nopts": 13, "pwd": "b3BlbmRvb3I=", "ipas": 0, "rst": 1,
"mm": 0, "mo": [0], "rbt": 0, "mtoff": 0, "nprogs": 1, "nbrd": 1, "tu": "C"})
"mm": 0, "mo": [0], "rbt": 0, "mtoff": 0, "nprogs": 1, "nbrd": 1, "tu": "C",
"snlen":32, "name":"OpenSprinkler Pi"})
sdf = open('./data/sd.json', 'w')
json.dump(gv.sd, sdf)
sdf.close()
@ -415,6 +432,8 @@ except KeyError:
sdref = {'15':'nbrd', '16':'seq', '18':'mas', '21':'urs', '23':'wl', '25':'ipas'} #lookup table (Dictionary)
gv.now = time.time()+((gv.sd['tz']/4)-12)*3600
gv.srvals = [0]*(gv.sd['nst']) #Shift Register values
gv.rovals = [0]* gv.sd['nbrd']*7 #Run Once durations
@ -480,17 +499,27 @@ def setShiftRegister(srvals):
##################
def pass_options(opts):
optstring = "var sd = {\n"
for o in opts:
optstring += "\t" + o + " : "
if (type(gv.sd[o]) == unicode):
optstring += "'" + gv.sd[o] + "'"
else:
optstring += str(gv.sd[o])
optstring += ",\n"
optstring = optstring[:-2] + "\n}\n"
return optstring
#### Class Definitions ####
class home:
"""Open Home page."""
def GET(self):
homepg = '<!DOCTYPE html>\n'
homepg += data('meta')+'\n'
homepg += '<link href="./static/images/icons/favicon.ico" rel="icon" type="image/x-icon" />\n'
homepg += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\n'
homepg += data('meta')
homepg += '<script>var baseurl=\"'+baseurl()+'\"</script>\n'
homepg += '<script>var ver=183,devt='+str(gv.now)+';var nbrd='+str(gv.sd['nbrd'])+',tz='+str(gv.sd['tz'])+';</script>\n'
homepg += '<script>var en='+str(gv.sd['en'])+',rd='+str(gv.sd['rd'])+',mm='+str(gv.sd['mm'])+',rdst='+str(gv.sd['rdst'])+',mas='+str(gv.sd['mas'])+',urs='+str(gv.sd['urs'])+',rs='+str(gv.sd['rs'])+',ir='+str(gv.sd['ir'])+',wl='+str(gv.sd['wl'])+',ipas='+str(gv.sd['ipas'])+',loc="'+str(gv.sd['loc'])+'";</script>\n'
homepg += '<script>var ver='+str(gv.ver)+',devt='+str(gv.now)+';</script>\n'
homepg += '<script>' + pass_options(["nbrd","tz","en","rd","rs","mm","rdst","mas","urs","rs","wl","ipas","nopts","loc","name","ir"]) + '</script>\n'
homepg += '<script>var sbits='+str(gv.sbits).replace(' ', '')+',ps='+str(gv.ps).replace(' ', '')+';</script>\n'
homepg += '<script>var lrun='+str(gv.lrun).replace(' ', '')+';</script>\n'
homepg += '<script>var snames='+data('snames')+'; var tempunit="'+str(gv.sd['tu'])+'";</script>\n'
@ -523,7 +552,6 @@ class change_values:
if qdict.has_key('mm') and qdict['mm'] == '0': clear_mm() #self.clear_mm()
if qdict.has_key('rd') and qdict['rd'] != '0':
gv.sd['rdst'] = (gv.now+(int(qdict['rd'])*3600))
#stop_stations()
stop_onrain()
elif qdict.has_key('rd') and qdict['rd'] == '0': gv.sd['rdst'] = 0
if qdict.has_key('rbt') and qdict['rbt'] == '1':
@ -545,15 +573,8 @@ class view_options:
"""Open the options page for viewing and editing."""
def GET(self):
optpg = '<!DOCTYPE html>\n'
optpg += data('meta')+'\n'
optpg += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\n'
optpg += '<link href="./static/images/icons/favicon.ico" rel="icon" type="image/x-icon" />\n'
optpg += '<script>var baseurl=\"'+baseurl()+'\"</script>\n'
optpg += '<script>var opts=["Time zone:",0,'+str(gv.sd['tz'])+',1,"HTTP port:",0,'+str(gv.sd['htp'])+',12,"",0,0,13,"Ext. boards:",\
0,'+str(gv.sd['nbrd']-1)+',15,"Sequential:",1,'+str(gv.sd['seq'])+',16,"Station delay:",0,'+str(gv.sd['sdt'])+',17,"Master station:",0,'+str(gv.sd['mas'])+',18,"Mas. on adj.:",0,'+str(gv.sd['mton'])+',19,"Mas. off adj.:",0,'+str(gv.sd['mtoff'])+',20,\
"Use rain sensor:",1,'+str(gv.sd['urs'])+',21,"Normally open:",1,'+str(gv.sd['rst'])+',22,"Water level (%):",0,'+str(gv.sd['wl'])+',23,\
"Ignore password:",1,'+str(gv.sd['ipas'])+',25,0];</script>\n'
optpg += '<script>var nopts='+str(gv.sd['nopts'])+',loc="'+str(gv.sd['loc'])+'";</script>\n'
optpg += data('meta')
optpg += '<script>var baseurl=\"'+baseurl()+'\";\n' + pass_options(["tz","htp","nbrd","sdt","seq","mas","mton","mtoff","urs","wl","ipas","rst","loc","name","lr","lg"]) + data('options')+ '</script>\n'
optpg += '<script src=\"'+baseurl()+'/static/scripts/java/svc1.8.3/viewoptions.js\"></script>'
return optpg
@ -562,85 +583,77 @@ class change_options:
def GET(self):
qdict = web.input()
try:
if not qdict.has_key('o25') and qdict['pw'] != base64.b64decode(gv.sd['pwd']):
if gv.sd['ipas'] == 0 and qdict['pw'] != base64.b64decode(gv.sd['pwd']):
raise web.unauthorized()
return
elif qdict.has_key('o25') and gv.sd['ipas'] == 0 and qdict['pw'] != base64.b64decode(gv.sd['pwd']):
raise web.unauthorized()
return
elif qdict.has_key('o25') and gv.sd['ipas'] == 0 and qdict['pw'] == base64.b64decode(gv.sd['pwd']):
if qdict.has_key('oipas') and (qdict['oipas'] == 'on' or qdict['oipas'] == ''):
gv.sd['ipas'] = 1
else:
gv.sd['ipas'] = 0
except KeyError:
pass
try:
if qdict['cpw'] !='' and qdict['cpw'] == qdict['npw']:
gv.sd['pwd'] = base64.b64encode(qdict['npw'])
except KeyError:
pass
vstr = data('options')
if vstr.find("Sequential:") == -1:
os.remove("./data/options.txt")
vstr = data('options')
ops = vstr.index('[')+1
ope = vstr.index(']')
optstr = vstr[ops:ope]
optlst = optstr.split(',')
onumlst = []
i=3
while i < len(optlst):
onumlst.append(optlst[i].replace(' ', ''))
if optlst[i-2] == '1': #clear check box items
optlst[i-1]= '0'
try:
sdref[optlst[i]];
gv.sd[sdref[optlst[i]]]=0
except KeyError:
pass
i+=4
for key in qdict.keys():
if key[:1] == 'o':
oidx = onumlst.index(key[1:])
if qdict[key] == 'on' or '':
qdict[key] = '1'
optlst[(oidx*4)+2] = qdict[key]
optstr = ','.join(optlst)
optstr = optstr.replace(', ', ',')
vstr = vstr.replace(vstr[ops:ope], optstr)
save('options', vstr)
if int(qdict['o15'])+1 != gv.sd['nbrd']: self.update_scount(qdict)
if int(qdict['o18']) != gv.sd['mas']:
clear_mm()
self.update_sd(qdict)
pass
gv.sd['name'] = qdict['oname']
gv.sd['loc'] = qdict['oloc']
gv.sd['tz'] = int(qdict['otz'])
if int(qdict['onbrd'])+1 != gv.sd['nbrd']: self.update_scount(qdict)
gv.sd['nbrd'] = int(qdict['onbrd'])+1
gv.sd['nst'] = gv.sd['nbrd']*8
gv.sd['htp']= int(qdict['ohtp'])
gv.sd['sdt']= int(qdict['osdt'])
gv.sd['mas'] = int(qdict['omas'])
gv.sd['mton']= int(qdict['omton'])
gv.sd['mtoff']= int(qdict['omtoff'])
gv.sd['wl'] = int(qdict['owl'])
if qdict.has_key('ours') and (qdict['ours'] == 'on' or qdict['ours'] == ''):
gv.sd['urs'] = 1
else:
gv.sd['urs'] = 0
if qdict.has_key('oseq') and (qdict['oseq'] == 'on' or qdict['oseq'] == ''):
gv.sd['seq'] = 1
else:
gv.sd['seq'] = 0
if qdict.has_key('orst') and (qdict['orst'] == 'on' or qdict['orst'] == ''):
gv.sd['rst'] = 1
else:
gv.sd['rst'] = 0
if qdict.has_key('olg') and (qdict['olg'] == 'on' or qdict['olg'] == ''):
gv.sd['lg'] = 1
else:
gv.sd['lg'] = 0
gv.lg = gv.sd['lg'] # necessary to make logging work correctly on Pi (see run_log())
gv.sd['lr'] = int(qdict['olr'])
gv.lr = gv.sd['lr']
srvals = [0]*(gv.sd['nst']) # Shift Register values
rovals = [0]*(gv.sd['nst']) # Run Once Durations
jsave(gv.sd, 'sd')
raise web.seeother('/')
#alert = '<script>alert("Options values saved.");window.location="/";</script>'
return #alert # -- Alerts are not considered good interface progrmming. Use sparingly!
def update_sd(self, qdict):
"""Transfer user input to vars."""
gv.sd['htp'] = int(qdict['htp'])
gv.sd['nbrd'] = int(qdict['o15'])+1
gv.sd['nst'] = gv.sd['nbrd']*8
gv.sd['sdt']= int(qdict['o17'])
gv.sd['mas'] = int(qdict['o18'])
gv.sd['mton']= int(qdict['o19'])
gv.sd['mtoff']= int(qdict['o20'])
gv.sd['tz'] = int(qdict['o1'])
if qdict.has_key('o16'): gv.sd['seq'] = int(qdict['o16'])
if qdict.has_key('o21'): gv.sd['urs'] = int(qdict['o21'])
gv.sd['wl'] = int(qdict['o23'])
if qdict.has_key('o25'): gv.sd['ipas'] = int(qdict['o25'])
gv.sd['loc'] = qdict['loc']
gv.srvals = [0]*(gv.sd['nst']) # Shift Register values
gv.rovals = [0]*(gv.sd['nst']) # Run Once Durations
jsave(gv.sd, 'sd')
return
def update_scount(self, qdict):
"""Increase or decrease the number of stations shown when expansion boards are added in options."""
if int(qdict['o15'])+1 > gv.sd['nbrd']: # Lengthen lists
incr = int(qdict['o15']) - (gv.sd['nbrd']-1)
if int(qdict['onbrd'])+1 > gv.sd['nbrd']: # Lengthen lists
incr = int(qdict['onbrd']) - (gv.sd['nbrd']-1)
for i in range(incr):
gv.sd['mo'].append(0)
for i in range(incr):
gv.sd['ir'].append(0)
snames = data('snames')
nlst = re.findall('[\'"].*?[\'"]', snames)
ln = len(nlst)
@ -649,35 +662,36 @@ class change_options:
nlst.append("'S"+('%d'%(i+ln)).zfill(2)+"'")
nstr = '['+','.join(nlst)
nstr = nstr.replace("', ", "',")+",'']"
save('snames', nstr)
elif int(qdict['o15'])+1 < gv.sd['nbrd']: # Shorten lists
decr = gv.sd['nbrd'] - (int(qdict['o15'])+1)
gv.sd['mo'] = gv.sd['mo'][:(int(qdict['o15'])+1)]
save('snames', nstr)
for i in range(incr*8):
gv.srvals.append(0)
gv.ps.append([0,0])
gv.rs.append([0,0,0,0])
for i in range(incr):
gv.sbits.append(0)
elif int(qdict['onbrd'])+1 < gv.sd['nbrd']: # Shorten lists
onbrd = qdict['onbrd']
decr = gv.sd['nbrd'] - (onbrd+1)
gv.sd['mo'] = gv.sd['mo'][:(onbrd+1)]
gv.sd['ir'] = gv.sd['ir'][:(onbrd+1)]
snames = data('snames')
nlst = re.findall('[\'"].*?[\'"]', snames)
nstr = '['+','.join(nlst[:8+(int(qdict['o15'])*8)])+','']'
nstr = '['+','.join(nlst[:8+(onbrd*8)])+','']'
save('snames', nstr)
gv.srvals = [0] * (int(qdict['o15'])+1) * 8
gv.ps = []
for i in range((int(qdict['o15'])+1) * 8):
gv.ps.append([0,0])
gv.rs = []
for i in range((int(qdict['o15'])+1) * 8):
gv.rs.append([0,0,0,0])
gv.sbits = [0] * (int(qdict['o15'])+2)
return
newlen = gv.sd['nst'] - decr * 8
gv.srvals = gv.srvals[:newlen]
gv.ps = gv.ps[:newlen]
gv.rs = gv.rs[:newlen]
gv.sbits = gv.sbits[:onbrd+1]
return
class view_stations:
"""Open a page to view and edit station names and master associations."""
def GET(self):
stationpg = '<!DOCTYPE html>\n'
stationpg += data('meta')+'\n'
stationpg += '<link href="./static/images/icons/favicon.ico" rel="icon" type="image/x-icon" />\n'
stationpg += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\n'
stationpg += data('meta')
stationpg += '<script>var baseurl=\"'+baseurl()+'\"</script>\n'
stationpg += '<script>var nboards='+str(gv.sd['nbrd'])+',maxlen=12,mas='+str(gv.sd['mas'])+',ipas='+str(gv.sd['ipas'])+';</script>\n'
#stationpg += '<script>var masop='+str(gv.sd['mo'])+';</script>\n'
stationpg += '<script>var masop='+str(gv.sd['mo'])+',rop='+str(gv.sd['ir'])+';</script>\n' ## added experimental "Ignore Rain"' feature
stationpg += '<script>var baseurl=\"'+baseurl()+'\"\n' + pass_options(["nbrd","snlen","mas","ipas","mo","ir"]) + '</script>\n'
stationpg += '<script>snames='+data('snames')+';</script>\n'
stationpg += '<script src=\"'+baseurl()+'/static/scripts/java/svc1.8.3/viewstations.js\"></script>'
return stationpg
@ -686,7 +700,6 @@ class change_stations:
"""Save changes to station names and master associations."""
def GET(self):
qdict = web.input()
print qdict
try:
if gv.sd['ipas'] != 1 and qdict['pw'] != base64.b64decode(gv.sd['pwd']):
raise web.unauthorized()
@ -755,17 +768,15 @@ class view_runonce:
"""Open a page to view and edit a run once program."""
def GET(self):
ropg = '<!DOCTYPE html>\n'
ropg += data('meta')+'\n'
ropg += '<link href="./static/images/icons/favicon.ico" rel="icon" type="image/x-icon" />\n'
ropg += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\n'
ropg += '<script >var baseurl=\"'+baseurl()+'\"</script>\n'
ropg += '<script >var nboards='+str(gv.sd['nbrd'])+',mas='+str(gv.sd['mas'])+',ipas='+str(gv.sd['ipas'])+',dur='+str(gv.rovals).replace(' ', '')+';</script>\n'
ropg += data('meta')
ropg += '<script >var baseurl=\"'+baseurl()+'\"\n' + pass_options(["nbrd","mas","ipas"]) + '</script>\n'
ropg += '<script >var dur='+str(gv.rovals).replace(' ', '')+';</script>\n'
ropg += '<script >snames='+data('snames')+';</script>\n'
ropg += '<script src=\"'+baseurl()+'/static/scripts/java/svc1.8.3/viewro.js\"></script>'
return ropg
class change_runonce:
"""Start a Run Once program. This wil. override any running program."""
"""Start a Run Once program. This will override any running program."""
def GET(self):
qdict = web.input()
try:
@ -777,12 +788,12 @@ class change_runonce:
if not gv.sd['en']: return # check operation status
gv.rovals = json.loads(qdict['t'])
gv.rovals.pop()
gv.ps = []
stations = [0] * gv.sd['nbrd']
gv.ps = [] # program schedule (for display)
gv.rs = [] # run schedule
for i in range(gv.sd['nst']):
gv.ps.append([0,0])
gv.rs = [] #run schedule
for i in range(gv.sd['nst']): # clear run schedule
gv.rs.append([0,0,0,0])
gv.rs.append([0,0,0,0])
for i, v in enumerate(gv.rovals):
if v: # if this element has a value
gv.rs[i][0] = gv.now
@ -790,18 +801,17 @@ class change_runonce:
gv.rs[i][3] = 98
gv.ps[i][0] = 98
gv.ps[i][1] = v
schedule_stations()
stations[i/8] += 2**(i%8)
schedule_stations(stations)
raise web.seeother('/')
class view_programs:
"""Open programs page."""
def GET(self):
programpg = '<!DOCTYPE html>\n'
programpg += data('meta')+'\n'
programpg += '<link href="./static/images/icons/favicon.ico" rel="icon" type="image/x-icon" />\n'
programpg += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\n'
programpg += data('meta')
programpg += '<script >var baseurl=\"'+baseurl()+'\"</script>\n'
programpg += '<script >'+output_prog()+'</script>\n'
programpg += '<script >'+ pass_options(["nbrd","ipas","mnp"]) + output_prog()+'</script>\n'
programpg += '<script >snames='+data('snames')+';</script>\n'
programpg += '<script src=\"'+baseurl()+'/static/scripts/java/svc1.8.3/viewprog.js\"></script>'
return programpg
@ -811,11 +821,8 @@ class modify_program:
def GET(self):
qdict = web.input()
modprogpg = '<!DOCTYPE html>\n'
modprogpg += data('meta')+'\n'
modprogpg += '<link href="./static/images/icons/favicon.ico" rel="icon" type="image/x-icon" />\n'
modprogpg += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\n'
modprogpg += '<script >var baseurl=\"'+baseurl()+'\"</script>\n'
modprogpg += '<script >var nboards='+str(gv.sd['nbrd'])+',ipas='+str(gv.sd['ipas'])+';\n'
modprogpg += data('meta')
modprogpg += '<script >var baseurl=\"'+baseurl()+'\"\n' + pass_options(["nbrd","ipas"]) + '\n'
if qdict['pid'] != '-1':
mp = gv.pd[int(qdict['pid'])][:]
if mp[1] >= 128 and mp[2] > 1: # If this is an interval program
@ -898,15 +905,11 @@ class graph_programs:
else: mm = str(lt.tm_mon)
if qdict.has_key('y'): yy = str(qdict['y'])
else: yy = str(lt.tm_year)
graphpg = '<script >var baseurl=\"'+baseurl()+'\"</script>\n'
graphpg += '<link href="./static/images/icons/favicon.ico" rel="icon" type="image/x-icon" />\n'
graphpg += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\n'
graphpg += ('<script >var mas='+str(gv.sd['mas'])+',wl='+str(gv.sd['wl'])+',sdt='+str(gv.sd['sdt'])+
',mton='+str(gv.sd['mton'])+',mtoff='+str(gv.sd['mtoff'])+',devday='+str(int(t/86400))+
',devmin='+str((lt.tm_hour*60)+lt.tm_min)+',dd='+dd+',mm='+mm+',yy='+yy+';var masop='+
str(gv.sd['mo'])+';'+output_prog()+'</script>\n')
graphpg += '<script>var seq='+str(gv.sd['seq'])+';</script>\n'
graphpg += '<script >var snames='+data('snames').replace(' ', '')+';</script>\n'
graphpg = '<!DOCTYPE html>\n'
graphpg += data('meta')
graphpg += '<script>var baseurl=\"'+baseurl()+'\";\n' + pass_options(["mas","seq","wl","sdt","mton","mtoff","nbrd","ipas","mnp","mo"]) + '</script>\n'
graphpg += '<script>var devday='+str(int(t/86400))+',devmin='+str((lt.tm_hour*60)+lt.tm_min)+',dd='+dd+',mm='+mm+',yy='+yy+';'+output_prog()+'</script>\n'
graphpg += '<script>var snames='+data('snames').replace(' ', '')+';</script>\n'
graphpg += '<script src=\"'+baseurl()+'/static/scripts/java/svc1.8.3/plotprog.js\"></script>'
return graphpg
@ -950,10 +953,10 @@ class log_options:
return
except KeyError:
pass
if qdict.has_key('log'): gv.sd['lg'] = "checked"
else: gv.sd['lg'] = ""
if qdict.has_key('log'): gv.sd['lg'] = 1
else: gv.sd['lg'] = 0
gv.lg = gv.sd['lg'] # necessary to make logging work correctly on Pi (see run_log())
gv.sd['lr'] = qdict['nrecords']
gv.sd['lr'] = int(qdict['nrecords'])
gv.lr = int(gv.sd['lr'])
jsave(gv.sd, 'sd')
raise web.seeother('/vl')
@ -973,7 +976,8 @@ class run_now:
if not p[0]: # if program is disabled
raise web.seeother('/vp')
stop_stations()
for b in range(gv.sd['nbrd']): # check each station
#for b in range(gv.sd['nbrd']): # check each station
for b in range(len(p[7:7+gv.sd['nbrd']])): # check each station
for s in range(8):
sid = b*8+s # station index
if sid+1 == gv.sd['mas']: continue # skip if this is master valve
@ -982,11 +986,21 @@ class run_now:
gv.rs[sid][3] = pid+1 # store program number in schedule
gv.ps[sid][0] = pid+1 # store program number for display
gv.ps[sid][1] = gv.rs[sid][2] # duration
schedule_stations()
schedule_stations(p[7:7+gv.sd['nbrd']])
raise web.seeother('/')
class show_revision:
"""Show revision info to the user. Use: [URL of Pi]/rev."""
def GET(self):
revpg = '<!DOCTYPE html>\n'
revpg += 'Python Interval Program for OpenSprinkler Pi<br/><br/>\n'
revpg += 'Compatable with OpenSprinkler firmware 1.8.3.<br/><br/>\n'
revpg += 'ospi.py revision: '+str(gv.rev) +'<br/><br/>\n'
revpg += 'updated ' + gv.rev_date +'\n'
return revpg
class toggle_temp:
"""Change units of Raspi's CPU temperature display on home page."""
"""Change units of Raspi\'s CPU temperature display on home page."""
def GET(self):
qdict = web.input()
if qdict['tunit'] == "C":
@ -1000,7 +1014,8 @@ class OSPi_app(web.application):
"""Allows HTTP port the program runs on to be selected by the program."""
def run(self, port=gv.sd['htp'], *middleware): # get port number from options settings
func = self.wsgifunc(*middleware)
return web.httpserver.runsimple(func, ('0.0.0.0', port))
return web.httpserver.runsimple(func, ('0.0.0.0', port))
if __name__ == '__main__':
app = OSPi_app(urls, globals())

View File

@ -1,4 +1,4 @@
Vars held in the settings dict (gv.sd)
??Vars held in the settings dict (gv.sd)
from controller values (cvalues):
en:1 enabled (operation)
@ -7,23 +7,25 @@ mm:0 manual mode (bool)
rbt:0 reboot (bool)
from options:
htp:8080 http port the program will run on (o12)
seq:1 sequential/concurrent operation (o16)
sdt:0 station delay time(o17)
mton:0 master on delay (o19)
mtoff:0 master off delay (020)
nbrd:1 number of boards (o15)
tz:16 time zone (o1)
urs:0 use rain sensor (o21 - not used)
rst:1 Rain sensor type (normaly open =1, dafault, or closed, o22 - not used)
wl:100 water level (percent, o23)
mas:0 master station index (o18)
ipas:1 ignore passwprd (bool, o25)
htp:8080 http port the program will run on
seq:1 sequential/concurrent operation
sdt:0 station delay time
mton:0 master on delay
mtoff:0 master off delay
nbrd:1 number of boards
tz:16 time zone
urs:0 use rain sensor
rst:1 Rain sensor type (normaly open =1, dafault, or closed - not used)
wl:100 water level (percent)
mas:0 master station index
ipas:1 ignore passwprd (bool)
pwd:"b3BlbmRvb3I=" encoded password (default shown here)
loc:"" location (for weather - not used)
rdst:0 rain delay stop time (unix time stamp)
rs:0 rain sensed (o22)
nopts:13 Number of optiions to be displayed
rs:0 rain sensed
nopts:14 Number of optiions to be displayed
name:"OpenSprinkler Pi" configurable name for system
snlen:32 max size of station names
for scheduling:
bsy:0 program buisy

View File

@ -10,7 +10,7 @@ function linkn(s){window.open(s, '_blank');}
// input rain delay value
function setrd(form,idx) {var h=prompt("Enter hours to delay","0");if(h!=null){form.elements[idx].value=h;form.submit()};}
function imgstr(s) {return "<img src=\""+baseurl+"/static/images/icons/svc_"+s+".png\" height=20 align=absmiddle>&nbsp;";}
function datestr(t) {var _t=tz-48; return (new Date(t)).toUTCString()+((_t>=0)?"+":"-")+(Math.abs(_t)/4>>0)+":"+((Math.abs(_t)%4)*15/10>>0)+((Math.abs(_t)%4)*15%10);}
function datestr(t) {var _t=sd['tz']-48; return (new Date(t)).toUTCString()+((_t>=0)?"+":"-")+(Math.abs(_t)/4>>0)+":"+((Math.abs(_t)%4)*15/10>>0)+((Math.abs(_t)%4)*15%10);}
// raspi CPU temp unit
function toggle(form) {form.elements[0].value=tempunit;form.submit();}
w("<form name=tt action=ttu method=get><input type=hidden name=tunit></form>");
@ -27,31 +27,33 @@ w("<button style=\"height:44\" onclick=link(\"/vp\")>"+imgstr("cal")+"Programs</
//w("<button style=\"height:44\" onclick=linkn(\"http://igoogle.wunderground.com/cgi-bin/findweather/getForecast?query="+loc+"\")>"+imgstr("weather")+"Weather</button><p></p>");
w("<button style=\"height:44\" onclick=link(\"/vl\")>"+imgstr("log")+"Log</button><p></p>");
// print device information
w("<b>System name</b>: "+sd['name']+"<br>");
if(ver>=100) w("<b>Firmware version</b>: "+(ver/100>>0)+"."+((ver/10>>0)%10)+"."+(ver%10)+"<br>");
else w("<b>Firmware version</b>: "+(ver/10>>0)+"."+(ver%10)+"<br>");
w("<b>Device time</b>: "+datestr(devt*1000)+"<br>");
if (typeof cputemp === 'undefined') cputemp="";
w("<b>CPU Temp</b>: <span id='heat' onmouseover='bluebg(this)' onmouseout='nobg(this)' style='cursor:pointer' onclick='toggle(tt)' title='Click to toggle Celsius <> Fahrenheit'>"+cputemp+"&deg;"+tempunit+"</span><hr>");
w("<script type=\"text/javascript\" src=\""+baseurl+"/static/scripts/java/svc1.8.3/"+((mm)?"manualmode.js":"progmode.js")+"\"></script>");
w("<script type=\"text/javascript\" src=\""+baseurl+"/static/scripts/java/svc1.8.3/"+((sd['mm'])?"manualmode.js":"progmode.js")+"\"></script>");
// print status and other information
w("<br><b>Operation</b>: "+(en?("on").fontcolor("green"):("OFF").fontcolor("red")));
w("<br><b>Raindelay</b>: "+(rd?("ON").fontcolor("red")+" (till "+datestr(rdst*1000)+")":("off").fontcolor("black")));
w("<br><b>Rainsense</b>: "+(urs?(rs?("Rain Detected").fontcolor("red"):("no rain").fontcolor("green")):"<font color=gray>n/a</font>"));
w("<br><b>Water level</b>: <font color="+((wl==100)?"green":"red")+">"+wl+"\%</font>");
w("<br><b>Operation</b>: "+(sd['en']?("on").fontcolor("green"):("OFF").fontcolor("red")));
w("<br><b>Raindelay</b>: "+(sd['rd']?("ON").fontcolor("red")+" (till "+datestr(rdst*1000)+")":("off").fontcolor("black")));
w("<br><b>Rainsense</b>: "+(sd['urs']?(sd['rs']?("Rain Detected").fontcolor("red"):("no rain").fontcolor("green")):"<font color=gray>n/a</font>"));
w("<br><b>Water level</b>: <font color="+((sd['wl']==100)?"green":"red")+">"+sd['wl']+"\%</font>");
var lrsid=lrun[0],lrpid=lrun[1],lrdur=lrun[2],lret=lrun[3];
var pname="P"+lrpid;
if(lrpid==255||lrpid==99) pname="Manual Mode";
if(lrpid==254||lrpid==98) pname="Run-once Program";
dstr=(new Date(lret*1000)).toUTCString().replace(" GMT","");
//dstr=(new Date(lret*1000)).toUTCString().replace(" GMT","");
dstr=(new Date(lret*1000)).toUTCString()+(((sd['tz']-48)>=0)?"+":"-")+(Math.abs(sd['tz']-48)/4>>0)+":"+((Math.abs(sd['tz']-48)%4)*15/10>>0)+((Math.abs(sd['tz']-48)%4)*15%10);
if(lrpid!=0) w("<br><b>Log</b>: "+(snames[lrsid]+" ran "+pname+" for "+(lrdur/60>>0)+"m"+(lrdur%60)+"s @ "+dstr).fontcolor("gray"));
else w("<br><b>Log</b>: <font color=gray>n/a</font>");
w("<hr>");
// print html form
w("<form name=hf action=cv method=get><p>Password:<input type=password "+(ipas?"disabled":"")+" size=10 id=pwd name=pw></p>");
w("<form name=hf action=cv method=get><p>Password:<input type=password "+(sd['ipas']?"disabled":"")+" size=10 id=pwd name=pw></p>");
w("<input type=hidden name=en><input type=hidden name=rd value=0><input type=hidden name=rbt value=0><input type=hidden name=mm value=0></form>");
w("<button style=\"height:36\" onclick=\"hf.elements[1].value="+(1-en)+";hf.submit();\">"+imgstr(en?"stop":"start")+(en?"Stop Operation":"Start Operation")+"</button>");
w("<button style=\"height:36\" onclick=\"hf.elements[4].value="+(1-mm)+";hf.submit();\">"+imgstr(mm?"auto":"manual")+(mm?"Manual Off":"Manual On")+"</button>");
w("<button style=\"height:36\" onclick=\"hf.elements[1].value="+(1-sd['en'])+";hf.submit();\">"+imgstr(sd['en']?"stop":"start")+(sd['en']?"Stop Operation":"Start Operation")+"</button>");
w("<button style=\"height:36\" onclick=\"hf.elements[4].value="+(1-sd['mm'])+";hf.submit();\">"+imgstr(sd['mm']?"auto":"manual")+(sd['mm']?"Manual Off":"Manual On")+"</button>");
w("<button style=\"height:36\" onclick=\"setrd(hf,2)\">"+imgstr("rain")+"Rain Delay</button>");
w("<button style=\"height:36\" onclick=\"hf.elements[3].value=1;hf.submit();\">"+imgstr("reboot")+"Reboot</button>");
w("<p></p><hr><br>");

View File

@ -18,14 +18,14 @@ function snf(sid,sbit) {
w("<b>Manual Control:</b> (timer is optional)<p></p>");
w("<table border=1>");
var bid,s,sid,sn,rem,remm,rems,sbit;
for(bid=0;bid<nbrd;bid++){
for(bid=0;bid<sd['nbrd'];bid++){
for(s=0;s<8;s++){
w("<tr><td bgcolor='#E4E4E4'>");
sid=bid*8+s;
sn=sid+1;
//w("Station "+(sn/10>>0)+(sn%10)+": ");
w(snames[sid]+":&nbsp;&nbsp;</td><td>");
if(sn==mas) {w(((sbits[bid]>>s)&1?("<b>On</b>").fontcolor("green"):("Off").fontcolor("black"))+" (<b>Master</b>)");}
if(sn==sd['mas']) {w(((sbits[bid]>>s)&1?("<b>On</b>").fontcolor("green"):("Off").fontcolor("black"))+" (<b>Master</b>)");}
else {
rem=ps[sid][1];
if(rem>65536) rem=0;

View File

@ -45,7 +45,7 @@ function fsubmit(f) {
if(days[0]==0||(days[1]<2&&(days[0]&0x7f)==0)) {alert("Error: You have not selected any day.");return;}
// process stations
var stations=[0],station_selected=0,bid;
for(bid=0;bid<nboards;bid++) {
for(bid=0;bid<sd['nbrd'];bid++) {
stations[bid]=0;
for(s=0;s<8;s++) {
sid=bid*8+s;
@ -67,12 +67,12 @@ function fsubmit(f) {
if(!(dm>=0&&ds>=0&&ds<60&&duration>0)) {alert("Error: Incorrect duration.");return;}
// password
var p="";
if(!ipas) p=prompt("Please enter your password:","");
if(!sd['ipas']) p=prompt("Please enter your password:","");
if(p!=null){
f.elements[0].value=p;
f.elements[1].value=pid;
f.elements[2].value="["+en+","+days[0]+","+days[1]+","+start_time+","+end_time+","+interval+","+duration;
for(i=0;i<nboards;i++) {f.elements[2].value+=","+stations[i];}
for(i=0;i<sd['nbrd'];i++) {f.elements[2].value+=","+stations[i];}
f.elements[2].value+="]";
f.submit();
}
@ -91,7 +91,7 @@ w("<input type=radio name=rad_day id=days_n><b><u>Interval</u>:</b> Every <input
w("<p><b>Select Stations:</b></p>");
w("<table border=1 cellpadding=3>");
var bid,s,sid;
for(bid=0;bid<nboards;bid++) {
for(bid=0;bid<sd['nbrd'];bid++) {
for(s=0;s<8;s++) {
sid=bid*8+s;
if(sid%4==0) w("<tr>");
@ -135,7 +135,7 @@ if(pid>-1) {
id("tds").value=""+((t%60)/10>>0)+((t%60)%10);
// process stations
var bits;
for(bid=0;bid<nboards;bid++) {
for(bid=0;bid<sd['nbrd'];bid++) {
bits=prog[bid+7];
for(s=0;s<8;s++) {sid=bid*8+s;id("s"+sid).checked=(bits&(1<<s)?true:false);}
}

View File

@ -8,7 +8,7 @@
var prog_color=["rgba(0,0,200,0.5)","rgba(0,200,0,0.5)","rgba(200,0,0,0.5)","rgba(0,200,200,0.5)"];
var days_str=["Sun","Mon","Tue","Wed","Thur","Fri","Sat"];
var xstart=80,ystart=80,stwidth=40,stheight=180;
var winwidth=stwidth*nboards*8+xstart, winheight=26*stheight+ystart;
var winwidth=stwidth*sd['nbrd']*8+xstart, winheight=26*stheight+ystart;
var sid,sn,t;
var simt=Date.UTC(yy,mm-1,dd,0,0,0,0);
var simdate=new Date(simt);
@ -50,39 +50,39 @@ function getrunstr(start,end){ // run time string
return str;
}
function plot_bar(sid,start,pid,end) { // plot program bar
w("<div title=\""+snames[sid]+" ["+getrunstr(start,end)+"]\" align=\"center\" style=\"position:absolute;background-color:"+prog_color[(pid+3)%4]+";left:"+getx(sid)+";top:"+gety(start/60)+";border:0;width:"+stwidth+";height:"+((end-start)/60*stheight/60)+"\">P"+pid+"</div>");
w("<div title=\""+snames[sid]+" ["+getrunstr(start,end)+"]\" align=\"center\" style=\"position:absolute;background-color:"+prog_color[(pid+3)%4]+";left:"+getx(sid)+"px;top:"+gety(start/60)+"px;border:0;width:"+stwidth+"px;height:"+((end-start)/60*stheight/60)+"px\">P"+pid+"</div>");
}
function plot_master(start,end) { // plot master station
w("<div title=\"Master ["+getrunstr(start,end)+"]\" style=\"position:absolute;background-color:#CCCC80;left:"+getx(mas-1)+";top:"+gety(start/60)+";border:0;width:"+stwidth+";height:"+((end-start)/60*stheight/60)+"\"></div>");
w("<div title=\"Master ["+getrunstr(start,end)+"]\" style=\"position:absolute;background-color:#CCCC80;left:"+getx(mas-1)+"px;top:"+gety(start/60)+"px;border:0;width:"+stwidth+"px;height:"+((end-start)/60*stheight/60)+"px\"></div>");
//if(mas==0||start==end) return;
//ctx.fillStyle="rgba(64,64,64,0.5)";
//ctx.fillRect(getx(mas-1),gety(start/60),stwidth,(end-start)/60*stheight/60);
}
function plot_currtime() {
w("<div style=\"position:absolute;left:"+(xstart-stwidth/2-10)+";top:"+gety(devmin)+";border:1px solid rgba(200,0,0,0.5);width:"+(winwidth-xstart+stwidth/2)+";height:0;\"></div>");
w("<div style=\"position:absolute;left:"+(xstart-stwidth/2-10)+"px;top:"+gety(devmin)+"px;border:1px solid rgba(200,0,0,0.5);width:"+(winwidth-xstart+stwidth/2)+"px;height:0px;\"></div>");
}
function run_sched(simseconds,st_array,pid_array,et_array) { // run and plot schedule stored in array data
var sid,endtime=simseconds;
for(sid=0;sid<nboards*8;sid++) {
for(sid=0;sid<sd['nbrd']*8;sid++) {
if(pid_array[sid]) {
if(seq==1) { // sequential
if(sd['seq']==1) { // sequential
plot_bar(sid,st_array[sid],pid_array[sid],et_array[sid]);
if((mas>0)&&(mas!=sid+1)&&(masop[sid>>3]&(1<<(sid%8))))
plot_master(st_array[sid]+mton, et_array[sid]+mtoff);
if((sd['mas']>0)&&(sd['mas']!=sid+1)&&(sd['mo'][sid>>3]&(1<<(sid%8))))
plot_master(st_array[sid]+sd['mton'], et_array[sid]+sd['mtoff']);
endtime=et_array[sid];
} else { // concurrent
plot_bar(sid,simseconds,pid_array[sid],et_array[sid]);
// check if this station activates master
if((mas>0)&&(mas!=sid+1)&&(masop[sid>>3]&(1<<(sid%8))))
if((sd['mas']>0)&&(sd['mas']!=sid+1)&&(sd['mo'][sid>>3]&(1<<(sid%8))))
endtime=(endtime>et_array[sid])?endtime:et_array[sid];
}
}
}
if(seq==0&&mas>0) plot_master(simseconds,endtime);
if(sd['seq']==0&&sd['mas']>0) plot_master(simseconds,endtime);
return endtime;
}
function draw_title() {
w("<div align=\"center\" style=\"background-color:#EEEEEE;position:absolute;left:0px;top:10px;border:2px solid gray;padding:5px 0px;width:"+(winwidth)+";border-radius:10px;box-shadow:3px 3px 2px #888888;\"><b>Program Preview of</b>&nbsp;");
w("<div align=\"center\" style=\"background-color:#EEEEEE;position:absolute;left:0px;top:10px;border:2px solid gray;padding:5px 0px;width:"+(winwidth)+"px;border-radius:10px;box-shadow:3px 3px 2px #888888;\"><b>Program Preview of</b>&nbsp;");
w(days_str[simdate.getUTCDay()]+" "+(simdate.getUTCMonth()+1)+"/"+(simdate.getUTCDate())+" "+(simdate.getUTCFullYear()));
w("<br><font size=2>(Hover over each colored bar to see tooltip)</font>");
w("</div>");
@ -90,25 +90,25 @@ function draw_title() {
function draw_grid() {
// draw table and grid
for(sid=0;sid<=nboards*8;sid++) {
for(sid=0;sid<=sd['nbrd']*8;sid++) {
sn=sid+1;
if(sid<nboards*8) w("<div style=\"position:absolute;left:"+(xstart+sid*stwidth-10)+";top:"+(ystart-15)+";width:"+stwidth+";height:20;border:0;padding:0;\"><font size=2>S"+(sn/10>>0)+(sn%10)+"</font></div>");
w("<div style=\"position:absolute;left:"+getx(sid)+";top:"+(ystart-10)+";border:1px solid gray;width:0;height:"+(winheight-ystart+30)+";\"></div>");
if(sid<sd['nbrd']*8) w("<div style=\"position:absolute;left:"+(xstart+sid*stwidth-10)+"px;top:"+(ystart-15)+"px;width:"+stwidth+"px;height:20px;border:0;padding:0;\"><font size=2>S"+(sn/10>>0)+(sn%10)+"</font></div>");
w("<div style=\"position:absolute;left:"+getx(sid)+"px;top:"+(ystart-10)+"px;border:1px solid gray;width:0px;height:"+(winheight-ystart+30)+"px;\"></div>");
}
// horizontal grid, time
for(t=0;t<=24;t++) {
w("<div style=\"position:absolute;left:"+(xstart-stwidth/2-15)+";top:"+gety(t*60)+";border:1px solid gray;width:15;height:0;\"></div>");
w("<div style=\"position:absolute;left:"+(xstart-stwidth/2-8)+";top:"+(gety(t*60)+stheight/2)+";border:1px solid gray;width:8;height:0;\"></div>");
w("<div style=\"position:absolute;left:"+(xstart-70)+";top:"+(ystart+t*stheight-7)+";width=70;height:20;border:0;padding:0;\"><font size=2>"+(t/10>>0)+(t%10)+":00</font></div>");
w("<div style=\"position:absolute;left:"+(xstart-stwidth/2-15)+"px;top:"+gety(t*60)+"px;border:1px solid gray;width:15px;height:0px;\"></div>");
w("<div style=\"position:absolute;left:"+(xstart-stwidth/2-8)+"px;top:"+(gety(t*60)+stheight/2)+"px;border:1px solid gray;width:8px;height:0px;\"></div>");
w("<div style=\"position:absolute;left:"+(xstart-70)+"px;top:"+(ystart+t*stheight-7)+"px;width=70;height:20px;border:0;padding:0;\"><font size=2>"+(t/10>>0)+(t%10)+":00</font></div>");
}
plot_currtime();
}
function draw_program() {
// plot program data by a full simulation
var simminutes=0,busy=0,match_found=0,bid,s,sid,pid,match=[0,0];
var st_array=new Array(nboards*8),pid_array=new Array(nboards*8);
var et_array=new Array(nboards*8);
for(sid=0;sid<nboards*8;sid++) {
var st_array=new Array(sd['nbrd']*8),pid_array=new Array(sd['nbrd']*8);
var et_array=new Array(sd['nbrd']*8);
for(sid=0;sid<sd['nbrd']*8;sid++) {
st_array[sid]=0;pid_array[sid]=0;et_array[sid]=0;
}
do { // check through every program
@ -117,11 +117,11 @@ function draw_program() {
for(pid=0;pid<nprogs;pid++) {
var prog=pd[pid];
if(check_match(prog,simminutes,simdate,simday)) {
for(sid=0;sid<nboards*8;sid++) {
for(sid=0;sid<sd['nbrd']*8;sid++) {
bid=sid>>3;s=sid%8;
if(mas==(sid+1)) continue; // skip master station
if(sd['mas']==(sid+1)) continue; // skip master station
if(prog[7+bid]&(1<<s)) {
et_array[sid]=prog[6]*wl/100>>0;pid_array[sid]=pid+1;
et_array[sid]=prog[6]*sd['wl']/100>>0;pid_array[sid]=pid+1;
match_found=1;
}//if
}//for_sid
@ -129,16 +129,16 @@ function draw_program() {
}//for_pid
if(match_found) {
var acctime=simminutes*60;
if(seq) { // sequential
for(sid=0;sid<nboards*8;sid++) {
if(sd['seq']) { // sequential
for(sid=0;sid<sd['nbrd']*8;sid++) {
if(et_array[sid]) {
st_array[sid]=acctime;acctime+=et_array[sid];
et_array[sid]=acctime;acctime+=sdt;
et_array[sid]=acctime;acctime+=sd['sdt'];
busy=1;
}//if
}//for
} else {
for(sid=0;sid<nboards*8;sid++) {
for(sid=0;sid<sd['nbrd']*8;sid++) {
if(et_array[sid]) {
st_array[sid]=simminutes*60;
et_array[sid]=simminutes*60+et_array[sid];
@ -149,9 +149,9 @@ function draw_program() {
}//if(match_found)
if (busy) {
var endminutes=run_sched(simminutes*60,st_array,pid_array,et_array)/60>>0;
if(seq&&simminutes!=endminutes) simminutes=endminutes;
if(sd['seq']&&simminutes!=endminutes) simminutes=endminutes;
else simminutes++;
for(sid=0;sid<nboards*8;sid++) {st_array[sid]=0;pid_array[sid]=0;et_array[sid]=0;} // clear program data
for(sid=0;sid<sd['nbrd']*8;sid++) {st_array[sid]=0;pid_array[sid]=0;et_array[sid]=0;} // clear program data
} else {
simminutes++; // increment simulation time
}

View File

@ -7,7 +7,7 @@
// print station status
function rsn() {
var p="";
if(!ipas) p=prompt("Please enter your password:","");
if(!sd['ipas']) p=prompt("Please enter your password:","");
if(p!=null) window.location="/cv?pw="+p+"&rsn=1";
}
@ -18,18 +18,18 @@ w("<p><b>Station Status</b>:</p>");
w("<table border=1>");
var bid,s,sid,sn,rem,remm,rems,off,pname;
//off=((en==0||rd!=0||(urs!=0&&rs!=0))?1:0); // move rain stuff to after sid = ...
off=((en==0)?1:0);
for(bid=0;bid<nbrd;bid++){
off=((sd['en']==0)?1:0);
for(bid=0;bid<sd['nbrd'];bid++){
for(s=0;s<8;s++){
w("<tr><td bgcolor=\"#E4E4E4\">");
sid=bid*8+s;
exempt=((ir[bid]&1<<s)?1:0);
if(en==1) {off=(((rd!=0||(urs!=0&&rs!=0))&&exempt!=1)?1:0);}
exempt=((sd['ir'][bid]&1<<s)?1:0);
if(sd['en']==1) {off=(((sd['rd']!=0||(sd['urs']!=0&&sd['rs']!=0))&&exempt!=1)?1:0);}
sn=sid+1;
w(snames[sid]+':&nbsp;&nbsp;');
w("</td><td>");
if(off) w("<strike>");
if(sn==mas) {w(((sbits[bid]>>s)&1?("<b>On</b>").fontcolor("green"):("Off").fontcolor("black"))+" (<b>Master</b>)");}
if(sn==sd['mas']) {w(((sbits[bid]>>s)&1?("<b>On</b>").fontcolor("green"):("Off").fontcolor("black"))+" (<b>Master</b>)");}
else {
rem=ps[sid][1];remm=rem/60>>0;rems=rem%60;
pname="P"+ps[sid][0];

83
static/scripts/java/svc1.8.3/viewoptions.js Normal file → Executable file
View File

@ -4,70 +4,79 @@
// Creative Commons Attribution ShareAlike 3.0 License
// Sep 2012, Rayshobby.net
var str_tooltips=["Example: GMT-4:00, GMT+5:30 (effective after reboot).", "HTTP port (effective after reboot).", "HTTP port (effective after reboot).", "Number of extension boards", "Sequential running or concurrent running", "Station delay time (in seconds), between 0 and 240.", "Select master station", "Master on delay (in seconds), between +0 and +60.", "Master off delay (in seconds), between -60 and +60.", "Use rain sensor", "Rain sensor type", "Water level, between 0% and 250%.", "Ignore web password"];
function w(s) {document.writeln(s);}
function imgstr(s) {return "<img src=\""+baseurl+"/static/images/icons/svc_"+s+".png\" height=20 align=absmiddle>&nbsp;";}
function submit_form(f) {
// process time zone value
var th=parseInt(f.elements["th"].value,10);
var tq=parseInt(f.elements["tq"].value,10);
tq=(tq/15>>0)/4.0;th=th+(th>=0?tq:-tq);
tq=(tq/15>>0)/4.0;
th=th+(th>=0?tq:-tq);
// huge hack, needs to find a more elegant way
f.elements["o1"].value=((th+12)*4)>>0;
f.elements["o12"].value=(f.elements["htp"].value)&0xff;
f.elements["o13"].value=(f.elements["htp"].value>>8)&0xff;
f.elements["o18"].value=f.elements["mas"].value;
f.elements["otz"].value=((th+12)*4)>>0;
f.elements["ohtp"].value=(f.elements["htp"].value)&0xff;
f.elements["ohtp2"].value=(f.elements["htp"].value>>8)&0xff;
f.elements["omas"].value=f.elements["mas"].value;
f.submit();
}
function fcancel() {window.location="/";}
function fshow() {
function ftoggle() {
var oid,tip;
for(oid=0;oid<nopts;oid++){
var state=document.getElementById("tip0").style.display=="none";
for(oid=0;oid<opts.length;oid++){
tip=document.getElementById("tip"+oid);
if(tip!=null) tip.hidden=false;
if(tip!=null) tip.style.display=state?"inline":"none";
}
document.getElementById("tooltips").innerHTML = (state?"Hide Tooltips":"Show Tooltips");
}
w("<div align=\"center\" style=\"background-color:#EEEEEE;border:2px solid gray;padding:5px 10px;width:240px;border-radius:10px;box-shadow:3px 3px 2px #888888;\">");
w("<b>Set Options</b>:<br><font size=2>(Hover on each option to see tooltip)</font></div>");
w("<p></p>");
w("<button style=\"height:24\" onclick=\"fshow();return false;\">Show Tooltips</button>");
w("<button id=\"tooltips\" style=\"height:24\" onclick=\"ftoggle();return false;\">Show Tooltips</button>");
// print html form
w("<form name=of action=co method=get>");
var oid,name,isbool,value,index,pasoid=0;
for(oid=0;oid<nopts;oid++){
name=opts[oid*4+0];
isbool=opts[oid*4+1];
value=opts[oid*4+2];
index=opts[oid*4+3];
if(name=="Ignore password:") pasoid=oid;
if(isbool) w("<p title=\""+str_tooltips[oid]+"\"><b>"+name+"</b> <input type=checkbox "+(value>0?"checked":"")+" name=o"+index+">");
else {
// hack
if (name=="Time zone:") {
w("<input type=hidden value=0 name=o"+index+">");
var oid,label,isbool,value,name,ipasvalue=0;
for(oid=0;oid<opts.length;oid++){
label=opts[oid][0];
datatype=opts[oid][1];
value=sd[opts[oid][2]];
name=opts[oid][2];
tooltip=opts[oid][3];
if(name=="ipas") ipasvalue=value;
if(datatype == "boolean") {
w("<p title=\""+tooltip+"\"><b>"+label+":</b> <input type=checkbox "+(value>0?"checked":"")+" name=o"+name+">");
} else if (datatype == "string") {
w("<p title=\""+tooltip+"\"><b>"+label+":</b> <input type=text size=31 maxlength=31 value='"+value+"' name=o"+name+">");
} else {
switch (name) {
case "tz":
w("<input type=hidden value=0 name=o"+name+">");
tz=value-48;
w("<p title=\""+str_tooltips[oid]+"\"><b>"+name+"</b> GMT<input type=text size=3 maxlength=3 value="+(tz>=0?"+":"-")+(Math.abs(tz)/4>>0)+" name=th>");
w("<p title=\""+tooltip+"\"><b>"+label+":</b> GMT<input type=text size=3 maxlength=3 value="+(tz>=0?"+":"-")+(Math.abs(tz)/4>>0)+" name=th>");
w(":<input type=text size=3 maxlength=3 value="+((Math.abs(tz)%4)*15/10>>0)+((Math.abs(tz)%4)*15%10)+" name=tq>");
} else if (name=="Master station:") {
w("<input type=hidden value=0 name=o"+index+">");
w("<p title=\""+str_tooltips[oid]+"\"><b>"+name+"</b> <select name=mas><option "+(value==0?" selected ":" ")+"value=0>None</option>");
break;
case "mas":
w("<input type=hidden value=0 name=o"+name+">");
w("<p title=\""+tooltip+"\"><b>"+label+":</b> <select name=mas><option "+(value==0?" selected ":" ")+"value=0>None</option>");
for(i=1;i<=8;i++) w("<option "+(value==i?" selected ":" ")+"value="+i+">Station 0"+i+"</option>");
w("</select>");
} else if (name=="HTTP port:") {
w("<input type=hidden value=0 name=o"+index+"><input type=hidden value=0 name=o"+(index+1)+">");
var port=value+(opts[(oid+1)*4+2]<<8);
w("<p title=\""+str_tooltips[oid]+"\"><b>"+name+"</b> <input type=text size=5 maxlength=5 value="+port+" name=htp>");
oid++;
}
else {
w("<p title=\""+str_tooltips[oid]+"\"><b>"+name+"</b> <input type=text size=3 maxlength=3 value="+value+" name=o"+index+">");
break;
case "htp":
w("<input type=hidden value=0 name=o"+name+"><input type=hidden value=0 name=o"+name+"2>");
var port=value+(opts[(oid+1)][2]<<8);
w("<p title=\""+tooltip+"\"><b>"+label+":</b> <input type=text size=5 maxlength=5 value="+port+" name=ohtp>");
break;
case "nbrd":
w("<p title=\""+tooltip+"\"><b>"+label+":</b> <input type=text size=3 maxlength=3 value="+(value-1)+" name=o"+name+">");
break;
default:
w("<p title=\""+tooltip+"\"><b>"+label+":</b> <input type=text size=3 maxlength=3 value="+value+" name=o"+name+">");
}
}
//w("</p>");
w(" <span style=\"background-color:#FFF2B8;\" id=tip"+oid+" hidden=\"hidden\"><font size=2>"+str_tooltips[oid]+"</font></span></p>");
w(" <span style=\"background-color:#FFF2B8;display:none\" id=tip"+oid+"><font size=2>"+tooltip+"</font></span></p>");
}
w("<p title=\"City name or zip code. Use comma or + in place of space.\"><b>Location:</b> <input type=text maxlength=31 value=\""+loc+"\" name=loc></p>");
w("<h4>Password:<input type=password size=10 "+(opts[pasoid*4+2]?"disabled":"")+" name=pw></h4>");
w("<h4>Password:<input type=password size=10 "+(ipasvalue?"disabled":"")+" name=pw></h4>");
w("<button style=\"height:36\" onclick=\"submit_form(of)\">"+imgstr("submit")+"<b>Submit Changes</b></button>");
w("<button style=\"height:36\" onclick=\"fcancel();return false;\">"+imgstr("delall")+"Cancel</button>");
w("<h4>Change password</b>:<input type=password size=10 name=npw>&nbsp;&nbsp;Confirm:&nbsp;<input type=password size=10 name=cpw></h4>");

6
static/scripts/java/svc1.8.3/viewprog.js Normal file → Executable file
View File

@ -9,7 +9,7 @@ function w(s) {document.writeln(s);}
function imgstr(s) {return "<img src=\""+baseurl+"/static/images/icons/svc_"+s+".png\" height=20 align=absmiddle>&nbsp;";}
function del(form,idx) {
var p="";
if(!ipas) p=prompt("Please enter your password:","");
if(!sd['ipas']) p=prompt("Please enter your password:","");
if(p!=null){form.elements[0].value=p;form.elements[1].value=idx;form.submit();}
}
function mod(form,idx) {form.elements[0].value=idx;form.submit();}
@ -38,7 +38,7 @@ function pdays(days){
function pstations(data){
w("<table border=1 cellpadding=3px>");
var bid,s,bits,sid;
for(bid=0;bid<nboards;bid++){
for(bid=0;bid<sd['nbrd'];bid++){
bits=data[bid+7];
for(s=0;s<8;s++){
sid=bid*8+s;
@ -61,7 +61,7 @@ w("<button style=\"height:44\" onclick=\"fcancel()\">"+imgstr("back")+"Back</but
w("<button style=\"height:44\" onclick=\"mod(mf,-1)\">"+imgstr("addall")+"<b>Add a New Program</b></button>");
w("<button style=\"height:44\" onclick=\"del(df,-1)\">"+imgstr("delall")+"Delete All</button>");
w("<button style=\"height:44\" onclick=\"fplot()\">"+imgstr("preview")+"Preview</button><hr>");
w("<b>Total number of programs: "+nprogs+" (maximum is "+mnp+")</b><br>");
w("<b>Total number of programs: "+nprogs+" (maximum is "+sd['mnp']+")</b><br>");
// print programs
var pid,st,et,iv,du,sd;
for(pid=0;pid<nprogs;pid++) {

View File

@ -8,17 +8,17 @@ function w(s) {document.writeln(s);}
function imgstr(s) {return "<img src=\""+baseurl+"/static/images/icons/svc_"+s+".png\" height=20 align=absmiddle>&nbsp;";}
function rst(f) {
var sid,sn;
for(sid=0;sid<nboards*8;sid++) {
if(sid+1==mas) continue;
for(sid=0;sid<sd['nbrd']*8;sid++) {
if(sid+1==sd['mas']) continue;
f.elements["mm"+sid].value=0;
f.elements["ss"+sid].value=0;
}
}
function fsubmit(f) {
var comm="/cr?pw="+(ipas?"":f.elements["pw"].value)+"&t=[";
var comm="/cr?pw="+(sd['ipas']?"":f.elements["pw"].value)+"&t=[";
var sid,strmm,strss,mm,ss,matchfound=0;
for(sid=0;sid<nboards*8;sid++) {
if(sid+1==mas) {comm+="0,";continue;}
for(sid=0;sid<sd['nbrd']*8;sid++) {
if(sid+1==sd['mas']) {comm+="0,";continue;}
strmm=f.elements["mm"+sid].value;
strss=f.elements["ss"+sid].value;
mm=(strmm=="")?0:parseInt(strmm);
@ -37,16 +37,16 @@ w("<font size=3><b>Run-Once Program:</b></font></div><p></p>");
var sid;
w("<table border=1>");
w("<form name=rf action=cr method=get>");
for(sid=0;sid<nboards*8;sid++) {
for(sid=0;sid<sd['nbrd']*8;sid++) {
w("<tr><td bgcolor=\"#E4E4E4\">");
w(snames[sid]+":&nbsp;&nbsp;</td><td>");
if (sid+1==mas) {w("(<b>Master</b>)<br>");continue;}
if (sid+1==sd['mas']) {w("(<b>Master</b>)<br>");continue;}
w("<input type=text size=3 maxlength=3 value=0 name=mm"+sid+">:");
w("<input type=text size=2 maxlength=2 value=0 name=ss"+sid+"> (mm:ss)<br>");
w("</td>");
}
w("</table>");
w("<hr><font size=3><b>Password:</b><input type=password size=10 "+(ipas?"disabled":"")+" name=pw></font><p></p>");
w("<hr><font size=3><b>Password:</b><input type=password size=10 "+(sd['ipas']?"disabled":"")+" name=pw></font><p></p>");
w("</form></span>");
w("<button style=\"height:36\" onclick=\"fsubmit(rf)\">"+imgstr("submit")+"<b>Run Now</b></button>");
w("<button style=\"height:36\" onclick=\"rst(rf)\">"+imgstr("reset")+"Reset Time</button>");

46
static/scripts/java/svc1.8.3/viewstations.js Normal file → Executable file
View File

@ -8,20 +8,20 @@ function w(s) {document.writeln(s);}
function imgstr(s) {return "<img src=\""+baseurl+"/static/images/icons/svc_"+s+".png\" height=20 align=absmiddle>&nbsp;";}
function rst() {
var sid,sn;
for(sid=0;sid<nboards*8;sid++) {
for(sid=0;sid<sd['nbrd']*8;sid++) {
sn=sid+1;
document.getElementById("n"+sid).value="S"+(sn/10>>0)+(sn%10);
}
}
function fsubmit(f) {
if(mas>0) {
if(sd['mas']>0) {
var s, bid, sid, v;
for(bid=0;bid<nboards;bid++) {
for(bid=0;bid<sd['nbrd'];bid++) {
v=0;
for(s=0;s<8;s++){
sid=bid*8+(7-s);
v=v<<1;
if(sid+1==mas) {v=v+1;continue;}
if(sid+1==sd['mas']) {v=v+1;continue;}
if(document.getElementById("mc"+sid).checked) {
v=v+1;
}
@ -30,12 +30,12 @@ function fsubmit(f) {
}
}
var vi;
for(bid=0;bid<nboards;bid++) {
for(bid=0;bid<sd['nbrd'];bid++) {
vi=0;
for(s=0;s<8;s++){
sid=bid*8+(7-s);
vi=vi<<1;
if(sid+1==mas) {vi=vi+1;continue;}
if(sid+1==sd['mas']) {vi=vi+1;continue;}
if(document.getElementById("rc"+sid).checked) {
vi=vi+1;
}
@ -47,27 +47,33 @@ function fsubmit(f) {
function fcancel() {window.location="/";}
w("<div align=\"center\" style=\"background-color:#EEEEEE;border:2px solid gray;padding:5px 10px;width:240px;border-radius:10px;box-shadow:3px 3px 2px #888888;\">");
w("<font size=3><b>Set Stations:</b></font><br>");
w("<font size=2>(Maximum name length is "+maxlen+" letters).</font></div><p></p>");
w("<font size=2>(Maximum name length is "+sd['snlen']+" letters).</font></div><p></p>");
var sid,sn,bid,s;
w("<span style=\"line-height:32px\"><form name=sf action=cs method=get>");
for(sid=0;sid<nboards*8;sid++) {
w("<form name=sf action=cs method=get>");
w("<table><tr><th>Station</th><th>Name</th><th>Ignore Rain?</th>" + (sd['mas']>0?"<th>Activate Master?</th>":"") + "</tr>");
for(sid=0;sid<sd['nbrd']*8;sid++) {
sn=sid+1;
bid=sid>>3;
s=sid%8;
w("Station "+(sn/10>>0)+(sn%10)+":");
w("<input type=text size="+maxlen+" maxlength="+maxlen+" value=\""+snames[sid]+"\" name=s"+sid+" id=n"+sid+">&nbsp;");
if (sid+1!=mas) w("<input type=checkbox "+(rop[bid]&(1<<s)?"checked":"")+" id=rc"+sid+">Ignore rain?");
if (sid+1==mas) w("(<b>Master</b>)");
else if (mas>0) w("<input type=checkbox "+(masop[bid]&(1<<s)?"checked":"")+" id=mc"+sid+">Activate master?");
w("<br>");
w("<tr><td>"+(sn/10>>0)+(sn%10)+"</td>");
if (sid+1==sd['mas']) {
w("<td colspan=2>--Master--</td>");
} else {
w("<td><input type=text size="+sd['snlen']+" maxlength="+sd['snlen']+" value=\""+snames[sid]+"\" name=s"+sid+" id=n"+sid+"></td>");
if (sid+1!=sd['mas']) {
w("<td><input type=checkbox "+(sd['ir'][bid]&(1<<s)?"checked":"")+" id=rc"+sid+"></td>");
}
if (sd['mas']>0) w("<td><input type=checkbox "+(sd['mo'][bid]&(1<<s)?"checked":"")+" id=mc"+sid+"></td>");
}
w("</tr>");
}
w("<hr><font size=3><b>Password:</b><input type=password size=10 "+(ipas?"disabled":"")+" name=pw></font><p></p>");
for(bid=0;bid<nboards;bid++) {
w("<input type=hidden name=m"+bid+">");
w("</table>");
//w("<p>Note: preface a station name with \"~\" to indicate that it is disconnected and hide it in the interface.</p>");
w("<hr><font size=3><b>Password:</b><input type=password size=10 "+(sd['ipas']?"disabled":"")+" name=pw></font><p></p>");
for(bid=0;bid<sd['nbrd'];bid++) {
w("<input type=hidden name=i"+bid+">");
}
w("</form></span>");
w("</form>");
w("<button style=\"height:36\" onclick=\"fsubmit(sf)\">"+imgstr("submit")+"<b>Submit Changes</b></button>");
w("<button style=\"height:36\" onclick=\"rst()\">"+imgstr("reset")+"Reset Names</button>");
w("<button style=\"height:36\" onclick=\"fcancel()\">"+imgstr("delall")+"Cancel</button>");

View File

@ -96,10 +96,12 @@ function overlay() {
</button>
$code:
if sd['lg'] == 'checked':
if sd['lg'] == 1:
log_state = "Enabled"
log_option = "checked"
else:
log_state = "Disabled"
log_option = ""
<br><br>
Logging $log_state
@ -119,7 +121,7 @@ $for r in records:
<div>
<form name="logopts" action="/lo" method="get">
<p><h2>Log Options</h2></p>
<label for="log">Enable Logging</label> <input type="checkbox" id="log" name="log" $sd['lg'] ><br>
<label for="log">Enable Logging</label> <input type="checkbox" id="log" name="log" $log_option ><br>
<label for="max">Maximum records to keep:</label> <input type="text" size="4" value="$sd['lr']" id="max" name="nrecords">(0 = no limit)<br>
<span id='pwarea' style='display:block'; class="pwspan">
<label for="pw">Password Required:</label> <input type="password" size="10"id="pw">