forked from Dan-in-CA/SIP
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsip.py
314 lines (280 loc) · 11.9 KB
/
sip.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Python 2/3 compatibility imports
from __future__ import print_function
from __future__ import division
from six.moves import range
# standard library imports
import ast
from calendar import timegm
from datetime import date
import i18n
import json
import os
import subprocess
import sys
from threading import Thread
import time
sip_path = os.path.dirname(os.path.abspath(__file__))
os.chdir(sip_path)
# local module imports
import gv
from gpio_pins import set_output
from helpers import (
check_rain,
get_rpi_revision,
jsave,
log_run,
plugin_adjustment,
prog_match,
report_new_day,
report_station_completed,
schedule_stations,
station_names,
stop_onrain,
restart,
convert_temp,
)
from ReverseProxied import ReverseProxied
from urls import urls # Provides access to URLs for UI pages
import web # the Web.py module. See webpy.org (Enables the Python SIP web interface)
sys.path.append(u"./plugins")
gv.restarted = 1
def timing_loop():
""" ***** Main timing algorithm. Runs in a separate thread.***** """
try:
print(_(u"Starting timing loop") + u"\n")
except Exception:
pass
last_min = 0
while True: # infinite loop
cur_ord = (
date.today().toordinal()
) # day of year
if cur_ord > gv.day_ord:
gv.day_ord = cur_ord
report_new_day()
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[u"en"]
and not gv.sd[u"mm"]
and (not gv.sd[u"bsy"] or not gv.sd[u"seq"])
):
if int(gv.now // 60) != last_min: # only check programs once a minute
last_min = int(gv.now // 60)
extra_adjustment = plugin_adjustment()
for i, p in enumerate(gv.pd): # get both index and prog item
if prog_match(p) and any(p[u"duration_sec"]):
# check each station per boards listed in program up to number of boards in Options
for b in range(len(p[u"station_mask"])):
for s in range(8):
sid = b * 8 + s # station index
if gv.sd[u"mas"] == sid + 1:
continue # skip, this is master station
if (
gv.srvals[sid]
and gv.sd[u"seq"]
): # skip if currently on and sequential mode
continue
# station duration conditionally scaled by "water level"
if gv.sd[u"iw"][b] & 1 << s:
duration_adj = 1.0
if gv.sd[u"idd"]:
duration = p[u"duration_sec"][sid]
else:
duration = p[u"duration_sec"][0]
else:
duration_adj = (
gv.sd[u"wl"] / 100.0
) * extra_adjustment
if gv.sd[u"idd"]:
duration = (
p[u"duration_sec"][sid] * duration_adj
)
else:
duration = p[u"duration_sec"][0] * duration_adj
duration = int(round(duration)) # convert to int
if (
p[u"station_mask"][b] & 1 << s
): # if this station is scheduled in this program
gv.rs[sid][2] = duration
gv.rs[sid][3] = i + 1 # program number for scheduling
gv.ps[sid][0] = i + 1 # program number for display
gv.ps[sid][1] = duration
schedule_stations(p[u"station_mask"]) # turns on gv.sd["bsy"]
if gv.sd[u"bsy"]:
for b in range(gv.sd[u"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] &= ~(1 << s)
if gv.sd[u"mas"] != sid + 1: # if not master, fill out log
gv.ps[sid] = [0, 0]
gv.lrun[0] = sid
gv.lrun[1] = gv.rs[sid][3]
gv.lrun[2] = int(gv.now - gv.rs[sid][0])
print(u"logging @ time check")
log_run()
report_station_completed(sid + 1)
gv.pon = None # Program has ended
gv.rs[sid] = [0, 0, 0, 0]
else: # if this station is not yet on
if gv.rs[sid][0] <= gv.now < gv.rs[sid][1]:
if gv.sd[u"mas"] != sid + 1: # if not master
gv.srvals[sid] = 1 # station is turned on
set_output()
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]
if gv.sd[u"mas"] and gv.sd[u"mo"][b] & (
1 << s
): # Master settings
masid = gv.sd[u"mas"] - 1 # master index
gv.rs[masid][0] = gv.rs[sid][0] + gv.sd[u"mton"]
gv.rs[masid][1] = gv.rs[sid][1] + gv.sd[u"mtoff"]
gv.rs[masid][3] = gv.rs[sid][3]
elif gv.sd[u"mas"] == sid + 1: # if this is master
masid = gv.sd[u"mas"] - 1 # master index
gv.sbits[b] |= 1 << sid
gv.srvals[masid] = 1
set_output()
for s in range(gv.sd[u"nst"]):
if gv.rs[s][1]: # if any station is scheduled
program_running = True
gv.pon = gv.rs[s][3] # Store number of running program
break
program_running = False
gv.pon = None
if program_running:
if (
gv.sd[u"urs"] and gv.sd[u"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.rs[sid][2] == 0: # skip stations with no duration
continue
if gv.srvals[
sid
]: # If station is on, decrement time remaining display
if gv.ps[sid][1] > 0: # if time is left
gv.ps[sid][1] -= 1
if not program_running:
gv.srvals = [0] * (gv.sd[u"nst"])
set_output()
gv.sbits = [0] * (gv.sd[u"nbrd"] + 1)
gv.ps = []
for i in range(gv.sd[u"nst"]):
gv.ps.append([0, 0])
gv.rs = []
for i in range(gv.sd[u"nst"]):
gv.rs.append([0, 0, 0, 0])
gv.sd[u"bsy"] = 0
if gv.sd[u"mas"] and ( # master is defined
gv.sd[u"mm"] or not gv.sd[u"seq"]
): # manual or concurrent mode.
for b in range(gv.sd[u"nbrd"]): # set stop time for master
for s in range(8):
sid = b * 8 + s
if (
gv.sd[u"mas"] != sid + 1 # if not master
and gv.srvals[sid] # station is on
and gv.rs[sid][1]
>= gv.now # station has a stop time >= now
and gv.sd[u"mo"][b] & (1 << s) # station activates master
):
gv.rs[gv.sd[u"mas"] - 1][1] = (
gv.rs[sid][1] + gv.sd[u"mtoff"]
) # set to future...
break # first found will do
if gv.sd[u"urs"]:
check_rain() # in helpers.py
if gv.sd[u"rd"] and gv.now >= gv.sd[u"rdst"]: # Check if rain delay time is up
gv.sd[u"rd"] = 0
gv.sd[u"rdst"] = 0 # Rain delay stop time
jsave(gv.sd, u"sd")
time.sleep(1)
# print("wl: ", gv.sd[u"wl"])
#### End of timing loop ####
class SIPApp(web.application):
"""Allow program to select HTTP port."""
def run(
self, port=gv.sd[u"htp"], ip=gv.sd[u"htip"], *middleware
): # get port number from options settings
func = self.wsgifunc(*middleware)
func = ReverseProxied(func)
return web.httpserver.runsimple(func, (ip, port))
app = SIPApp(urls, globals())
# disableShiftRegisterOutput()
web.config.debug = False # Improves page load speed
web.config._session = web.session.Session(
app, web.session.DiskStore(u"sessions"), initializer={u"user": u"anonymous"}
)
template_globals = {
"gv": gv,
u"str": str,
u"eval": eval,
u"convert_temp": convert_temp,
u"session": web.config._session,
u"json": json,
u"ast": ast,
u"_": _,
u"i18n": i18n,
u"app_path": lambda p: web.ctx.homepath + p, # - test
u"web": web,
u"round": round,
u"time": time,
u"timegm": timegm,
}
template_render = web.template.render(
u"templates", globals=template_globals, base=u"base"
)
if __name__ == u"__main__":
#########################################################
#### Code to import all webpages and plugin webpages ####
import plugins
try:
print(_(u"plugins loaded:"))
except Exception as e:
print(u"Import plugins error", e)
pass
for name in plugins.__all__:
print(u" ", name)
gv.plugin_menu.sort(key=lambda entry: entry[0])
# Keep plugin manager at top of menu
try:
for i, item in enumerate(gv.plugin_menu):
if u"/plugins" in item:
gv.plugin_menu.pop(i)
except Exception as e:
print(u"Creating plugins menu", e)
pass
tl = Thread(target=timing_loop)
tl.daemon = True
tl.start()
if gv.use_gpio_pins:
set_output()
app.notfound = lambda: web.seeother(u"/")
###########################
#### For HTTPS (SSL): ####
if gv.sd["htp"] == 443:
try:
from cheroot.server import HTTPServer
from cheroot.ssl.builtin import BuiltinSSLAdapter
HTTPServer.ssl_adapter = BuiltinSSLAdapter(
certificate='/usr/lib/ssl/certs/SIP.crt',
private_key='/usr/lib/ssl/private/SIP.key'
)
except IOError as e:
gv.sd[u"htp"] = int(80)
jsave(gv.sd, u"sd")
print(u"SSL error", e)
restart(2)
app.run()