-
Notifications
You must be signed in to change notification settings - Fork 4
/
control.py
executable file
·305 lines (256 loc) · 10.6 KB
/
control.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
#!/usr/bin/env python3
# *****************************************
# irrigator - control script
# *****************************************
#
# Description: This script controls the sprinkler relays and turns
# selected relays on/off
#
# control.py -z[xx] -d[xxx] -f
# -z[xx] Zone Turn on Zone 1,2,3,4
# -d[xxx] Duration Runtime in minutes
# -f Force Force Multiple Zones or Ignore Weather
# -i Init Initialize Relays to OFF
# -w[xxxxx] Weather Zip Code
#
# This script runs as a separate process from the Flask / Gunicorn
# implementation which handles the web interface.
#
# *****************************************
import sys
import argparse
import time
import datetime
import os
import json
from common import *
def Irrigate(platform, zonename, duration, json_data_filename, relay_trigger=0):
# *****************************************
# Function: Irrigate
# Input: platform, str zonename, int duration, str json_data_filename
# Description: Turn on relay for duration
# *****************************************
errorcode = 0
event = f"Turning on Zone: {zonename} for {duration} minutes."
WriteLog(event)
errorcode = errorcode + platform.setrelay(relay_trigger, zonename) # Turning on Zone
starttime = time.time()
now = starttime
while(now - starttime < (duration*60)):
if(CheckOverride(json_data_filename) > 0):
break
else:
time.sleep(1) # Pause for 1 Second
now = time.time()
event = f"Turning off Zone: {zonename}."
WriteLog(event)
errorcode = errorcode + platform.setrelay((not relay_trigger), zonename) # Turning off Zone
if (CheckOverride(json_data_filename) > 0):
errorcode = 42
return(errorcode)
def CheckOverride(json_data_filename):
json_data_dict = ReadJSON(json_data_filename)
if (json_data_dict['controls']['manual_override'] == True):
errorcode = 42
else:
errorcode = 0
return (errorcode)
def checkweather():
# *****************************************
# Function: checkweather
# Input: none
# Output: amount, errorcode
# Description: Read precipitation amount for last day from file
# *****************************************
errorcode = 0
try:
wx_status = ReadJSON("wx_status.json", type="weather")
except:
wx_status = create_wx_json()
errorcode = 6
return(wx_status, errorcode)
# *****************************************
# Main Program Start
# *****************************************
# control.py -z[xx] -d[xxx] -f
# -z [name] Zone Manual Mode: Turn on Zone [name]
# -d [xxx] Duration Manual Mode: Runtime in minutes
# -f Force ALL Modes: Ignore Rain
# -i Init ALL Modes: Initialize Relays to OFF on reboot
# -j [filename] JSON File Alternate JSON File [default: irrigator.json]
# -s [schedule] Schedule Auto Mode: Select Schedule Run [name]
event = "***** Control Script Starting *****"
WriteLog(event)
# Parse Input Arguments
parser = argparse.ArgumentParser(description='Irrigator - Sprinkler Zone Control Script. Usage as follows: ')
parser.add_argument('-z','--zone', help='Manually turn on zone. (-z [zone_name])',required=False)
parser.add_argument('-d','--duration',help='Duration to turn on zone. (NOTE: Works with manual zone control only)', required=False)
parser.add_argument('-s','--schedule',help='Name of schedule/program to run. Auto-Mode.', required=False)
parser.add_argument('-j','--json',help='Use an alternative JSON settings file. Default = [irrigator.json]', required=False)
parser.add_argument('-f','--force',help='Force irrigation regardless of weather', action='store_true',required=False)
parser.add_argument('-i','--init',help='Initialize relays (on first boot).',action='store_true',required=False)
args = parser.parse_args()
# *****************************************
# Set variables (json,schedule,zone,duration,location)
# *****************************************
errorcode = 0
if(args.json):
json_data_filename = args.json
else:
json_data_filename = "irrigator.json"
# General open & read JSON into a dictionary
json_data_dict = ReadJSON(json_data_filename)
relay_trigger = json_data_dict['settings']['relay_trigger']
# Flag Control Active at beginning of script
json_data_dict['controls']['active'] = True
WriteJSON(json_data_dict, json_data_filename)
if(args.schedule):
schedule_selected = args.schedule
schedule_run = True
else:
schedule_selected = "null"
schedule_run = False
if(args.zone):
zone = args.zone
else:
zone = "null"
if(args.duration):
duration = int(args.duration)
else:
duration = 0
# Store init relays flag
init = args.init
# *****************************************
# Initialize Relays Globally
# *****************************************
# Init outpin structure
outpins = {}
for index_key, index_value in json_data_dict['zonemap'].items():
outpins[index_key] = json_data_dict['zonemap'][index_key]['GPIO_mapping']
outpins['gate'] = json_data_dict['settings']['zone_gate']
# Init platform object
if(json_data_dict['settings']['target_sys'] == "CHIP"):
event = "Initializing Relays on CHIP."
WriteLog(event)
from platform_chip import Platform
platform = Platform(outpins, relay_trigger=relay_trigger)
elif(json_data_dict['settings']['target_sys'] == "RasPi"):
event = "Initializing Relays on Raspberry Pi."
WriteLog(event)
from platform_raspi import Platform
platform = Platform(outpins, relay_trigger=relay_trigger)
else:
event = "Initializing Relays on NONE. Prototype Mode."
WriteLog(event)
from platform_prototype import Platform
platform = Platform(outpins, relay_trigger=relay_trigger)
# Check weather status
wx_status, errorcode = checkweather()
p_units = 'inches' if json_data_dict['wx_data']['units'] == 'F' else 'mm'
if errorcode != 0:
event = "Weather Fetch Failed for some reason. Bad API response? Network Issue?"
else:
event = ""
if json_data_dict['wx_data']['temp_enable']:
event += f"Current Temperature: {wx_status['temp_current']}{json_data_dict['wx_data']['units']} "
if json_data_dict['wx_data']['history_enable']:
event += f"Precipitation History: {wx_status['rain_history_total']} {p_units} "
if json_data_dict['wx_data']['forecast_enable']:
event += f"Precipitation Forecast: {wx_status['rain_forecast']} {p_units} "
if event != "":
WriteLog(event)
# Check if force run is enabled.
if ((args.force == True)):
force = True
event = "Force run selected. Ignoring weather."
WriteLog(event)
else:
force = False
# Check weather flags
wx_cancel = False # Set Weather Cancel to False
wx_cancel_reason = ""
# Check rain history > precip max
if (json_data_dict['wx_data']['history_enable']) and (wx_status['rain_history_total'] > json_data_dict['wx_data']['precip']):
wx_cancel = True
wx_cancel_reason += "(Precipitation History) "
# Check rain forecast > precip max
if (json_data_dict['wx_data']['forecast_enable']) and (wx_status['rain_forecast'] > json_data_dict['wx_data']['precip']):
wx_cancel = True
wx_cancel_reason += "(Precipitation Forecast) "
# Check temperature exceeds limits
if (json_data_dict['wx_data']['temp_enable']) and ((wx_status['temp_current'] < json_data_dict['wx_data']['min_temp']) or (wx_status['temp_current'] > json_data_dict['wx_data']['max_temp'])):
wx_cancel = True
wx_cancel_reason += "(Temperature) "
# Check rain forecast + history > precip max
if (json_data_dict['wx_data']['forecast_history_enable']) and (wx_status['rain_forecast'] + wx_status['rain_history_total'] > json_data_dict['wx_data']['precip']):
wx_cancel = True
wx_cancel_reason += "(Precipitation Forecast + History) "
# *****************************************
# Main Program If / Else Tree
# *****************************************
if (init):
event = "Initialize Relays Selected."
WriteLog(event)
errorcode = 0
# Schedule Run
elif ((schedule_run == True) and ((wx_cancel == False) or (force == True))):
event = "Schedule Run Selected with Schedule: " + schedule_selected
WriteLog(event)
if (schedule_selected in json_data_dict['schedules']):
event = schedule_selected + " found in JSON file. Running Now."
WriteLog(event)
json_data_dict['schedules'][schedule_selected]['start_time']['active'] = True # Set Schedule active = True
WriteJSON(json_data_dict, json_data_filename)
for index_key, index_value in sorted(json_data_dict['schedules'][schedule_selected]['zones'].items()):
if ((json_data_dict['zonemap'][index_key]['enabled'] == True) and (index_value['duration'] != 0)): # Check if zone enabled and has greater than 0 time
json_data_dict['zonemap'][index_key]['active'] = True # Set Zone Active = False
WriteJSON(json_data_dict, json_data_filename)
# Run Zone
errorcode = Irrigate(platform, index_key, index_value['duration'], json_data_filename, relay_trigger=relay_trigger)
# Read latest control data from JSON file (just in case anything changed)
json_data_dict = ReadJSON(json_data_filename)
json_data_dict['zonemap'][index_key]['active'] = False # Set Zone Active = False
WriteJSON(json_data_dict, json_data_filename)
json_data_dict['schedules'][schedule_selected]['start_time']['active'] = False # Set Schedule active = False
WriteJSON(json_data_dict, json_data_filename)
else:
event = f"{schedule_selected} not found in JSON file. Exiting Now."
WriteLog(event)
errorcode = 1
# Manual Run
elif ((schedule_run == False) and ((wx_cancel == False) or (force == True))):
event = "Manual Run Selected."
WriteLog(event)
if (zone in json_data_dict['zonemap']):
json_data_dict['zonemap'][zone]['active'] = True # Set Zone Active = True
WriteJSON(json_data_dict, json_data_filename)
# Run Zone
errorcode = Irrigate(platform, zone, duration, json_data_filename, relay_trigger=relay_trigger)
json_data_dict['zonemap'][zone]['active'] = False # Set Zone Active = False
WriteJSON(json_data_dict, json_data_filename)
else:
event = f"{zone} not found in JSON file. Exiting."
WriteLog(event)
errorcode = 1
# Weather Cancellation
elif (wx_cancel == True):
event = f"Irrigation cancelled due to weather status exceeding limits. {wx_cancel_reason}"
WriteLog(event)
errorcode = 1
# Catch All Conditions
else:
event = "No Action."
WriteLog(event)
errorcode = 0
# Cleanup GPIOs if on SBC
platform.cleanup()
# Write Result and exit
event = "Exiting with errorcode = " + str(errorcode)
WriteLog(event)
event = "***** Control Script Ended *****"
WriteLog(event)
# Flag Control Active at beginning of script
json_data_dict['controls']['active'] = False
json_data_dict['controls']['manual_override'] = False
WriteJSON(json_data_dict, json_data_filename)
exit()