From d1792ea7bf72025f30ae268a7fd247a2dc4dc96c Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Sat, 21 Apr 2018 21:58:19 -0400 Subject: [PATCH 01/22] Add routing table, proxy endpoint --- postgres/create_schema.sql | 11 +++++++ web/index.py | 62 +++++++++++++++++++++++++++++++------- web/postgresLibrary.py | 21 +++++++++++++ web/requirements.txt | 3 +- 4 files changed, 85 insertions(+), 12 deletions(-) diff --git a/postgres/create_schema.sql b/postgres/create_schema.sql index 3026ac1..0515607 100644 --- a/postgres/create_schema.sql +++ b/postgres/create_schema.sql @@ -129,3 +129,14 @@ CREATE TABLE employees ( last_name text, admin boolean DEFAULT false ); + +-- Routing Table. +-- We can redirect based on peripheral OR by room. +CREATE TABLE routing ( + uuid uuid PRIMARY KEY DEFAULT gen_random_uuid(), + room_uuid uuid REFERENCES rooms(uuid), + peripheral_uuid uuid REFERENCES peripherals(uuid), + metric integer DEFAULT 0, + target text NOT NULL, + enabled BOOLEAN default true +); \ No newline at end of file diff --git a/web/index.py b/web/index.py index 9c3427c..4d5310e 100644 --- a/web/index.py +++ b/web/index.py @@ -1,9 +1,10 @@ #!/usr/bin/python from flask import Flask from flask_restful import Resource, Api -from flask import request +from flask import request, Response from flask.json import jsonify from flask import render_template +import requests import json import postgresLibrary as pL import uuid @@ -21,16 +22,10 @@ PORT_NUMBER = 8000 app = Flask(__name__) api = Api(app) -class Index(Resource): - def get(self): - # rooms = pL.getRooms() - # for room in rooms: - # print(room) - # room['pheripherals'] = pL.getPeripheralsbyRoom(room['uuid']) - # return rooms - return app.send_static_file('static/ManagementApp.html') - -api.add_resource(Index, '/') +# class Index(Resource): +# def get(self): +# return app.send_static_file('/src/static/ManagementApp.html') +# api.add_resource(Index, '/') #################################### @@ -41,6 +36,51 @@ api.add_resource(Index, '/') # Room Info #################################### +@app.route('/api/peripheral/', methods=['POST']) +def setPeripheralID(uuid): + input = request.get_json() + + if input == None: + return None + + per = pL.getPeripheralbyUUID(uuid) + print(per['room_uuid'], uuid) + entries = pL.routingEntries(per['room_uuid'], uuid) + + if len(entries) == 0: + return "locally handled." + else: + dest = entries[0] + resp = requests.request( + method=request.method, + url='http://' + dest['target'] + request.script_root + request.full_path, + headers={key: value for (key, value) in request.headers if key != 'Host'}, + data=request.get_data(), + cookies=request.cookies, + timeout=0.5, + allow_redirects=False + ) + + excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection'] + headers = [(name, value) for (name, value) in resp.raw.headers.items() + if name.lower() not in excluded_headers] + + response = Response(resp.content, resp.status_code, headers) + return response + + +@app.route('/api/guest/rooms', methods=['GET','POST']) +def getGuestRooms(): + rooms = pL.getRooms() + for room in rooms: + print(room) + room['pheripherals'] = pL.getPeripheralsbyRoom(room['uuid']) + return rooms + +@app.route('/api/routes//entries', methods=['GET','POST']) +def getRoutes(uuid): + return jsonify(pL.routingEntries(uuid, uuid)) + # Get all rooms @app.route('/api/admin/rooms', methods=['GET', 'POST']) def getRooms(): diff --git a/web/postgresLibrary.py b/web/postgresLibrary.py index cd2ab34..06ea678 100644 --- a/web/postgresLibrary.py +++ b/web/postgresLibrary.py @@ -26,6 +26,13 @@ def getAll( query ): closeConnection(conn, cur) return values +def getAllParams( query, params ): + conn, cur = setupConnection() + cur.execute(query, params) + values = cur.fetchall() + closeConnection(conn, cur) + return values + def getItembyUUID( query, uuid ): conn, cur = setupConnection() cur.execute(query, (uuid,)) @@ -464,3 +471,17 @@ def deleteEmployee( empUUID ): def updateEmployeeAdmin( admin, empUUID): query = "UPDATE employees SET admin = (%s) WHERE uuid = (%s);" return updateItem(query, (admin,empUUID)) + +# Get routing table entries for a room / peripheral ID. +def routingEntries ( room_uuid, peripheral_uuid ): + return getAllParams(""" + SELECT * + FROM routing + WHERE + ( + room_uuid = %s OR + peripheral_uuid = %s + ) + AND enabled + ORDER BY metric ASC + """, (room_uuid, peripheral_uuid)) diff --git a/web/requirements.txt b/web/requirements.txt index 8f02268..d1a904e 100644 --- a/web/requirements.txt +++ b/web/requirements.txt @@ -1,3 +1,4 @@ psycopg2 flask -flask_restful \ No newline at end of file +flask_restful +requests \ No newline at end of file From e9b4df92aef9ff987528452d9dd4b7143116a6fd Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Sun, 22 Apr 2018 17:21:21 -0400 Subject: [PATCH 02/22] Add peripherals --- .gitignore | 1 + lights/__pycache__/discover.cpython-35.pyc | Bin 2028 -> 0 bytes lights/__pycache__/protocol.cpython-35.pyc | Bin 3110 -> 0 bytes lights/__pycache__/smartbulb.cpython-35.pyc | Bin 1598 -> 0 bytes web/Dockerfile | 1 + web/index.py | 20 ++++++++++------- {lights => web/lights}/README.md | 0 {lights => web/lights}/discover.py | 0 {lights => web/lights}/protocol.py | 0 {lights => web/lights}/smartbulb.py | 0 {lights => web/lights}/test.py | 0 web/peripherals.py | 13 +++++++++++ web/postgresLibrary.py | 23 ++++++++++++++++++++ 13 files changed, 50 insertions(+), 8 deletions(-) delete mode 100644 lights/__pycache__/discover.cpython-35.pyc delete mode 100644 lights/__pycache__/protocol.cpython-35.pyc delete mode 100644 lights/__pycache__/smartbulb.cpython-35.pyc rename {lights => web/lights}/README.md (100%) rename {lights => web/lights}/discover.py (100%) rename {lights => web/lights}/protocol.py (100%) rename {lights => web/lights}/smartbulb.py (100%) rename {lights => web/lights}/test.py (100%) create mode 100644 web/peripherals.py diff --git a/.gitignore b/.gitignore index 53268b7..57465e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ ignorecase = true +__pycache__ diff --git a/lights/__pycache__/discover.cpython-35.pyc b/lights/__pycache__/discover.cpython-35.pyc deleted file mode 100644 index b494d342792bbbc23f503a4e6f9dc0d1d7120a63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2028 zcmZ7%O>g5=aGvcrj+?aIE@ijU0#-SoNUK&t^gx8rmZt2kDDBd8cUgmEx%O-7#9w{y zr6r4VKnoH_uH3ltE0*8kE2r`kI5FcSCF*P5c;3vs`F_)Tt(N`buV3AtEP%gZVd;oJ z!Y#i;5aN$P4B+123|v;xHHbAxbZ~V@3~&udYT(v@(H$LP6OuZ(bzqM}1Hu~ojN}@; z(ZIDxXRmEuqX9T<>!s0+0#zC(qc+|on9(aXv+S0e)v+Ak!M1cb&|+Nsx$>xBasFqM{5~3MYo4rju=2uRbps?SO*3024I2|4JJA$ z1_M?>t%B$^(hVpK7#J0$RzZvxSvs)WjQS$OfiW@?p?|0;`Be zv(mo|1**k-GWbPeJ@I88G!K492qrz>Np!;ZT zwX$#r3X3d|ktHaWs8AbNsf|e!usTH(h9-y)7$QXrsVzv~rJBtcJH)^vF|dp%GFYQ@ z(WSRg)M0QJ3Y)aEi?{IdWx5UqW$+ys+8}a<>s89&Ta?6_`~P6>ZD+Q6JPoB2&bnai zBwR|r&mEOHAMfmJE$q&xxSS|;G7)ldqW}d%Y>_}Y{-qzq{%On|Im)RQc!;daDBzN= zsdMhcS>VS`%GFgSh7WIa_-W`|`H_;&Stgu*bjeYC+C~+{PvsRC$Q4;KSGi^1T<_$* z@Dpd=h#hDCvYaT%W9;Bm`6|j#XXcC~zkP3JspCA!uF%7@Pjci=Wm|BWXQ|}!AzD83 zM=^Sd{GHocDBzAmkk}H_>YR$q4+CGSc~)=fpoDJyuSY1H79FfbpcaVLBkDN2Lil4R zJ97pS1IbPYm~rX+xEbY}51maiOJ97q9zER6$&H4?*DDJ}q=H^!{o&d`5Wy;ds3RtD zWXt%q!+nb(__n-Ince~x=>dt&(7!+Gs3r9&Vg-PaCx=(w1 zM=$n|UwPj=N9fjh8i+CKHz~s^b1lJxOX`cV7Gt)hTMv09L1%25()9BjMMV?!wU>pB zA3a~co(FvYT?odedW7`ABMu!qP#RH(QbVZW#|ZQx%^ zThmsUqnVn{bhgf#>^@sY*urhIHKeSv8hd?bKEb!<`r3ABd0y%#-1AD?^O7ta#e_FK z?_%V~Qw|PYOmI`u(+F_LpJ(B;#Cnt}LETa6QVH=sVpUWN;60*IfoMz0Y!jVYs~pu? z#c4Oh8cE$n;F{P?QQ8+8QRx3crGHurYOCtTf__S(AS0#q+0^^bsb?jbyU!l5RsBB$ CV)2Cl diff --git a/lights/__pycache__/protocol.cpython-35.pyc b/lights/__pycache__/protocol.cpython-35.pyc deleted file mode 100644 index 46f7008a7d17337248c5251bae8f7e3198b2e473..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3110 zcmb7GO>Z1Y8Gid?J~OuCY(h3HEG4n9JFuQfkPyOx)mpJhMl!Z#kJn+O)u`QF_Vj#o zx2tQ`8OuIpkvQ%J2??$o5I=x3KY<&nD<}U2F7Uk7-09?3f-8x%pPOi_itDADH*y@H7)s{rX` zimDXVD5}#J9>>eOD|YuT8;b5w1Pe>Ic2_A{Mk*`jVRvskPDTflKGHJ#K~R zq+OVwTW`;8*RAwc5~^&*0k=10sAidseInsB$_tV6K^zW5prjbbp-gfaiD?o^Wz*Zp zf)H757dsz{Po&B@`=7NQFQP#(=jZ3Gz>2m~b=saA%G=wWr&~J*TWdGs*MA2D;+6M_ zu1lCTed7<(T$_rLXH$uZpQTEhvXXkLl8|A{4=qD$~o{ z$^ka6H4Q_+UX@1h`d88o8ZI$#FVPS{aEX2on8G|h9zpxE#5=!$HP-TJ1E!4 zGhQsw!%>Oyf3sji4QcOeNJmEi_{AE{?px<8Hif&a;T$FR$k(ehs*`Fscz%~S^n&F% zoqHU+^Wp`k{}wwbdc_8Xb3wK^CK#=R3ML}gN^rr~?I@A}?UE`HXCjCqsO2|p z6jGgx0pUE^RWrn^KJ?nfQPU)Mf{o$LSMQOlS% zADDWa$4Rb(B$UQyN5*IKrW8#lS+2N2%*y80Z#E9MyZvtGw_Cf1T~p4}a3r-^4V4VE z?1L#18FE(}pVKiFiIzv7o6_-2%iQ=%hMz%At7)iv|OYbc*OXG*dpF zo}5V4E5pO79>C0Cnv8nY8&Ld>)Q_R|-ygOIAj@_ZgP8J>PP6uQd^*s1JDW3aE1Q`T zSV7GmKtBHv11+yOD{js4oVVTYxHZ3w*^1L}gj08#PR%Pj_njrb<~H0%?#1`t!00V& zv*{Ko+=11Q3&al}Ilq=?`j~)ch6Sa#q&*s<%Rw(_XnVJy)zL{;F1>uhE1RD-nRJ+# z(2yV+hc*hP0Vjcen1^tIqL0s7bcBV2rhDU!Uvt5PPenM-RW&~JG(pmPqvK=hK>&ml-Yp!80rvu_ zhZvsY&ACmeT65lYmz=v!*;#cje(Py9~z#2E9E$$g6i{hSw@^Lkw z*7i3E*VLnzzvdcle)@BJTcD<>tgYS#2z~J}udHuDVQJmg_eXSuD`N;46cE9rjB3>7p_Q?AvAlT;RH6FOQz2()!5uSG@(#rU6FQaajCBG9G8Zs8Jqodgv1I+kyJ+JlHbKY6?9DCU4wpCVUA=^-}?*^jCI Rs>u2yCbXQxe$V-y_kT(N2crN0 diff --git a/lights/__pycache__/smartbulb.cpython-35.pyc b/lights/__pycache__/smartbulb.cpython-35.pyc deleted file mode 100644 index f1d5c34d6565e16f0e238ecb8991e83a3103ae2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1598 zcma)6O>f*b5FLt=R{LcKMiSd85Clk!Kq#>L2Z|tQQ?!R*6TktI>_u2hv@Nez+LCk| z8+I@5p-2CVqCca5gV&yN>bbe!!+Qs z6mIoSdp+*EMiD49yxBzns!=iRBDz3D9V!yKj(kwZpT~NaiXIhxx|WP!nvlW@)(2D! zso1z(M~ncVP1p4pzCNuECOSY>sdV9SW2{S-H<(lx&x0dGpaglmwDxP(mnkA{bYO$TF1+B{6d+z zZI@{mvB;WT$JJTmBnF7T%i%@fjFR05Nvvk_xD!3$D7?i1hxQ24NtO2O|H1(zYc zt@kLjlIA_qM;(2f#t$sLF#-^bs$wipyvv#lh z?T1`>fu*tTqC!1nv%zK)#wA*rWm6k94b%tEWo!DpIm~>j>{wMx<>L**rEmb!q!@s> z-C#?cZ?BQJegg9DG549VGpkzeSPA@uH^X!H3==Q{g609~drT1YP(!qm9W)lm0!<*a zWfO9Tq(L9Ljr(#x?Mz(oYVuJalV&rGn>uR_56_=^2~_2w8kLP5tpriALdyEcDE(uZ zD|g@OS#3)0#?0@vWv4>lMLw|W`MbxtyS4hM-F5x7(9ijcJnlz0VFoZ1fUzJJg8+it zTyf^3JXe}as+BSS=1Sr$^X0jE42PBEb;Dib<;C9ukQ1y32`Bmsg#K^*dI!5fO*dJsN*Q~nPk;ZDx z9BIa?8LVerKY-_CSzCYQS{c3|R;k7}oOt+#9>|Biy^Wo|U!9~K-xB@{f@VHj;_qJF d;Ns@sMCI1U{#R<3eAU+bj1^_LsypJo{0CDvPm2Hm diff --git a/web/Dockerfile b/web/Dockerfile index 02294a3..f85b328 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -17,6 +17,7 @@ RUN pip install -r /src/requirements.txt COPY index.py /src/index.py COPY static/ManagementApp.html /src/static/ManagementApp.html COPY postgresLibrary.py /src/postgresLibrary.py +COPY lights/* /src/lights/* # Add entrypoint COPY docker-entrypoint.sh /usr/local/bin/ diff --git a/web/index.py b/web/index.py index 4d5310e..301eb9a 100644 --- a/web/index.py +++ b/web/index.py @@ -8,6 +8,7 @@ import requests import json import postgresLibrary as pL import uuid +import peripherals as peripherals from datetime import date, datetime @@ -38,19 +39,22 @@ api = Api(app) @app.route('/api/peripheral/', methods=['POST']) def setPeripheralID(uuid): - input = request.get_json() + body = request.get_json() - if input == None: + if body == None: return None per = pL.getPeripheralbyUUID(uuid) - print(per['room_uuid'], uuid) - entries = pL.routingEntries(per['room_uuid'], uuid) - - if len(entries) == 0: - return "locally handled." + roomRoutes = pL.roomRoutingEntries(per['room_uuid']) + peripheralRoutes = pL.peripheralRoutingEntries(uuid) + + # At the room level, we simply proxy requests. + # If there's a specific peripheral IP in a peripheral route, it will be ignored if there is a room route. + if len(roomRoutes) == 0: + # We're doing a peripheral specific handling. + peripherals.handlePeripheral(per, body, peripheralRoutes) else: - dest = entries[0] + dest = roomRoutes[0] resp = requests.request( method=request.method, url='http://' + dest['target'] + request.script_root + request.full_path, diff --git a/lights/README.md b/web/lights/README.md similarity index 100% rename from lights/README.md rename to web/lights/README.md diff --git a/lights/discover.py b/web/lights/discover.py similarity index 100% rename from lights/discover.py rename to web/lights/discover.py diff --git a/lights/protocol.py b/web/lights/protocol.py similarity index 100% rename from lights/protocol.py rename to web/lights/protocol.py diff --git a/lights/smartbulb.py b/web/lights/smartbulb.py similarity index 100% rename from lights/smartbulb.py rename to web/lights/smartbulb.py diff --git a/lights/test.py b/web/lights/test.py similarity index 100% rename from lights/test.py rename to web/lights/test.py diff --git a/web/peripherals.py b/web/peripherals.py new file mode 100644 index 0000000..c14e9fd --- /dev/null +++ b/web/peripherals.py @@ -0,0 +1,13 @@ +from lights.smartbulb import SmartBulb +import postgresLibrary as pL + +def handlePeripheral(peripheral, body, routes): + if len(routes) == 0: + return "No Route to Host." + elif peripheral['type'] == 'lightDimmable': + bulb = SmartBulb(routes[0]['target']) + if body['power'] == False: + bulb.state('OFF') + else: + bulb.state('ON') + bulb.brightness(int(body['state'])) \ No newline at end of file diff --git a/web/postgresLibrary.py b/web/postgresLibrary.py index 06ea678..c4dfd78 100644 --- a/web/postgresLibrary.py +++ b/web/postgresLibrary.py @@ -485,3 +485,26 @@ def routingEntries ( room_uuid, peripheral_uuid ): AND enabled ORDER BY metric ASC """, (room_uuid, peripheral_uuid)) + +# Get routing table entries for a room / peripheral ID. +def roomRoutingEntries ( room_uuid ): + return getAllParams(""" + SELECT * + FROM routing + WHERE + room_uuid = %s + AND enabled + ORDER BY metric ASC + """, (room_uuid)) + +# Get routing table entries for a room / peripheral ID. +def peripheralRoutingEntries ( peripheral_uuid ): + return getAllParams(""" + SELECT * + FROM routing + WHERE + peripheral_uuid = %s + AND enabled + ORDER BY metric ASC + """, (peripheral_uuid)) + From 2c4026ee783c6a95426b62e6fe5f55c2716fe807 Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Sun, 22 Apr 2018 17:53:59 -0400 Subject: [PATCH 03/22] Add lights routing --- web/Dockerfile | 3 ++- web/index.py | 10 ++++++++-- web/lights/__init__.py | 0 web/lights/discover.py | 2 +- web/lights/smartbulb.py | 2 +- web/lights/test.py | 4 ++-- web/peripherals.py | 17 +++++++++++++---- web/postgresLibrary.py | 8 ++++++-- 8 files changed, 33 insertions(+), 13 deletions(-) create mode 100644 web/lights/__init__.py diff --git a/web/Dockerfile b/web/Dockerfile index f85b328..97e2a40 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -17,7 +17,8 @@ RUN pip install -r /src/requirements.txt COPY index.py /src/index.py COPY static/ManagementApp.html /src/static/ManagementApp.html COPY postgresLibrary.py /src/postgresLibrary.py -COPY lights/* /src/lights/* +COPY peripherals.py /src/peripherals.py +COPY lights/ /src/lights/ # Add entrypoint COPY docker-entrypoint.sh /usr/local/bin/ diff --git a/web/index.py b/web/index.py index 301eb9a..eab819d 100644 --- a/web/index.py +++ b/web/index.py @@ -37,6 +37,12 @@ api = Api(app) # Room Info #################################### +@app.route('/api/peripheral/', methods=['GET']) +def getPeripheral(uuid): + per = pL.getPeripheralbyUUID(uuid) + + return jsonify(per) + @app.route('/api/peripheral/', methods=['POST']) def setPeripheralID(uuid): body = request.get_json() @@ -52,7 +58,7 @@ def setPeripheralID(uuid): # If there's a specific peripheral IP in a peripheral route, it will be ignored if there is a room route. if len(roomRoutes) == 0: # We're doing a peripheral specific handling. - peripherals.handlePeripheral(per, body, peripheralRoutes) + return peripherals.handlePeripheral(per, body, peripheralRoutes) else: dest = roomRoutes[0] resp = requests.request( @@ -79,7 +85,7 @@ def getGuestRooms(): for room in rooms: print(room) room['pheripherals'] = pL.getPeripheralsbyRoom(room['uuid']) - return rooms + return jsonify(rooms) @app.route('/api/routes//entries', methods=['GET','POST']) def getRoutes(uuid): diff --git a/web/lights/__init__.py b/web/lights/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/web/lights/discover.py b/web/lights/discover.py index 6ee6205..99d64f8 100644 --- a/web/lights/discover.py +++ b/web/lights/discover.py @@ -1,6 +1,6 @@ import socket, json from typing import Dict, Type, List -from protocol import TPLinkSmartHomeProtocol +from .protocol import TPLinkSmartHomeProtocol class Discover: diff --git a/web/lights/smartbulb.py b/web/lights/smartbulb.py index 7e1d6c2..fdf5c00 100644 --- a/web/lights/smartbulb.py +++ b/web/lights/smartbulb.py @@ -1,4 +1,4 @@ -from protocol import TPLinkSmartHomeProtocol +from .protocol import TPLinkSmartHomeProtocol from typing import Optional, Dict diff --git a/web/lights/test.py b/web/lights/test.py index ae41e26..b066e6d 100644 --- a/web/lights/test.py +++ b/web/lights/test.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 import time -from discover import Discover -from smartbulb import SmartBulb +from .discover import Discover +from .smartbulb import SmartBulb """ This is a test file to show how things work. diff --git a/web/peripherals.py b/web/peripherals.py index c14e9fd..9b01780 100644 --- a/web/peripherals.py +++ b/web/peripherals.py @@ -2,12 +2,21 @@ from lights.smartbulb import SmartBulb import postgresLibrary as pL def handlePeripheral(peripheral, body, routes): + print(peripheral, body, routes) if len(routes) == 0: return "No Route to Host." elif peripheral['type'] == 'lightDimmable': bulb = SmartBulb(routes[0]['target']) - if body['power'] == False: - bulb.state('OFF') - else: + power = body['power'] + brightness = int(body['state']) + + if power: bulb.state('ON') - bulb.brightness(int(body['state'])) \ No newline at end of file + bulb.brightness(brightness) + else: + bulb.state('OFF') + + pL.updatePeripheralStateAndPower(brightness, power, peripheral['uuid']) + return "OK" + else: + return "Unknown Type." \ No newline at end of file diff --git a/web/postgresLibrary.py b/web/postgresLibrary.py index c4dfd78..81d38d6 100644 --- a/web/postgresLibrary.py +++ b/web/postgresLibrary.py @@ -260,6 +260,10 @@ def updatePeripheralActive( active, periUUID): query = "UPDATE peripherals SET active = (%s) WHERE uuid = (%s);" return updateItem( query, (active, periUUID)) +def updatePeripheralStateAndPower( state, power, periUUID): + query = "UPDATE peripherals SET state = (%s), power = (%s), last_update = CURRENT_TIMESTAMP WHERE uuid = (%s);" + return updateItem( query, (state, power, periUUID)) + #TV Channel Select Functions def getChannels(): query = "SELECT * from tv_channels;" @@ -495,7 +499,7 @@ def roomRoutingEntries ( room_uuid ): room_uuid = %s AND enabled ORDER BY metric ASC - """, (room_uuid)) + """, (room_uuid,)) # Get routing table entries for a room / peripheral ID. def peripheralRoutingEntries ( peripheral_uuid ): @@ -506,5 +510,5 @@ def peripheralRoutingEntries ( peripheral_uuid ): peripheral_uuid = %s AND enabled ORDER BY metric ASC - """, (peripheral_uuid)) + """, (peripheral_uuid,)) From ee997ba21306b53384f519a7a2da53725c3bf4a7 Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Sun, 22 Apr 2018 17:57:32 -0400 Subject: [PATCH 04/22] Main guest interface endpoint --- web/index.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/index.py b/web/index.py index eab819d..e891aa4 100644 --- a/web/index.py +++ b/web/index.py @@ -85,7 +85,10 @@ def getGuestRooms(): for room in rooms: print(room) room['pheripherals'] = pL.getPeripheralsbyRoom(room['uuid']) - return jsonify(rooms) + return jsonify({ + 'rooms': rooms, + 'hotel_name': 'Hotel' + }) @app.route('/api/routes//entries', methods=['GET','POST']) def getRoutes(uuid): From a54c1b6c15a33ae3c141b502f0f1eda0c91258f6 Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Sun, 22 Apr 2018 18:12:12 -0400 Subject: [PATCH 05/22] Fix spelling --- web/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/index.py b/web/index.py index e891aa4..82684b7 100644 --- a/web/index.py +++ b/web/index.py @@ -84,7 +84,7 @@ def getGuestRooms(): rooms = pL.getRooms() for room in rooms: print(room) - room['pheripherals'] = pL.getPeripheralsbyRoom(room['uuid']) + room['peripherals'] = pL.getPeripheralsbyRoom(room['uuid']) return jsonify({ 'rooms': rooms, 'hotel_name': 'Hotel' From 2793105e6362ec7b04a37ff740a9be87f314a5ca Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Sun, 22 Apr 2018 18:39:27 -0400 Subject: [PATCH 06/22] fix schema --- postgres/create_schema.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgres/create_schema.sql b/postgres/create_schema.sql index 0515607..6b53d76 100644 --- a/postgres/create_schema.sql +++ b/postgres/create_schema.sql @@ -21,7 +21,7 @@ CREATE TABLE guests ( city text, state text, zip text, - room_uuid uuid REFERENCES rooms(uuid). + room_uuid uuid REFERENCES rooms(uuid) ); -- Sessions table to keep track of tokens for authentication. From e899c5654042b9338d27a650013c3f4e5ff2fce5 Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 12:07:49 -0400 Subject: [PATCH 07/22] Add thermostat lib --- web/thermostat/HT16K33.py | 101 ++++++++++++++++ web/thermostat/SevenSegment.py | 204 +++++++++++++++++++++++++++++++++ web/thermostat/__init__.py | 0 web/thermostat/server.py | 52 +++++++++ 4 files changed, 357 insertions(+) create mode 100644 web/thermostat/HT16K33.py create mode 100644 web/thermostat/SevenSegment.py create mode 100644 web/thermostat/__init__.py create mode 100644 web/thermostat/server.py diff --git a/web/thermostat/HT16K33.py b/web/thermostat/HT16K33.py new file mode 100644 index 0000000..7abdc01 --- /dev/null +++ b/web/thermostat/HT16K33.py @@ -0,0 +1,101 @@ +# Copyright (c) 2014 Adafruit Industries +# Author: Tony DiCola +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +from __future__ import division + + +# Constants +DEFAULT_ADDRESS = 0x70 +HT16K33_BLINK_CMD = 0x80 +HT16K33_BLINK_DISPLAYON = 0x01 +HT16K33_BLINK_OFF = 0x00 +HT16K33_BLINK_2HZ = 0x02 +HT16K33_BLINK_1HZ = 0x04 +HT16K33_BLINK_HALFHZ = 0x06 +HT16K33_SYSTEM_SETUP = 0x20 +HT16K33_OSCILLATOR = 0x01 +HT16K33_CMD_BRIGHTNESS = 0xE0 + + +class HT16K33(object): + """Driver for interfacing with a Holtek HT16K33 16x8 LED driver.""" + + def __init__(self, address=DEFAULT_ADDRESS, i2c=None, **kwargs): + """Create an HT16K33 driver for device on the specified I2C address + (defaults to 0x70) and I2C bus (defaults to platform specific bus). + """ + if i2c is None: + import Adafruit_GPIO.I2C as I2C + i2c = I2C + self._device = i2c.get_i2c_device(address, **kwargs) + self.buffer = bytearray([0]*16) + + def begin(self): + """Initialize driver with LEDs enabled and all turned off.""" + # Turn on the oscillator. + self._device.writeList(HT16K33_SYSTEM_SETUP | HT16K33_OSCILLATOR, []) + # Turn display on with no blinking. + self.set_blink(HT16K33_BLINK_OFF) + # Set display to full brightness. + self.set_brightness(15) + + def set_blink(self, frequency): + """Blink display at specified frequency. Note that frequency must be a + value allowed by the HT16K33, specifically one of: HT16K33_BLINK_OFF, + HT16K33_BLINK_2HZ, HT16K33_BLINK_1HZ, or HT16K33_BLINK_HALFHZ. + """ + if frequency not in [HT16K33_BLINK_OFF, HT16K33_BLINK_2HZ, + HT16K33_BLINK_1HZ, HT16K33_BLINK_HALFHZ]: + raise ValueError('Frequency must be one of HT16K33_BLINK_OFF, HT16K33_BLINK_2HZ, HT16K33_BLINK_1HZ, or HT16K33_BLINK_HALFHZ.') + self._device.writeList(HT16K33_BLINK_CMD | HT16K33_BLINK_DISPLAYON | frequency, []) + + def set_brightness(self, brightness): + """Set brightness of entire display to specified value (16 levels, from + 0 to 15). + """ + if brightness < 0 or brightness > 15: + raise ValueError('Brightness must be a value of 0 to 15.') + self._device.writeList(HT16K33_CMD_BRIGHTNESS | brightness, []) + + def set_led(self, led, value): + """Sets specified LED (value of 0 to 127) to the specified value, 0/False + for off and 1 (or any True/non-zero value) for on. + """ + if led < 0 or led > 127: + raise ValueError('LED must be value of 0 to 127.') + # Calculate position in byte buffer and bit offset of desired LED. + pos = led // 8 + offset = led % 8 + if not value: + # Turn off the specified LED (set bit to zero). + self.buffer[pos] &= ~(1 << offset) + else: + # Turn on the speciried LED (set bit to one). + self.buffer[pos] |= (1 << offset) + + def write_display(self): + """Write display buffer to display hardware.""" + for i, value in enumerate(self.buffer): + self._device.write8(i, value) + + def clear(self): + """Clear contents of display buffer.""" + for i, value in enumerate(self.buffer): + self.buffer[i] = 0 diff --git a/web/thermostat/SevenSegment.py b/web/thermostat/SevenSegment.py new file mode 100644 index 0000000..ba725b7 --- /dev/null +++ b/web/thermostat/SevenSegment.py @@ -0,0 +1,204 @@ +# Copyright (c) 2014 Adafruit Industries +# Author: Tony DiCola +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +from . import HT16K33 + + +# Digit value to bitmask mapping: +DIGIT_VALUES = { + ' ': 0x00, + '-': 0x40, + '0': 0x3F, + '1': 0x06, + '2': 0x5B, + '3': 0x4F, + '4': 0x66, + '5': 0x6D, + '6': 0x7D, + '7': 0x07, + '8': 0x7F, + '9': 0x6F, + 'A': 0x77, + 'B': 0x7C, + 'C': 0x39, + 'D': 0x5E, + 'E': 0x79, + 'F': 0x71 +} + +IDIGIT_VALUES = { + ' ': 0x00, + '-': 0x40, + '0': 0x3F, + '1': 0x30, + '2': 0x5B, + '3': 0x79, + '4': 0x74, + '5': 0x6D, + '6': 0x6F, + '7': 0x38, + '8': 0x7F, + '9': 0x7D, + 'A': 0x7E, + 'B': 0x67, + 'C': 0x0F, + 'D': 0x73, + 'E': 0x4F, + 'F': 0x4E +} + + + +class SevenSegment(HT16K33.HT16K33): + """Seven segment LED backpack display.""" + + def __init__(self, invert=False, **kwargs): + """Initialize display. All arguments will be passed to the HT16K33 class + initializer, including optional I2C address and bus number parameters. + """ + super(SevenSegment, self).__init__(**kwargs) + self.invert = invert + + def set_invert(self, _invert): + """Set whether the display is upside-down or not. + """ + self.invert = _invert + + def set_digit_raw(self, pos, bitmask): + """Set digit at position to raw bitmask value. Position should be a value + of 0 to 3 with 0 being the left most digit on the display.""" + if pos < 0 or pos > 3: + # Ignore out of bounds digits. + return + # Jump past the colon at position 2 by adding a conditional offset. + offset = 0 if pos < 2 else 1 + + # Calculate the correct position depending on orientation + if self.invert: + pos = 4-(pos+offset) + else: + pos = pos+offset + + # Set the digit bitmask value at the appropriate position. + self.buffer[pos*2] = bitmask & 0xFF + + def set_decimal(self, pos, decimal): + """Turn decimal point on or off at provided position. Position should be + a value 0 to 3 with 0 being the left most digit on the display. Decimal + should be True to turn on the decimal point and False to turn it off. + """ + if pos < 0 or pos > 3: + # Ignore out of bounds digits. + return + # Jump past the colon at position 2 by adding a conditional offset. + offset = 0 if pos < 2 else 1 + + # Calculate the correct position depending on orientation + if self.invert: + pos = 4-(pos+offset) + else: + pos = pos+offset + + # Set bit 7 (decimal point) based on provided value. + if decimal: + self.buffer[pos*2] |= (1 << 7) + else: + self.buffer[pos*2] &= ~(1 << 7) + + def set_digit(self, pos, digit, decimal=False): + """Set digit at position to provided value. Position should be a value + of 0 to 3 with 0 being the left most digit on the display. Digit should + be a number 0-9, character A-F, space (all LEDs off), or dash (-). + """ + if self.invert: + self.set_digit_raw(pos, IDIGIT_VALUES.get(str(digit).upper(), 0x00)) + else: + self.set_digit_raw(pos, DIGIT_VALUES.get(str(digit).upper(), 0x00)) + + if decimal: + self.set_decimal(pos, True) + + def set_colon(self, show_colon): + """Turn the colon on with show colon True, or off with show colon False.""" + if show_colon: + self.buffer[4] |= 0x02 + else: + self.buffer[4] &= (~0x02) & 0xFF + + def set_left_colon(self, show_colon): + """Turn the left colon on with show color True, or off with show colon + False. Only the large 1.2" 7-segment display has a left colon. + """ + if show_colon: + self.buffer[4] |= 0x04 + self.buffer[4] |= 0x08 + else: + self.buffer[4] &= (~0x04) & 0xFF + self.buffer[4] &= (~0x08) & 0xFF + + def set_fixed_decimal(self, show_decimal): + """Turn on/off the single fixed decimal point on the large 1.2" 7-segment + display. Set show_decimal to True to turn on and False to turn off. + Only the large 1.2" 7-segment display has this decimal point (in the + upper right in the normal orientation of the display). + """ + if show_decimal: + self.buffer[4] |= 0x10 + else: + self.buffer[4] &= (~0x10) & 0xFF + + def print_number_str(self, value, justify_right=True): + """Print a 4 character long string of numeric values to the display. + Characters in the string should be any supported character by set_digit, + or a decimal point. Decimal point characters will be associated with + the previous character. + """ + # Calculate length of value without decimals. + length = sum(map(lambda x: 1 if x != '.' else 0, value)) + # Error if value without decimals is longer than 4 characters. + if length > 4: + self.print_number_str('----') + return + # Calculcate starting position of digits based on justification. + pos = (4-length) if justify_right else 0 + # Go through each character and print it on the display. + for i, ch in enumerate(value): + if ch == '.': + # Print decimal points on the previous digit. + self.set_decimal(pos-1, True) + else: + self.set_digit(pos, ch) + pos += 1 + + def print_float(self, value, decimal_digits=2, justify_right=True): + """Print a numeric value to the display. If value is negative + it will be printed with a leading minus sign. Decimal digits is the + desired number of digits after the decimal point. + """ + format_string = '{{0:0.{0}F}}'.format(decimal_digits) + self.print_number_str(format_string.format(value), justify_right) + + def print_hex(self, value, justify_right=True): + """Print a numeric value in hexadecimal. Value should be from 0 to FFFF. + """ + if value < 0 or value > 0xFFFF: + # Ignore out of range values. + return + self.print_number_str('{0:X}'.format(value), justify_right) diff --git a/web/thermostat/__init__.py b/web/thermostat/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/web/thermostat/server.py b/web/thermostat/server.py new file mode 100644 index 0000000..7f7b345 --- /dev/null +++ b/web/thermostat/server.py @@ -0,0 +1,52 @@ +#!/usr/bin/python +from flask import Flask +from flask_restful import Resource, Api +from flask import request, Response +from flask.json import jsonify +from flask import render_template +import requests +import json +import postgresLibrary as pL +import uuid +import peripherals as peripherals + +from datetime import date, datetime +from . import SevenSegment +PORT_NUMBER = 8000 + +app = Flask(__name__) +api = Api(app) + +# class Index(Resource): +# def get(self): +# return app.send_static_file('/src/static/ManagementApp.html') +# api.add_resource(Index, '/') + + +#################################### +# Management App API Requirements +#################################### + +#################################### +# Room Info +#################################### + +segment = SevenSegment.SevenSegment(address=0x70) +segment.begin() +segment.clear() + + +@app.route('/api/set-temp', methods=['POST']) +def setTemp(uuid): + body = request.get_json() + + if body == None: + return None + + segment.print_number_str(" {} ".format(int(body['state']))) + segment.write_display() + + return "OK" + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=PORT_NUMBER) From f0eee7df41961ce7b4713f83e65a949a1615ded6 Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 12:09:43 -0400 Subject: [PATCH 08/22] Fix issue --- web/thermostat/server.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/web/thermostat/server.py b/web/thermostat/server.py index 7f7b345..af6bf5a 100644 --- a/web/thermostat/server.py +++ b/web/thermostat/server.py @@ -6,31 +6,15 @@ from flask.json import jsonify from flask import render_template import requests import json -import postgresLibrary as pL import uuid -import peripherals as peripherals - from datetime import date, datetime + from . import SevenSegment PORT_NUMBER = 8000 app = Flask(__name__) api = Api(app) -# class Index(Resource): -# def get(self): -# return app.send_static_file('/src/static/ManagementApp.html') -# api.add_resource(Index, '/') - - -#################################### -# Management App API Requirements -#################################### - -#################################### -# Room Info -#################################### - segment = SevenSegment.SevenSegment(address=0x70) segment.begin() segment.clear() From 622bd00935168e8cfba06d960666bc83b8c49b45 Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 12:12:53 -0400 Subject: [PATCH 09/22] inline deps --- web/thermostat/server.py | 310 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 309 insertions(+), 1 deletion(-) diff --git a/web/thermostat/server.py b/web/thermostat/server.py index af6bf5a..f102b12 100644 --- a/web/thermostat/server.py +++ b/web/thermostat/server.py @@ -9,7 +9,7 @@ import json import uuid from datetime import date, datetime -from . import SevenSegment +# from . import SevenSegment PORT_NUMBER = 8000 app = Flask(__name__) @@ -34,3 +34,311 @@ def setTemp(uuid): if __name__ == '__main__': app.run(host='0.0.0.0', port=PORT_NUMBER) + + +# Copyright (c) 2014 Adafruit Industries +# Author: Tony DiCola +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + + +# Digit value to bitmask mapping: +DIGIT_VALUES = { + ' ': 0x00, + '-': 0x40, + '0': 0x3F, + '1': 0x06, + '2': 0x5B, + '3': 0x4F, + '4': 0x66, + '5': 0x6D, + '6': 0x7D, + '7': 0x07, + '8': 0x7F, + '9': 0x6F, + 'A': 0x77, + 'B': 0x7C, + 'C': 0x39, + 'D': 0x5E, + 'E': 0x79, + 'F': 0x71 +} + +IDIGIT_VALUES = { + ' ': 0x00, + '-': 0x40, + '0': 0x3F, + '1': 0x30, + '2': 0x5B, + '3': 0x79, + '4': 0x74, + '5': 0x6D, + '6': 0x6F, + '7': 0x38, + '8': 0x7F, + '9': 0x7D, + 'A': 0x7E, + 'B': 0x67, + 'C': 0x0F, + 'D': 0x73, + 'E': 0x4F, + 'F': 0x4E +} + + + +class SevenSegment(HT16K33.HT16K33): + """Seven segment LED backpack display.""" + + def __init__(self, invert=False, **kwargs): + """Initialize display. All arguments will be passed to the HT16K33 class + initializer, including optional I2C address and bus number parameters. + """ + super(SevenSegment, self).__init__(**kwargs) + self.invert = invert + + def set_invert(self, _invert): + """Set whether the display is upside-down or not. + """ + self.invert = _invert + + def set_digit_raw(self, pos, bitmask): + """Set digit at position to raw bitmask value. Position should be a value + of 0 to 3 with 0 being the left most digit on the display.""" + if pos < 0 or pos > 3: + # Ignore out of bounds digits. + return + # Jump past the colon at position 2 by adding a conditional offset. + offset = 0 if pos < 2 else 1 + + # Calculate the correct position depending on orientation + if self.invert: + pos = 4-(pos+offset) + else: + pos = pos+offset + + # Set the digit bitmask value at the appropriate position. + self.buffer[pos*2] = bitmask & 0xFF + + def set_decimal(self, pos, decimal): + """Turn decimal point on or off at provided position. Position should be + a value 0 to 3 with 0 being the left most digit on the display. Decimal + should be True to turn on the decimal point and False to turn it off. + """ + if pos < 0 or pos > 3: + # Ignore out of bounds digits. + return + # Jump past the colon at position 2 by adding a conditional offset. + offset = 0 if pos < 2 else 1 + + # Calculate the correct position depending on orientation + if self.invert: + pos = 4-(pos+offset) + else: + pos = pos+offset + + # Set bit 7 (decimal point) based on provided value. + if decimal: + self.buffer[pos*2] |= (1 << 7) + else: + self.buffer[pos*2] &= ~(1 << 7) + + def set_digit(self, pos, digit, decimal=False): + """Set digit at position to provided value. Position should be a value + of 0 to 3 with 0 being the left most digit on the display. Digit should + be a number 0-9, character A-F, space (all LEDs off), or dash (-). + """ + if self.invert: + self.set_digit_raw(pos, IDIGIT_VALUES.get(str(digit).upper(), 0x00)) + else: + self.set_digit_raw(pos, DIGIT_VALUES.get(str(digit).upper(), 0x00)) + + if decimal: + self.set_decimal(pos, True) + + def set_colon(self, show_colon): + """Turn the colon on with show colon True, or off with show colon False.""" + if show_colon: + self.buffer[4] |= 0x02 + else: + self.buffer[4] &= (~0x02) & 0xFF + + def set_left_colon(self, show_colon): + """Turn the left colon on with show color True, or off with show colon + False. Only the large 1.2" 7-segment display has a left colon. + """ + if show_colon: + self.buffer[4] |= 0x04 + self.buffer[4] |= 0x08 + else: + self.buffer[4] &= (~0x04) & 0xFF + self.buffer[4] &= (~0x08) & 0xFF + + def set_fixed_decimal(self, show_decimal): + """Turn on/off the single fixed decimal point on the large 1.2" 7-segment + display. Set show_decimal to True to turn on and False to turn off. + Only the large 1.2" 7-segment display has this decimal point (in the + upper right in the normal orientation of the display). + """ + if show_decimal: + self.buffer[4] |= 0x10 + else: + self.buffer[4] &= (~0x10) & 0xFF + + def print_number_str(self, value, justify_right=True): + """Print a 4 character long string of numeric values to the display. + Characters in the string should be any supported character by set_digit, + or a decimal point. Decimal point characters will be associated with + the previous character. + """ + # Calculate length of value without decimals. + length = sum(map(lambda x: 1 if x != '.' else 0, value)) + # Error if value without decimals is longer than 4 characters. + if length > 4: + self.print_number_str('----') + return + # Calculcate starting position of digits based on justification. + pos = (4-length) if justify_right else 0 + # Go through each character and print it on the display. + for i, ch in enumerate(value): + if ch == '.': + # Print decimal points on the previous digit. + self.set_decimal(pos-1, True) + else: + self.set_digit(pos, ch) + pos += 1 + + def print_float(self, value, decimal_digits=2, justify_right=True): + """Print a numeric value to the display. If value is negative + it will be printed with a leading minus sign. Decimal digits is the + desired number of digits after the decimal point. + """ + format_string = '{{0:0.{0}F}}'.format(decimal_digits) + self.print_number_str(format_string.format(value), justify_right) + + def print_hex(self, value, justify_right=True): + """Print a numeric value in hexadecimal. Value should be from 0 to FFFF. + """ + if value < 0 or value > 0xFFFF: + # Ignore out of range values. + return + self.print_number_str('{0:X}'.format(value), justify_right) + + +# Copyright (c) 2014 Adafruit Industries +# Author: Tony DiCola +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +from __future__ import division + + +# Constants +DEFAULT_ADDRESS = 0x70 +HT16K33_BLINK_CMD = 0x80 +HT16K33_BLINK_DISPLAYON = 0x01 +HT16K33_BLINK_OFF = 0x00 +HT16K33_BLINK_2HZ = 0x02 +HT16K33_BLINK_1HZ = 0x04 +HT16K33_BLINK_HALFHZ = 0x06 +HT16K33_SYSTEM_SETUP = 0x20 +HT16K33_OSCILLATOR = 0x01 +HT16K33_CMD_BRIGHTNESS = 0xE0 + + +class HT16K33(object): + """Driver for interfacing with a Holtek HT16K33 16x8 LED driver.""" + + def __init__(self, address=DEFAULT_ADDRESS, i2c=None, **kwargs): + """Create an HT16K33 driver for device on the specified I2C address + (defaults to 0x70) and I2C bus (defaults to platform specific bus). + """ + if i2c is None: + import Adafruit_GPIO.I2C as I2C + i2c = I2C + self._device = i2c.get_i2c_device(address, **kwargs) + self.buffer = bytearray([0]*16) + + def begin(self): + """Initialize driver with LEDs enabled and all turned off.""" + # Turn on the oscillator. + self._device.writeList(HT16K33_SYSTEM_SETUP | HT16K33_OSCILLATOR, []) + # Turn display on with no blinking. + self.set_blink(HT16K33_BLINK_OFF) + # Set display to full brightness. + self.set_brightness(15) + + def set_blink(self, frequency): + """Blink display at specified frequency. Note that frequency must be a + value allowed by the HT16K33, specifically one of: HT16K33_BLINK_OFF, + HT16K33_BLINK_2HZ, HT16K33_BLINK_1HZ, or HT16K33_BLINK_HALFHZ. + """ + if frequency not in [HT16K33_BLINK_OFF, HT16K33_BLINK_2HZ, + HT16K33_BLINK_1HZ, HT16K33_BLINK_HALFHZ]: + raise ValueError('Frequency must be one of HT16K33_BLINK_OFF, HT16K33_BLINK_2HZ, HT16K33_BLINK_1HZ, or HT16K33_BLINK_HALFHZ.') + self._device.writeList(HT16K33_BLINK_CMD | HT16K33_BLINK_DISPLAYON | frequency, []) + + def set_brightness(self, brightness): + """Set brightness of entire display to specified value (16 levels, from + 0 to 15). + """ + if brightness < 0 or brightness > 15: + raise ValueError('Brightness must be a value of 0 to 15.') + self._device.writeList(HT16K33_CMD_BRIGHTNESS | brightness, []) + + def set_led(self, led, value): + """Sets specified LED (value of 0 to 127) to the specified value, 0/False + for off and 1 (or any True/non-zero value) for on. + """ + if led < 0 or led > 127: + raise ValueError('LED must be value of 0 to 127.') + # Calculate position in byte buffer and bit offset of desired LED. + pos = led // 8 + offset = led % 8 + if not value: + # Turn off the specified LED (set bit to zero). + self.buffer[pos] &= ~(1 << offset) + else: + # Turn on the speciried LED (set bit to one). + self.buffer[pos] |= (1 << offset) + + def write_display(self): + """Write display buffer to display hardware.""" + for i, value in enumerate(self.buffer): + self._device.write8(i, value) + + def clear(self): + """Clear contents of display buffer.""" + for i, value in enumerate(self.buffer): + self.buffer[i] = 0 From 1e21100d1a2ddd87027a8e8adb0792433cd7698d Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 12:14:07 -0400 Subject: [PATCH 10/22] fix --- web/thermostat/server.py | 227 ++++++++++++++++++++------------------- 1 file changed, 114 insertions(+), 113 deletions(-) diff --git a/web/thermostat/server.py b/web/thermostat/server.py index f102b12..926b0d2 100644 --- a/web/thermostat/server.py +++ b/web/thermostat/server.py @@ -8,6 +8,8 @@ import requests import json import uuid from datetime import date, datetime +from __future__ import division + # from . import SevenSegment PORT_NUMBER = 8000 @@ -15,25 +17,107 @@ PORT_NUMBER = 8000 app = Flask(__name__) api = Api(app) -segment = SevenSegment.SevenSegment(address=0x70) -segment.begin() -segment.clear() +# Copyright (c) 2014 Adafruit Industries +# Author: Tony DiCola +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. -@app.route('/api/set-temp', methods=['POST']) -def setTemp(uuid): - body = request.get_json() - - if body == None: - return None +# Constants +DEFAULT_ADDRESS = 0x70 +HT16K33_BLINK_CMD = 0x80 +HT16K33_BLINK_DISPLAYON = 0x01 +HT16K33_BLINK_OFF = 0x00 +HT16K33_BLINK_2HZ = 0x02 +HT16K33_BLINK_1HZ = 0x04 +HT16K33_BLINK_HALFHZ = 0x06 +HT16K33_SYSTEM_SETUP = 0x20 +HT16K33_OSCILLATOR = 0x01 +HT16K33_CMD_BRIGHTNESS = 0xE0 - segment.print_number_str(" {} ".format(int(body['state']))) - segment.write_display() - - return "OK" -if __name__ == '__main__': - app.run(host='0.0.0.0', port=PORT_NUMBER) +class HT16K33(object): + """Driver for interfacing with a Holtek HT16K33 16x8 LED driver.""" + + def __init__(self, address=DEFAULT_ADDRESS, i2c=None, **kwargs): + """Create an HT16K33 driver for device on the specified I2C address + (defaults to 0x70) and I2C bus (defaults to platform specific bus). + """ + if i2c is None: + import Adafruit_GPIO.I2C as I2C + i2c = I2C + self._device = i2c.get_i2c_device(address, **kwargs) + self.buffer = bytearray([0]*16) + + def begin(self): + """Initialize driver with LEDs enabled and all turned off.""" + # Turn on the oscillator. + self._device.writeList(HT16K33_SYSTEM_SETUP | HT16K33_OSCILLATOR, []) + # Turn display on with no blinking. + self.set_blink(HT16K33_BLINK_OFF) + # Set display to full brightness. + self.set_brightness(15) + + def set_blink(self, frequency): + """Blink display at specified frequency. Note that frequency must be a + value allowed by the HT16K33, specifically one of: HT16K33_BLINK_OFF, + HT16K33_BLINK_2HZ, HT16K33_BLINK_1HZ, or HT16K33_BLINK_HALFHZ. + """ + if frequency not in [HT16K33_BLINK_OFF, HT16K33_BLINK_2HZ, + HT16K33_BLINK_1HZ, HT16K33_BLINK_HALFHZ]: + raise ValueError('Frequency must be one of HT16K33_BLINK_OFF, HT16K33_BLINK_2HZ, HT16K33_BLINK_1HZ, or HT16K33_BLINK_HALFHZ.') + self._device.writeList(HT16K33_BLINK_CMD | HT16K33_BLINK_DISPLAYON | frequency, []) + + def set_brightness(self, brightness): + """Set brightness of entire display to specified value (16 levels, from + 0 to 15). + """ + if brightness < 0 or brightness > 15: + raise ValueError('Brightness must be a value of 0 to 15.') + self._device.writeList(HT16K33_CMD_BRIGHTNESS | brightness, []) + + def set_led(self, led, value): + """Sets specified LED (value of 0 to 127) to the specified value, 0/False + for off and 1 (or any True/non-zero value) for on. + """ + if led < 0 or led > 127: + raise ValueError('LED must be value of 0 to 127.') + # Calculate position in byte buffer and bit offset of desired LED. + pos = led // 8 + offset = led % 8 + if not value: + # Turn off the specified LED (set bit to zero). + self.buffer[pos] &= ~(1 << offset) + else: + # Turn on the speciried LED (set bit to one). + self.buffer[pos] |= (1 << offset) + + def write_display(self): + """Write display buffer to display hardware.""" + for i, value in enumerate(self.buffer): + self._device.write8(i, value) + + def clear(self): + """Clear contents of display buffer.""" + for i, value in enumerate(self.buffer): + self.buffer[i] = 0 + # Copyright (c) 2014 Adafruit Industries @@ -241,104 +325,21 @@ class SevenSegment(HT16K33.HT16K33): self.print_number_str('{0:X}'.format(value), justify_right) -# Copyright (c) 2014 Adafruit Industries -# Author: Tony DiCola -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -from __future__ import division - - -# Constants -DEFAULT_ADDRESS = 0x70 -HT16K33_BLINK_CMD = 0x80 -HT16K33_BLINK_DISPLAYON = 0x01 -HT16K33_BLINK_OFF = 0x00 -HT16K33_BLINK_2HZ = 0x02 -HT16K33_BLINK_1HZ = 0x04 -HT16K33_BLINK_HALFHZ = 0x06 -HT16K33_SYSTEM_SETUP = 0x20 -HT16K33_OSCILLATOR = 0x01 -HT16K33_CMD_BRIGHTNESS = 0xE0 - - -class HT16K33(object): - """Driver for interfacing with a Holtek HT16K33 16x8 LED driver.""" - - def __init__(self, address=DEFAULT_ADDRESS, i2c=None, **kwargs): - """Create an HT16K33 driver for device on the specified I2C address - (defaults to 0x70) and I2C bus (defaults to platform specific bus). - """ - if i2c is None: - import Adafruit_GPIO.I2C as I2C - i2c = I2C - self._device = i2c.get_i2c_device(address, **kwargs) - self.buffer = bytearray([0]*16) - - def begin(self): - """Initialize driver with LEDs enabled and all turned off.""" - # Turn on the oscillator. - self._device.writeList(HT16K33_SYSTEM_SETUP | HT16K33_OSCILLATOR, []) - # Turn display on with no blinking. - self.set_blink(HT16K33_BLINK_OFF) - # Set display to full brightness. - self.set_brightness(15) - - def set_blink(self, frequency): - """Blink display at specified frequency. Note that frequency must be a - value allowed by the HT16K33, specifically one of: HT16K33_BLINK_OFF, - HT16K33_BLINK_2HZ, HT16K33_BLINK_1HZ, or HT16K33_BLINK_HALFHZ. - """ - if frequency not in [HT16K33_BLINK_OFF, HT16K33_BLINK_2HZ, - HT16K33_BLINK_1HZ, HT16K33_BLINK_HALFHZ]: - raise ValueError('Frequency must be one of HT16K33_BLINK_OFF, HT16K33_BLINK_2HZ, HT16K33_BLINK_1HZ, or HT16K33_BLINK_HALFHZ.') - self._device.writeList(HT16K33_BLINK_CMD | HT16K33_BLINK_DISPLAYON | frequency, []) - - def set_brightness(self, brightness): - """Set brightness of entire display to specified value (16 levels, from - 0 to 15). - """ - if brightness < 0 or brightness > 15: - raise ValueError('Brightness must be a value of 0 to 15.') - self._device.writeList(HT16K33_CMD_BRIGHTNESS | brightness, []) +segment = SevenSegment.SevenSegment(address=0x70) +segment.begin() +segment.clear() - def set_led(self, led, value): - """Sets specified LED (value of 0 to 127) to the specified value, 0/False - for off and 1 (or any True/non-zero value) for on. - """ - if led < 0 or led > 127: - raise ValueError('LED must be value of 0 to 127.') - # Calculate position in byte buffer and bit offset of desired LED. - pos = led // 8 - offset = led % 8 - if not value: - # Turn off the specified LED (set bit to zero). - self.buffer[pos] &= ~(1 << offset) - else: - # Turn on the speciried LED (set bit to one). - self.buffer[pos] |= (1 << offset) +@app.route('/api/set-temp', methods=['POST']) +def setTemp(uuid): + body = request.get_json() + + if body == None: + return None - def write_display(self): - """Write display buffer to display hardware.""" - for i, value in enumerate(self.buffer): - self._device.write8(i, value) + segment.print_number_str(" {} ".format(int(body['state']))) + segment.write_display() + + return "OK" - def clear(self): - """Clear contents of display buffer.""" - for i, value in enumerate(self.buffer): - self.buffer[i] = 0 +if __name__ == '__main__': + app.run(host='0.0.0.0', port=PORT_NUMBER) From 55e8cf106bf779e414a661d38d2173ec3a4b3400 Mon Sep 17 00:00:00 2001 From: chs13013 Date: Tue, 24 Apr 2018 16:17:48 +0000 Subject: [PATCH 11/22] fixes --- web/thermostat/server.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/web/thermostat/server.py b/web/thermostat/server.py index 926b0d2..40ab78f 100644 --- a/web/thermostat/server.py +++ b/web/thermostat/server.py @@ -1,4 +1,5 @@ #!/usr/bin/python +from __future__ import division from flask import Flask from flask_restful import Resource, Api from flask import request, Response @@ -8,7 +9,7 @@ import requests import json import uuid from datetime import date, datetime -from __future__ import division + # from . import SevenSegment @@ -187,7 +188,7 @@ IDIGIT_VALUES = { -class SevenSegment(HT16K33.HT16K33): +class SevenSegment(HT16K33): """Seven segment LED backpack display.""" def __init__(self, invert=False, **kwargs): @@ -325,12 +326,12 @@ class SevenSegment(HT16K33.HT16K33): self.print_number_str('{0:X}'.format(value), justify_right) -segment = SevenSegment.SevenSegment(address=0x70) +segment = SevenSegment(address=0x70) segment.begin() segment.clear() @app.route('/api/set-temp', methods=['POST']) -def setTemp(uuid): +def setTemp(): body = request.get_json() if body == None: From dd5d58f7c862ab55b36e3cde5c8e8a0c315158d2 Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 16:45:23 -0400 Subject: [PATCH 12/22] thermostat routing --- web/peripherals.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/web/peripherals.py b/web/peripherals.py index 9b01780..83b00c5 100644 --- a/web/peripherals.py +++ b/web/peripherals.py @@ -1,10 +1,30 @@ from lights.smartbulb import SmartBulb import postgresLibrary as pL +import requests +from flask.json import jsonify +from flask import request, Response def handlePeripheral(peripheral, body, routes): print(peripheral, body, routes) if len(routes) == 0: return "No Route to Host." + elif peripheral['type'] == 'thermostat': + dest = routes[0] + url = 'http://' + dest['target'] + '/api/set-temp' + resp = requests.request( + method='POST', + url=url, + headers= { + 'Content-Type': 'application/json' + }, + data='{ "state": ' + int(body['state']) + '}', + timeout=0.5, + allow_redirects=False + ) + + response = Response(resp.content, resp.status_code, {}) + return response + elif peripheral['type'] == 'lightDimmable': bulb = SmartBulb(routes[0]['target']) power = body['power'] From 5fde8e5c4d23e05e5b54ced1e6e2669948b0f85f Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 16:55:28 -0400 Subject: [PATCH 13/22] fix error --- web/peripherals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/peripherals.py b/web/peripherals.py index 83b00c5..d9041d5 100644 --- a/web/peripherals.py +++ b/web/peripherals.py @@ -17,7 +17,7 @@ def handlePeripheral(peripheral, body, routes): headers= { 'Content-Type': 'application/json' }, - data='{ "state": ' + int(body['state']) + '}', + data='{ "state": ' + str(int(body['state'])) + '}', timeout=0.5, allow_redirects=False ) From 50066dbf43be711aabefb2369f694c3a369b8fbc Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 17:01:04 -0400 Subject: [PATCH 14/22] Fix fail --- web/peripherals.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/peripherals.py b/web/peripherals.py index d9041d5..eecf81f 100644 --- a/web/peripherals.py +++ b/web/peripherals.py @@ -11,6 +11,7 @@ def handlePeripheral(peripheral, body, routes): elif peripheral['type'] == 'thermostat': dest = routes[0] url = 'http://' + dest['target'] + '/api/set-temp' + print(url) resp = requests.request( method='POST', url=url, @@ -18,6 +19,7 @@ def handlePeripheral(peripheral, body, routes): 'Content-Type': 'application/json' }, data='{ "state": ' + str(int(body['state'])) + '}', + cookies={}, timeout=0.5, allow_redirects=False ) From deea8dabcb3d53719745eaf355db675bc4b9a211 Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 17:05:18 -0400 Subject: [PATCH 15/22] Add off string --- web/thermostat/server.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/web/thermostat/server.py b/web/thermostat/server.py index 40ab78f..51caf29 100644 --- a/web/thermostat/server.py +++ b/web/thermostat/server.py @@ -337,8 +337,13 @@ def setTemp(): if body == None: return None - segment.print_number_str(" {} ".format(int(body['state']))) - segment.write_display() + if body['power']: + segment.print_number_str(" {} ".format(int(body['state']))) + segment.write_display() + else: + segment.print_number_str("0FF ") + segment.write_display() + return "OK" From 804d1e6c812d380c564d83b19689bc5098fa11b4 Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 17:12:25 -0400 Subject: [PATCH 16/22] Send power param --- web/peripherals.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web/peripherals.py b/web/peripherals.py index eecf81f..001481f 100644 --- a/web/peripherals.py +++ b/web/peripherals.py @@ -12,13 +12,18 @@ def handlePeripheral(peripheral, body, routes): dest = routes[0] url = 'http://' + dest['target'] + '/api/set-temp' print(url) + + power = "false" + if body['power']: + power = "true" + resp = requests.request( method='POST', url=url, headers= { 'Content-Type': 'application/json' }, - data='{ "state": ' + str(int(body['state'])) + '}', + data='{ "state": ' + str(int(body['state'])) + ', "power": ' + power + ' }' , cookies={}, timeout=0.5, allow_redirects=False From 9f770a4d405bc2a67084b91229bbc29bc8289251 Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 17:19:35 -0400 Subject: [PATCH 17/22] Add degf --- web/thermostat/server.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/thermostat/server.py b/web/thermostat/server.py index 51caf29..59db6c4 100644 --- a/web/thermostat/server.py +++ b/web/thermostat/server.py @@ -162,7 +162,8 @@ DIGIT_VALUES = { 'C': 0x39, 'D': 0x5E, 'E': 0x79, - 'F': 0x71 + 'F': 0x71, + 'd': 0b1111000 } IDIGIT_VALUES = { @@ -338,7 +339,7 @@ def setTemp(): return None if body['power']: - segment.print_number_str(" {} ".format(int(body['state']))) + segment.print_number_str("{}dF".format(int(body['state']))) segment.write_display() else: segment.print_number_str("0FF ") From 593a86e361135ddb43760872bea1d950a5456218 Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 17:21:24 -0400 Subject: [PATCH 18/22] change placeholder --- web/thermostat/server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/thermostat/server.py b/web/thermostat/server.py index 59db6c4..3bf36c1 100644 --- a/web/thermostat/server.py +++ b/web/thermostat/server.py @@ -163,7 +163,7 @@ DIGIT_VALUES = { 'D': 0x5E, 'E': 0x79, 'F': 0x71, - 'd': 0b1111000 + 'x': 0b1111000 } IDIGIT_VALUES = { @@ -339,7 +339,7 @@ def setTemp(): return None if body['power']: - segment.print_number_str("{}dF".format(int(body['state']))) + segment.print_number_str("{}xF".format(int(body['state']))) segment.write_display() else: segment.print_number_str("0FF ") From 9e9769febb678c51299f3d8c8aafb766249e0e50 Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 17:23:50 -0400 Subject: [PATCH 19/22] add things --- web/thermostat/server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/thermostat/server.py b/web/thermostat/server.py index 3bf36c1..d9d7611 100644 --- a/web/thermostat/server.py +++ b/web/thermostat/server.py @@ -162,8 +162,7 @@ DIGIT_VALUES = { 'C': 0x39, 'D': 0x5E, 'E': 0x79, - 'F': 0x71, - 'x': 0b1111000 + 'F': 0x71 } IDIGIT_VALUES = { @@ -340,6 +339,7 @@ def setTemp(): if body['power']: segment.print_number_str("{}xF".format(int(body['state']))) + segment.set_digit_raw(2, 0xF0) segment.write_display() else: segment.print_number_str("0FF ") From d7d1b7f040c34e797484905cbb3a8f3a3ce9a4bb Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 17:30:26 -0400 Subject: [PATCH 20/22] things --- web/thermostat/server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/thermostat/server.py b/web/thermostat/server.py index d9d7611..96e5ba5 100644 --- a/web/thermostat/server.py +++ b/web/thermostat/server.py @@ -338,8 +338,8 @@ def setTemp(): return None if body['power']: - segment.print_number_str("{}xF".format(int(body['state']))) - segment.set_digit_raw(2, 0xF0) + segment.print_number_str("{} F".format(int(body['state']))) + segment.set_digit_raw(2, 0xF8) segment.write_display() else: segment.print_number_str("0FF ") From 754b4c70c72ee5bc2c0cc61eb084bc2041162247 Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 19:39:31 -0400 Subject: [PATCH 21/22] Add state update, degrees 7 segment --- web/peripherals.py | 2 ++ web/thermostat/server.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/web/peripherals.py b/web/peripherals.py index 001481f..a93df49 100644 --- a/web/peripherals.py +++ b/web/peripherals.py @@ -17,6 +17,8 @@ def handlePeripheral(peripheral, body, routes): if body['power']: power = "true" + pL.updatePeripheralStateAndPower(int(body['state']), body['power'], peripheral['uuid']) + resp = requests.request( method='POST', url=url, diff --git a/web/thermostat/server.py b/web/thermostat/server.py index 96e5ba5..a5dffba 100644 --- a/web/thermostat/server.py +++ b/web/thermostat/server.py @@ -339,7 +339,7 @@ def setTemp(): if body['power']: segment.print_number_str("{} F".format(int(body['state']))) - segment.set_digit_raw(2, 0xF8) + segment.set_digit_raw(2, 0x63) segment.write_display() else: segment.print_number_str("0FF ") From 8dcd9c7af46e24a6c11a79bd3a35e9977966d0e6 Mon Sep 17 00:00:00 2001 From: Rich Infante Date: Tue, 24 Apr 2018 19:41:51 -0400 Subject: [PATCH 22/22] Add order for peripherals --- web/postgresLibrary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/postgresLibrary.py b/web/postgresLibrary.py index 81d38d6..f4f5010 100644 --- a/web/postgresLibrary.py +++ b/web/postgresLibrary.py @@ -230,7 +230,7 @@ def getPeripheralbyUUID( periUUID ): def getPeripheralsbyRoom( roomUUID ): """returns information by room_uuid for all peripherals in room""" - query = "SELECT * from peripherals WHERE room_uuid = (%s);" + query = "SELECT * from peripherals WHERE room_uuid = (%s) ORDER BY name DESC;" peripherals = getItemsbyCol(query, roomUUID) return peripherals