diff options
author | Mike Crute <mcrute@gmail.com> | 2013-08-29 09:19:44 -0400 |
---|---|---|
committer | Mike Crute <mcrute@gmail.com> | 2013-08-29 09:19:44 -0400 |
commit | 62bcc93047f677f322422fb1c9a0fab3f1923008 (patch) | |
tree | 0564b91532e43ba0e02e0ab4a304ebac0ec3952f | |
parent | e43d2aa4813ad65d2034cb356872008bee0302cc (diff) | |
download | ubntmfi-62bcc93047f677f322422fb1c9a0fab3f1923008.tar.bz2 ubntmfi-62bcc93047f677f322422fb1c9a0fab3f1923008.tar.xz ubntmfi-62bcc93047f677f322422fb1c9a0fab3f1923008.zip |
Changing to web based manager
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | mfiapi.py | 58 | ||||
-rw-r--r-- | server.py | 46 | ||||
-rw-r--r-- | templates/index.html | 18 |
4 files changed, 84 insertions, 39 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore | |||
@@ -0,0 +1 @@ | |||
*.pyc | |||
diff --git a/mfiapi.py b/mfiapi.py new file mode 100644 index 0000000..31f0330 --- /dev/null +++ b/mfiapi.py | |||
@@ -0,0 +1,58 @@ | |||
1 | import json | ||
2 | import urllib2 | ||
3 | import urllib | ||
4 | from cookielib import CookieJar | ||
5 | |||
6 | def bool_to_bin(value): | ||
7 | return 1 if value == True else 0 | ||
8 | |||
9 | |||
10 | def bin_to_bool(value): | ||
11 | return True if int(value) == 1 else False | ||
12 | |||
13 | |||
14 | class MFiAPI(object): | ||
15 | |||
16 | def __init__(self, host): | ||
17 | self.host = host | ||
18 | self.opener = urllib2.build_opener( | ||
19 | urllib2.HTTPCookieProcessor(CookieJar())) | ||
20 | |||
21 | def _make_url(self, suffix): | ||
22 | return 'https://{}/{}'.format(self.host, suffix) | ||
23 | |||
24 | def login(self, username, password): | ||
25 | form_data = urllib.urlencode({ | ||
26 | 'username' : username, | ||
27 | 'password': password, | ||
28 | 'login': 'login' | ||
29 | }) | ||
30 | |||
31 | self.opener.open(self._make_url('login'), form_data) | ||
32 | |||
33 | def get_sensor_data(self): | ||
34 | resp = self.opener.open(self._make_url('api/v1.0/list/sensors')) | ||
35 | return json.load(resp)['data'] | ||
36 | |||
37 | def port_status(self): | ||
38 | data = self.get_sensor_data() | ||
39 | return dict((i['label'], bin_to_bool(i['output_val'])) for i in data) | ||
40 | |||
41 | def set_port(self, mac, port, value): | ||
42 | data = urllib.urlencode({ "json": json.dumps({ | ||
43 | "mac": mac, | ||
44 | "val": bool_to_bin(value), | ||
45 | "port": int(port), | ||
46 | "cmd": "mfi-output" | ||
47 | }) | ||
48 | }) | ||
49 | |||
50 | self.opener.open(self._make_url("api/v1.0/cmd/devmgr"), data) | ||
51 | |||
52 | def toggle_port(self, name): | ||
53 | mac, port, status = None, None, None | ||
54 | |||
55 | for data in self.get_sensor_data(): | ||
56 | if data['label'] == name: | ||
57 | set_to = not bin_to_bool(data['output_val']) | ||
58 | self.set_port(data['mac'], data['port'], set_to) | ||
@@ -6,49 +6,41 @@ import tornado.options | |||
6 | import tornado.process | 6 | import tornado.process |
7 | import tornado.template | 7 | import tornado.template |
8 | import tornado.httpserver | 8 | import tornado.httpserver |
9 | from mfiapi import MFiAPI | ||
9 | from tornado.options import define, options | 10 | from tornado.options import define, options |
10 | 11 | ||
11 | define("port", default=8888, help="run on the given port", type=int) | 12 | define("port", default=8888, help="run on the given port", type=int) |
12 | 13 | ||
13 | 14 | ||
14 | STATUSES = { | ||
15 | 'relay1': True, # Fan | ||
16 | 'relay2': False, # Light | ||
17 | 'relay3': False, | ||
18 | } | ||
19 | |||
20 | |||
21 | # for relay in STATUSES.keys(): | ||
22 | # caller = subprocess.Popen(["ssh","admin@10.0.1.15", | ||
23 | # "cat /proc/power/{}".format(relay)], stdout=subprocess.PIPE) | ||
24 | # output = caller.communicate()[0] | ||
25 | # STATUSES[relay] = output.startswith("1") | ||
26 | |||
27 | |||
28 | class PowerStatusHandler(tornado.web.RequestHandler): | 15 | class PowerStatusHandler(tornado.web.RequestHandler): |
29 | 16 | ||
30 | def get(self): | 17 | def __init__(self, *args, **kwargs): |
31 | self.finish(STATUSES) | 18 | self.api = MFiAPI('172.16.0.38:6443') |
19 | super(PowerStatusHandler, self).__init__(*args, **kwargs) | ||
32 | 20 | ||
33 | @tornado.web.asynchronous | 21 | def get(self): |
34 | def _trigger_relay(self, relay, value): | 22 | self.api.login('admin', 'password') |
35 | STATUSES[relay] = value | 23 | self.finish(self.api.port_status()) |
36 | value = 1 if value is True else 0 | ||
37 | tornado.process.Subprocess(["ssh","admin@10.0.1.15", | ||
38 | "echo {} > /proc/power/{}".format(value, relay)], | ||
39 | stdout=tornado.process.Subprocess.STREAM) | ||
40 | 24 | ||
41 | def post(self): | 25 | def post(self): |
42 | for key, value in self.request.arguments.items(): | 26 | self.api.login('admin', 'password') |
43 | self._trigger_relay(key, value[0] == "on") | ||
44 | 27 | ||
45 | self.finish(STATUSES) | 28 | for key, value in self.request.arguments.items(): |
29 | self.api.toggle_port(key) | ||
46 | 30 | ||
47 | 31 | ||
48 | class IndexHandler(tornado.web.RequestHandler): | 32 | class IndexHandler(tornado.web.RequestHandler): |
49 | 33 | ||
34 | def __init__(self, *args, **kwargs): | ||
35 | self.api = MFiAPI('172.16.0.38:6443') | ||
36 | super(IndexHandler, self).__init__(*args, **kwargs) | ||
37 | |||
50 | def get(self): | 38 | def get(self): |
51 | self.render("index.html") | 39 | self.api.login('admin', 'password') |
40 | |||
41 | ports = [(port, port.replace(" ", "_").replace("'", "-")) for port in self.api.port_status().keys()] | ||
42 | |||
43 | self.render("index.html", ports=ports) | ||
52 | 44 | ||
53 | 45 | ||
54 | class Application(tornado.web.Application): | 46 | class Application(tornado.web.Application): |
diff --git a/templates/index.html b/templates/index.html index dba7d79..6903a10 100644 --- a/templates/index.html +++ b/templates/index.html | |||
@@ -13,12 +13,13 @@ | |||
13 | $.ajax("/power-status/").done(function(data) { | 13 | $.ajax("/power-status/").done(function(data) { |
14 | for (key in data) { | 14 | for (key in data) { |
15 | var value = data[key] ? "on" : "off"; | 15 | var value = data[key] ? "on" : "off"; |
16 | var key = key.replace("'", "-").replace(" ", "_"); | ||
16 | $("#"+key).val(value).slider("refresh"); | 17 | $("#"+key).val(value).slider("refresh"); |
17 | } | 18 | } |
18 | }); | 19 | }); |
19 | $("select[data-role=slider]").on("change", function() { | 20 | $("select[data-role=slider]").on("change", function() { |
20 | var data = {}; | 21 | var data = {}; |
21 | data[this.name] = $(this).val(); | 22 | data[$(this).attr("data-name")] = $(this).val(); |
22 | $.ajax({ url: "/power-status/", data: data, type: "POST" }); | 23 | $.ajax({ url: "/power-status/", data: data, type: "POST" }); |
23 | }); | 24 | }); |
24 | }); | 25 | }); |
@@ -32,24 +33,17 @@ | |||
32 | 33 | ||
33 | <div data-role="content"> | 34 | <div data-role="content"> |
34 | <form> | 35 | <form> |
36 | {% for name, id in ports %} | ||
35 | <div class="ui-grid-a"> | 37 | <div class="ui-grid-a"> |
36 | <div class="ui-block-a"><div class="ui-bar" style="height:60px"> | 38 | <div class="ui-block-a"><div class="ui-bar" style="height:60px"> |
37 | <label for="relay2">Light</label> | 39 | <label for="{{ id }}">{{ name }}</label> |
38 | <select name="relay2" id="relay2" data-role="slider"> | 40 | <select name="{{ id }}" id="{{ id }}" data-name="{{ name }}" data-role="slider"> |
39 | <option value="off">Off</option> | ||
40 | <option value="on">On</option> | ||
41 | </select> | ||
42 | </div></div> | ||
43 | </div><!-- /grid-a --> | ||
44 | <div class="ui-grid-a"> | ||
45 | <div class="ui-block-a"><div class="ui-bar" style="height:60px"> | ||
46 | <label for="relay1">Fan</label> | ||
47 | <select name="relay1" id="relay1" data-role="slider"> | ||
48 | <option value="off">Off</option> | 41 | <option value="off">Off</option> |
49 | <option value="on">On</option> | 42 | <option value="on">On</option> |
50 | </select> | 43 | </select> |
51 | </div></div> | 44 | </div></div> |
52 | </div><!-- /grid-a --> | 45 | </div><!-- /grid-a --> |
46 | {% end %} | ||
53 | </form> | 47 | </form> |
54 | </div><!-- /content --> | 48 | </div><!-- /content --> |
55 | </div> | 49 | </div> |