forked from Groguard/CHIP-Web-Control
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
353 lines (299 loc) · 16 KB
/
main.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
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
#!/usr/bin/env python3
import gevent
import gevent.monkey
from gevent.pywsgi import WSGIServer
gevent.monkey.patch_all()
from flask import Flask, render_template, request, redirect, url_for, flash, Response
from werkzeug.datastructures import ImmutableOrderedMultiDict
from time import sleep
import CHIP_IO.GPIO as GPIO
import threading
import configparser
app = Flask(__name__)
app.secret_key = "super secret key"
# Load the config file
config = configparser.ConfigParser()
configPath = 'usersettings.ini'
# list of all pins on CHIP
chip_io = ['TWI1-SDA', 'TWI1-SCK', 'LCD-D2', 'PWM0', 'LCD-D4', 'LCD-D3', 'LCD-D6', 'LCD-D5',
'LCD-D10', 'LCD-D7', 'LCD-D12', 'LCD-D11', 'LCD-D14', 'LCD-D13', 'LCD-D18',
'LCD-D15', 'LCD-D20', 'LCD-D19', 'LCD-D22', 'LCD-D21', 'LCD-CLK', 'LCD-D23',
'LCD-VSYNC', 'LCD-HSYNC', 'LCD-DE', 'UART1-TX', 'UART1-RX', 'LRADC', 'XIO-P0',
'XIO-P1', 'XIO-P2', 'XIO-P3', 'XIO-P4', 'XIO-P5', 'XIO-P6', 'XIO-P7', 'AP-EINT1',
'AP-EINT3', 'TWI2-SDA', 'TWI2-SCK', 'CSIPCK', 'CSICK', 'CSIHSYNC', 'CSIVSYNC',
'CSID0', 'CSID1', 'CSID2', 'CSID3', 'CSID4', 'CSID5', 'CSID6', 'CSID7']
# Live dictionary for states of pins
pass_data = {}
pass_data['chip_pins'] = chip_io
pass_data['devices'] = {}
def initial_setup(): # Starts first to build live dictionary
# Clean up GPIO at start
GPIO.cleanup()
# Read the config file to get devices and settings
config.read(configPath)
# Get the zone count from config to add to live dictionary
zones = config.get('zones', 'zone_count')
# loop through all devices in the config
for device in config.sections()[1:]:
# Nested dictionary for storing device options in the device dictionary
deviceData = {}
# loop through all device options for the current device
for deviceOptions in config.options(device):
# Add device options to deviceData dictionary
deviceData[deviceOptions] = config.get(device, deviceOptions)
# Add device and deviceData to live dictionary
pass_data['devices'][device] = deviceData
# Add a device dictionary to track the state of the device
pass_data['devices'][device]['state'] = 'Off'
# Add the number of zones from the config file
pass_data['zone_count'] = (int(zones))
# Setup devices after building live dictionary
setup_devices()
def setup_devices(): # Initial setup for devices saved in config at startup
for device in pass_data['devices']:
# If the device is set for Input in the live dictionary
if pass_data['devices'][device]['input_output'] == 'Input':
input_setup(device)
# If the device is set for Output in the live dictionary
elif pass_data['devices'][device]['input_output'] == 'Output':
output_setup(device)
# If a device timer is set to On, launch timer thread
if pass_data['devices'][device]['timer_onoff'] == 'On':
timer_thread_setup(device, pass_data['devices'][device]['pin'])
def input_setup(device):
# If the device is set to 'Input' and internal resistor 'Pull Up'
if (pass_data['devices'][device]['pullup_pulldown'] == 'Pull Up'
and pass_data['devices'][device]['input_output'] == 'Input'):
GPIO.setup(pass_data['devices'][device]['pin'], GPIO.IN, pull_up_down=GPIO.PUD_UP)
# If the device is set to 'Input' and internal resistor 'Pull Down'
elif (pass_data['devices'][device]['pullup_pulldown'] == 'Pull Down'
and pass_data['devices'][device]['input_output'] == 'Input'):
GPIO.setup(pass_data['devices'][device]['pin'], GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
def output_setup(device):
# If the device is set to 'Output'
if pass_data['devices'][device]['input_output'] == 'Output':
GPIO.setup(pass_data['devices'][device]['pin'], GPIO.OUT) # Set the pin to output
# If the device is set to 'Output' and the default state is 'High'
elif (pass_data['devices'][device]['defaultstate'] == 'High'
and pass_data['devices'][device]['input_output'] == 'Output'):
GPIO.output(pass_data['devices'][device]['pin'], GPIO.HIGH) # Set the pin state High
# If the device is set to 'Output' and the default state is 'Low'
elif (pass_data['devices'][device]['defaultstate'] == 'Low'
and pass_data['devices'][device]['input_output'] == 'Output'):
GPIO.output(pass_data['devices'][device]['pin'], GPIO.LOW) # Set the pin state to Low
def timer_thread_setup(device, pin):
# Start device timer thread
deviceName = device
device = threading.Thread(target=timer, args=(deviceName, pin))
device.daemon = True
device.start()
# Devices threaded timer generator
def timer(device, pin):
while True:
try:
if pass_data['devices'][device]['timer_onoff'] == 'On':
time1 = pass_data['devices'][device]['timeon']
time2 = pass_data['devices'][device]['timeoff']
if pass_data['devices'][device]['reverse_logic'] == 'No': # for reverse high/low logic
GPIO.output(pin, GPIO.HIGH)
pass_data['devices'][device]['state'] = 'On'
sleep(float(time1)*3600)
GPIO.output(pin, GPIO.LOW)
pass_data['devices'][device]['state'] = 'Off'
sleep(float(time2)*3600)
else:
GPIO.output(pin, GPIO.HIGH)
pass_data['devices'][device]['state'] = 'Off'
sleep(float(time1)*3600)
GPIO.output(pin, GPIO.LOW)
pass_data['devices'][device]['state'] = 'On'
sleep(float(time2)*3600)
else:
sleep(5)
except KeyError: # The device doesn't exist anymore, pass
sleep(300)
@app.route('/')
def index():
return render_template('index.html', pass_data=pass_data)
# For adding new devices
@app.route('/add', methods=['GET', 'POST'])
def addDevice():
config.read(configPath)
if request.method == 'POST':
#request.parameter_storage_class = ImmutableOrderedMultiDict
# Device Name
lastDevice = request.form['device[]']
# Check if the device name is empty
if lastDevice == '':
flash("Device name can't be empty")
return redirect(url_for('addDevice'))
# Check if the device name has spaces in it
elif ' ' in lastDevice:
flash("Device name can't contain spaces")
return redirect(url_for('addDevice'))
# Check if the name is 'default' or 'Default'. configparser doesn't allow sections with that name.
elif lastDevice.lower() == 'default':
flash('Device name can\'t be "Default" or "default"')
return redirect(url_for('addDevice'))
# Go through each section minus the first of the config, we just want devices
for device in config.sections()[1:]:
# Check if the device name is taken and if the pin is in use
if (config.get(device, 'pin') == request.form['pin[]']
and device == lastDevice):
flash("Device name already taken and Device pin is already in use.")
return redirect(url_for('addDevice'))
# Check if the name of the device is already in use
elif device == lastDevice:
flash("Device name already taken")
return redirect(url_for('addDevice'))
# Check if the pin is already in use
elif config.get(device, 'pin') == request.form['pin[]']:
flash("Device pin is already in use.")
return redirect(url_for('addDevice'))
else:
# Add the new device to the config file
config.add_section(lastDevice)
config.set(lastDevice, 'pin', request.form['pin[]'])
config.set(lastDevice, 'input_output', request.form['input_output[]'])
config.set(lastDevice, 'pullup_pulldown', request.form['pullup_pulldown[]'])
config.set(lastDevice, 'defaultstate', request.form['high_low[]'])
config.set(lastDevice, 'timer_onoff', request.form['timer_onoff[]'])
config.set(lastDevice, 'timer_scale', request.form['timer_scale[]'])
config.set(lastDevice, 'timeon', request.form['timeon[]'])
config.set(lastDevice, 'timeoff', request.form['timeoff[]'])
config.set(lastDevice, 'zone', request.form['zone[]'])
config.set(lastDevice, 'reverse_logic', request.form['reverse_logic[]'])
# Add the new device to the live dictionary
pass_data['devices'][lastDevice] = {}
pass_data['devices'][lastDevice]['pin'] = request.form['pin[]']
pass_data['devices'][lastDevice]['input_output'] = request.form['input_output[]']
pass_data['devices'][lastDevice]['pullup_pulldown'] = request.form['pullup_pulldown[]']
pass_data['devices'][lastDevice]['defaultstate'] = request.form['high_low[]']
pass_data['devices'][lastDevice]['timer_onoff'] = request.form['timer_onoff[]']
pass_data['devices'][lastDevice]['timer_scale'] = request.form['timer_scale[]']
pass_data['devices'][lastDevice]['timeon'] = request.form['timeon[]']
pass_data['devices'][lastDevice]['timeoff'] = request.form['timeoff[]']
pass_data['devices'][lastDevice]['zone'] = request.form['zone[]']
pass_data['devices'][lastDevice]['reverse_logic'] = request.form['reverse_logic[]']
pass_data['devices'][lastDevice]['state'] = 'Off'
# Setup the pin
if pass_data['devices'][lastDevice]['input_output'] == 'Output':
# Device is set to output
output_setup(lastDevice)
else:
# Device is set to input
input_setup(lastDevice)
# Check if the timer is set to on and start the timer thread
if pass_data['devices'][lastDevice]['timer_onoff'] == 'On':
timer_thread_setup(lastDevice, pass_data['devices'][lastDevice]['pin'])
# Write the new configs to the config file
with open(configPath, "r+", encoding='utf8') as config_file:
config.write(config_file)
return render_template('addDevice.html', pass_data=pass_data)
# For editing device settings
@app.route('/edit', methods=['GET', 'POST'])
def editDevice():
config.read(configPath)
if request.method == 'POST':
# If request is Update Device Settings
if request.form['submit'] == 'Update Device Settings':
# Grab device name
device = request.form['device[]']
# Remove all current options from the device in the config
config.remove_option(device, 'pin')
config.remove_option(device, 'input_output')
config.remove_option(device, 'pullup_pulldown')
config.remove_option(device, 'defaultstate')
config.remove_option(device, 'timer_onoff')
config.remove_option(device, 'timer_scale')
config.remove_option(device, 'timeon')
config.remove_option(device, 'timeoff')
config.remove_option(device, 'zone')
config.remove_option(device, 'reverse_logic')
# Set the new option settings for the device in the config
config.set(device, 'pin', request.form['pin[]'])
config.set(device, 'input_output', request.form['input_output[]'])
config.set(device, 'pullup_pulldown', request.form['pullup_pulldown[]'])
config.set(device, 'defaultstate', request.form['high_low[]'])
config.set(device, 'timer_onoff', request.form['timer_onoff[]'])
config.set(device, 'timer_scale', request.form['timer_scale[]'])
config.set(device, 'timeon', request.form['timeon[]'])
config.set(device, 'timeoff', request.form['timeoff[]'])
config.set(device, 'zone', request.form['zone[]'])
config.set(device, 'reverse_logic', request.form['reverse_logic[]'])
# Update the live dictionary with the new settings
pass_data['devices'][device]['pin'] = request.form['pin[]']
pass_data['devices'][device]['input_output'] = request.form['input_output[]']
pass_data['devices'][device]['pullup_pulldown'] = request.form['pullup_pulldown[]']
pass_data['devices'][device]['defaultstate'] = request.form['high_low[]']
pass_data['devices'][device]['timer_onoff'] = request.form['timer_onoff[]']
pass_data['devices'][device]['timer_scale'] = request.form['timer_scale[]']
pass_data['devices'][device]['timeon'] = request.form['timeon[]']
pass_data['devices'][device]['timeoff'] = request.form['timeoff[]']
pass_data['devices'][device]['zone'] = request.form['zone[]']
pass_data['devices'][device]['reverse_logic'] = request.form['reverse_logic[]']
# Write the new configs to the config file
with open(configPath, "w", encoding='utf8') as config_file:
config.write(config_file)
# If request is to remove device
if request.form['submit'] == 'Remove Device':
# Remove the device from the config
config.remove_section(request.form['device[]'])
# Remove the device from the live dictionary
pass_data['devices'].pop(request.form['device[]'], None)
# Clean up the GPIO
GPIO.cleanup(request.form['pin[]'])
# Write the new configs to the config file
with open(configPath, "w", encoding='utf8') as config_file:
config.write(config_file)
return redirect(url_for('addDevice'))
# For editing the global zone count
@app.route('/_zoneCount', methods=['POST'])
def zoneCount():
config.read(configPath)
zone_count = request.form['zone[]']
config.set('zones', 'zone_count', zone_count)
with open(configPath, "r+") as config_file:
config.write(config_file)
return redirect(url_for('addDevice'))
# ajax GET call function to set state of pins
@app.route("/_state")
def _state():
# Get the current state of the button
state = request.args.get('state')
# Get the current pin
device = request.args.get('device')
pin = pass_data['devices'][device]['pin']
if pass_data['devices'][device]['reverse_logic'] == 'No': # for reverse high/low logic
if state=="Power On": # If the current state of the button is 'Power On'
GPIO.output(pin, GPIO.HIGH) # Set that pin state to High
pass_data['devices'][device]['state'] = 'On' # Set the device state in the live dictionary
else:
GPIO.output(pin, GPIO.LOW) # Set the pin state to Low
pass_data['devices'][device]['state'] = 'Off' # Set the device state in the live dictionary
else: # for reverse high/low logic
if state=="Power On": # If the current state of the button is 'Power On'
GPIO.output(pin, GPIO.LOW) # Set that pin state to Low
pass_data['devices'][device]['state'] = 'On' # Set the device state in the live dictionary
else:
GPIO.output(pin, GPIO.HIGH) # Set the pin state to High
pass_data['devices'][device]['state'] = 'Off' # Set the device state in the live dictionary
return ""
def event_stream():
while True:
for device in pass_data['devices']:
pin = pass_data['devices'][device]['pin']
if pass_data['devices'][device]['state'] == 'On':
yield 'data: %s %s Power On \n\n' % (device, pin)
else:
yield 'data: %s %s Power Off \n\n' % (device, pin)
gevent.sleep(1)
@app.route('/my_event_source')
def sse_request():
return Response(event_stream(), mimetype='text/event-stream')
if __name__ == '__main__':
initial_setup()
print('Starting web server')
server = WSGIServer(('0.0.0.0', 5000), app)
print('Web server running..')
server.serve_forever()