I, Samer Albahra, am a medical school graduate, currently doing a pathology residency at UTHSCSA. I enjoy making mobile applications in my spare time and was excited when I first discovered the OpenSprinkler, an open-source Internet based sprinkler system, which lacked a truly mobile interface.
+
I decided to add a mobile front-end using jQuery Mobile. There were a few things I wanted to accomplish:
+
Large on/off buttons in manual mode
Easy slider inputs for any duration input
Compatibility between many/all devices
Easy feedback of current status
Easy program input/modification
+
Fortunately, I had a lot of feedback on Ray's forums and now have an application that has been tested across many devices and installed in many unique environments.
+
I fully support every feature of the OpenSprinkler and also the OpenSprinkler Pi (using the interval program).
I, Samer Albahra, am a medical school graduate, currently doing a pathology residency at UTHSCSA. I enjoy making mobile applications in my spare time and was excited when I first discovered the OpenSprinkler, an open-source Internet based sprinkler system, which lacked a truly mobile interface.
-
I decided to add a mobile front-end using jQuery Mobile. There were a few things I wanted to accomplish:
-
Large on/off buttons in manual mode
Easy slider inputs for any duration input
Compatibility between many/all devices
Easy feedback of current status
Easy program input/modification
-
Fortunately, I had a lot of feedback on Ray's forums and now have an application that has been tested across many devices and installed in many unique environments.
-
I fully support every feature of the OpenSprinkler and also the OpenSprinkler Pi (using the interval program).
-
This copy of the web application is unique because it requires no install and only requires access to the OpenSprinkler by opening a port on your home router.
");
+ return;
+ }
+
+ var open = new Object;
+ $.each(window.device.status, function (i, stn) {
+ if (stn) open[i] = stn;
+ });
+
+ if (window.device.settings.mas) {
+ open.splice(window.device.settings.mas-1,1);
+ }
+
+ if (Object.keys(open).length >= 2) {
+ var ptotal = 0;
+ $.each(open,function (key, status){
+ var tmp = window.device.settings.ps[key][1];
+ if (tmp > ptotal) ptotal = tmp;
+ });
+
+ var sample = open[0],
+ pid = window.device.settings.ps[sample][0],
+ pname = pidname(pid),
+ line = "
";
+
+ line += pname+" is running on "+Object.keys(open).length+" stations ";
+ if (pid!=255&&pid!=99) line += "("+sec2hms(ptotal)+" remaining)";
+ line += "
";
+ change_status(ptotal,window.device.options.sdt,"green",line);
+ return;
+ }
+
+ var match = false,
+ i = 0;
+ $.each(window.device.stations.snames,function (station,name){
+ var info = "";
+ if (window.device.settings.ps[i][0] && window.device.status[i] && window.device.settings.mas != i+1) {
+ match = true
+ var pid = window.device.settings.ps[i][0],
+ pname = pidname(pid),
+ line = "
";
+ line += pname+" is running on station "+name+" ";
+ if (pid!=255&&pid!=99) line += "("+sec2hms(window.device.settings.ps[i][1])+" remaining)";
+ line += "
";
+ change_status(window.device.settings.ps[i][1],window.device.options.sdt,"green",line);
+ return false;
}
- data = JSON.parse(data);
- if (window.interval_id !== undefined) clearInterval(window.interval_id);
- if (window.timeout_id !== undefined) clearTimeout(window.timeout_id);
- if (data.seconds != 0) update_timer(data.seconds,data.sdelay);
- footer.removeClass().addClass(data.color).html(data.line).slideDown();
- })
+ i++;
+ });
+
+ if (match) return;
+
+ if (window.device.settings.mm) {
+ change_status(0,window.device.options.sdt,"red","
Manual mode enabled
");
+ return;
+ }
+
+ $("#footer-running").slideUp();
+}
+
+function pidname(pid) {
+ pname = "Program "+pid;
+ if(pid==255||pid==99) pname="Manual program";
+ if(pid==254||pid==98) pname="Run-once program";
+ return pname;
}
function update_timer(total,sdelay) {
window.lastCheck = new Date().getTime();
- window.interval_id = setInterval(function(){
+ window.interval_id = setInterval(function(){
var now = new Date().getTime();
var diff = now - window.lastCheck;
if (diff > 3000) {
clearInterval(window.interval_id);
- $("#footer-running").html("");
- check_status();
+ $("#footer-running").html("");
+ update_device(check_status);
}
window.lastCheck = now;
+
if (total <= 0) {
clearInterval(window.interval_id);
- $("#footer-running").slideUp().html("");
+ $("#footer-running").slideUp().html("");
if (window.timeout_id !== undefined) clearTimeout(window.timeout_id);
- window.timeout_id = setTimeout(check_status,(sdelay*1000));
+ window.timeout_id = setTimeout(function(){
+ update_device(check_status);
+ },(sdelay*1000));
}
else
--total;
@@ -336,44 +534,10 @@ function sec2hms(diff) {
var hours = parseInt( diff / 3600 ) % 24;
var minutes = parseInt( diff / 60 ) % 60;
var seconds = diff % 60;
- if (hours) str += (hours < 10 ? "0"+hours : hours)+":";
- return str+(minutes < 10 ? "0"+minutes : minutes)+":"+(seconds < 10 ? "0"+seconds : seconds);
+ if (hours) str += pad(hours)+":";
+ return str+pad(minutes)+":"+pad(seconds);
}
-$(document).on("pagebeforeshow",function(e,data){
- var newpage = e.target.id;
-
- if (window.interval_id !== undefined) clearInterval(window.interval_id);
- if (window.timeout_id !== undefined) clearTimeout(window.timeout_id);
-
- if (newpage == "sprinklers") {
- update_weather();
- $("#footer-running").html("");
- setTimeout(check_status,1000);
- } else {
- var title = document.title;
- document.title = "OpenSprinkler: "+title;
- }
-});
-
-//This bind intercepts most links to remove the 300ms delay iOS adds
-$(document).on('pageinit', function (e, data) {
- var newpage = e.target.id;
- var currpage = $(e.target);
-
- currpage.find("a[href='#"+currpage.attr('id')+"-settings']").on('vclick', function (e) {
- e.preventDefault(); e.stopImmediatePropagation();
- highlight(this);
- $(".ui-page-active [id$=settings]").panel("open");
- });
- currpage.find("a[data-onclick]").on('vclick', function (e) {
- e.preventDefault(); e.stopImmediatePropagation();
- var func = $(this).data("onclick");
- highlight(this);
- eval(func);
- });
-});
-
function highlight(button) {
$(button).addClass("ui-btn-active").delay(150).queue(function(next){
$(this).removeClass("ui-btn-active");
@@ -383,18 +547,31 @@ function highlight(button) {
function update_weather() {
var $weather = $("#weather");
- $weather.html("");
- $.get("index.php","os_ip="+window.curr_ip+"&os_pw="+window.curr_pw+"&action=get_weather",function(result){
- var weather = JSON.parse(result);
- if (weather["code"] == null) {
+ $("#weather").unbind("click");
+ $weather.html("");
+
+ $.getJSON("http://query.yahooapis.com/v1/public/yql?q=select%20item%20from%20weather.forecast%20where%20location%3D%22"+escape(window.device.settings.loc)+"%22&format=json&callback=?",function(data){
+ if (data.query.results.channel.item.title == "City not found") {
$("#weather-list").animate({
"margin-left": "-1000px"
},1000,function(){
$(this).hide();
})
- return;
+ return;
}
- $weather.html(""+weather["temp"]+" "+weather["location"]+"");
+ var now = data.query.results.channel.item.condition,
+ text = now.text,
+ code = now.code,
+ temp = now.temp,
+ date = now.date;
+
+ var title = data.query.results.channel.item.title,
+ loc = /Conditions for (.*) at \d+:\d+ [a|p]m .*/.exec(title);
+
+ temp = temp+"°F";
+
+ $weather.html(""+temp+" "+loc[1]+"");
+ $("#weather").bind("click",get_forecast);
$("#weather-list").animate({
"margin-left": "0"
},1000).show()
@@ -402,98 +579,323 @@ function update_weather() {
}
function gohome() {
- $.mobile.changePage($('#sprinklers'), {reverse: true});
+ $("body").pagecontainer("change","#sprinklers",{reverse: true});
+}
+
+function changePage(toPage) {
+ var curr = "#"+$("body").pagecontainer("getActivePage").attr("id");
+ if (curr === toPage) {
+ bind_links(curr);
+ } else {
+ $("body").pagecontainer("change",toPage);
+ }
+}
+
+function changeFromPanel(func) {
+ var $panel = $("#sprinklers-settings");
+ $panel.one("panelclose", func);
+ $panel.panel("close");
+}
+
+function show_about() {
+ changePage("#about");
+}
+
+function open_popup(id) {
+ var popup = $(id);
+
+ popup.on("popupafteropen", function(){
+ $(this).popup("reposition", {
+ "positionTo": "window"
+ });
+ }).popup().enhanceWithin().popup("open");
}
function show_settings() {
- $.mobile.showPageLoadingMsg();
- $.get("index.php","os_ip="+window.curr_ip+"&os_pw="+window.curr_pw+"&action=make_settings_list",function(items){
- var list = $("#os-settings-list");
- list.html(items).trigger("create");
- if (list.hasClass("ui-listview")) list.listview("refresh");
- $.mobile.hidePageLoadingMsg();
- $.mobile.changePage($("#os-settings"));
- })
+ $.mobile.loading("show");
+
+ var list = new Object
+ list.start = "
",
+ isMaster = window.device.settings.mas;
+ if (isMaster) list += "
Station Name
Activate Master?
";
+ $i = 0;
+ $.each(window.device.stations.snames,function(i, station) {
+ if (isMaster) list += "
";
+ list += "";
+ if (isMaster) {
+ if (window.device.settings.mas == i+1) {
+ list += "
(Master)
";
+ } else {
+ list += "
";
+ }
+ }
+ i++;
+ });
+ if (isMaster) list += "
";
+ list += "
";
+
+ var stations = $("#os-stations-list");
+ stations.html(list).enhanceWithin();
+ if (stations.hasClass("ui-listview")) stations.listview("refresh");
+ $.mobile.loading("hide");
+ changePage("#os-stations");
+}
+
+function get_forecast() {
+/*
+ $.mobile.loading("show");
+ $.get("",function(items){
+ var list = $("#forecast_list");
+ list.html(items).enhanceWithin();
if (list.hasClass("ui-listview")) list.listview("refresh");
- $.mobile.hidePageLoadingMsg();
- $.mobile.changePage($("#os-stations"));
+ $.mobile.loading("hide");
+ changePage("#forecast");
})
+*/
}
function get_status() {
- $.mobile.showPageLoadingMsg();
- $.get("index.php","os_ip="+window.curr_ip+"&os_pw="+window.curr_pw+"&action=make_list_status",function(items){
- var list = $("#status_list");
- items = JSON.parse(items)
- list.html(items.list);
- $("#status_header").html(items.header);
- $("#status_footer").html(items.footer);
- if (list.hasClass("ui-listview")) list.listview("refresh");
- window.totals = JSON.parse(items.totals);
- if (window.interval_id !== undefined) clearInterval(window.interval_id);
- if (window.timeout_id !== undefined) clearTimeout(window.timeout_id);
- $.mobile.hidePageLoadingMsg();
- $.mobile.changePage($("#status"));
- if (window.totals["d"] !== undefined) {
- delete window.totals["p"];
- setTimeout(get_status,window.totals["d"]*1000);
+ var runningTotal = new Object,
+ allPnames = new Array;
+
+ var list = "",
+ tz = window.device.options.tz-48;
+
+ tz = ((tz>=0)?"+":"-")+pad((Math.abs(tz)/4>>0))+":"+((Math.abs(tz)%4)*15/10>>0)+((Math.abs(tz)%4)*15%10);
+
+ var header = ""+(new Date(window.device.settings.devt*1000).toUTCString().slice(0,-4))+" GMT "+tz;
+
+ runningTotal.c = window.device.settings.devt;
+
+ var master = window.device.settings.mas,
+ i = 0,
+ ptotal = 0;
+
+ var open = new Object;
+ $.each(window.device.status, function (i, stn) {
+ if (stn) open[i] = stn;
+ });
+ open = Object.keys(open).length;
+
+ if (master && window.device.status[master-1]) open--;
+
+ $.each(window.device.stations.snames,function(i, station) {
+ var info = "";
+ if (master == i+1) {
+ station += " (Master)";
+ } else if (window.device.settings.ps[i][0]) {
+ var rem=window.device.settings.ps[i][1];
+ if (open > 1) {
+ if (rem > ptotal) ptotal = rem;
+ } else {
+ ptotal+=rem;
+ }
+ remm=rem/60>>0;
+ rems=rem%60;
+ var pid = window.device.settings.ps[i][0],
+ pname = pidname(pid);
+ if (window.device.status[i] && (pid!=255&&pid!=99)) runningTotal[i] = rem;
+ allPnames[i] = pname;
+ info = "
"+((window.device.status[i]) ? "Running" : "Scheduled")+" "+pname;
+ if (pid!=255&&pid!=99) info += " ("+(remm/10>>0)+(remm%10)+":"+(rems/10>>0)+(rems%10)+" remaining)";
+ info += "
";
}
- update_timers(items.sdelay);
+ if (window.device.status[i]) {
+ var color = "green";
+ } else {
+ var color = "red";
+ }
+ list += "
"+station+"
"+info+"
";
+ i++;
})
+
+ var footer = "";
+ var lrdur = window.device.settings.lrun[2];
+
+ if (lrdur != 0) {
+ var lrpid = window.device.settings.lrun[1];
+ var pname= pidname(lrpid);
+
+ footer = '
'+pname+' last ran station '+window.device.stations.snames[window.device.settings.lrun[0]]+' for '+(lrdur/60>>0)+'m '+(lrdur%60)+'s on '+(new Date(window.device.settings.lrun[3]*1000).toUTCString().slice(0,-4))+'
';
+ }
+
+ if (ptotal) {
+ scheduled = allPnames.length;
+ if (!open && scheduled) runningTotal.d = window.device.options["sdt"];
+ if (open == 1) ptotal += (scheduled-1)*window.device.options["sdt"];
+ allPnames = allPnames.getUnique();
+ numProg = allPnames.length;
+ allPnames = allPnames.join(" and ");
+ var pinfo = allPnames+" "+((numProg > 1) ? "are" : "is")+" running ";
+ pinfo += " ("+sec2hms(ptotal)+" remaining)";
+ runningTotal.p = ptotal;
+ header += " "+pinfo;
+ }
+
+ var status = $("#status_list");
+ status.html(list);
+ $("#status_header").html(header);
+ $("#status_footer").html(footer);
+ if (status.hasClass("ui-listview")) status.listview("refresh");
+ window.totals = runningTotal;
+ if (window.interval_id !== undefined) clearInterval(window.interval_id);
+ if (window.timeout_id !== undefined) clearTimeout(window.timeout_id);
+
+ changePage("#status");
+ if (window.totals.d !== undefined) {
+ delete window.totals.p;
+ setTimeout(get_status,window.totals.d*1000);
+ }
+ update_timers(window.device.options["sdt"]);
}
function get_manual() {
- $.mobile.showPageLoadingMsg();
- $.get("index.php","os_ip="+window.curr_ip+"&os_pw="+window.curr_pw+"&action=make_list_manual",function(items){
- var list = $("#mm_list");
- list.html(items);
- if (list.hasClass("ui-listview")) list.listview("refresh");
- $.mobile.hidePageLoadingMsg();
- $.mobile.changePage($("#manual"));
+ $.mobile.loading("show");
+
+ var list = "
Sprinkler Stations
",
+ i = 0;
+
+ $.each(window.device.stations.snames,function(i,station) {
+ list += '
'.$pname.' last ran station '.$stations[$settings["lrun"][0]].' for '.($lrdur/60>>0).'m '.($lrdur%60).'s on '.gmdate("D, d M Y H:i:s",$settings["lrun"][3]).'