From 317fdb93ed6ab155b439abf8d63b13fefb5421f9 Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 1 Oct 2016 17:01:37 +0200 Subject: [PATCH 01/18] picture client side ready --- controller/README.md | 10 +++++++++ controller/config/copy__init__.py.example | 5 ++++- controller/drivers/camera/__init__.py | 27 +++++++++++++++++++++++ controller/sensors.py | 8 ++++++- controller/state/changer.py | 9 ++++++++ 5 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 controller/drivers/camera/__init__.py diff --git a/controller/README.md b/controller/README.md index 78dbd22..14a7a80 100644 --- a/controller/README.md +++ b/controller/README.md @@ -19,3 +19,13 @@ crontab -e -u root python /home/pi/projects/tfm/controller/lockdown.py python /home/pi/projects/tfm/controller/dweet.py ``` + +5. create directory +``` +mkdir -p /mnt/zoblakdata +``` + +6. add following line to /etc/fstab for ramdisk +``` +tmpfs /mnt/zoblakdata tmpfs nodev,nosuid,size=100M 0 0 +``` diff --git a/controller/config/copy__init__.py.example b/controller/config/copy__init__.py.example index 52ec8d4..4708f59 100644 --- a/controller/config/copy__init__.py.example +++ b/controller/config/copy__init__.py.example @@ -14,4 +14,7 @@ START_PUMPING_AT = 'TANKFULL' # start pumping when this level = 0 STOP_PUMPING_AT = 'TANKFULL' # stop pumping when this level = 1 API_BASE_URL = 'http://agrar.zoblak.com/api/v1.0' CONTROLLER_ID = '120' # every controller must have a different one -STATE_FILE = '/var/run/controller_state' +STATE_FILE = '/mnt/zoblakdata/controller_state' +PICTURE_TRANSFER_FILE='/mnt/zoblakdata/picture_transfer_ready.jpg' +PICTURE_INPUT_FILE='/mnt/zoblakdata/picture.jpg' # must match file in PICTURE_COMMAND +PICTURE_COMMAND='avconv -i rtsp://192.168.1.10:554//user=admin_password=_channel=1_stream=0.sdp -f image2 -vframes 1 /mnt/zoblakdata/picture.jpg' # filename must match PICTURE_FILE path diff --git a/controller/drivers/camera/__init__.py b/controller/drivers/camera/__init__.py new file mode 100644 index 0000000..c76bc05 --- /dev/null +++ b/controller/drivers/camera/__init__.py @@ -0,0 +1,27 @@ +import config +import os +import base64; +from shutil import copyfile + +def capture_picture(): + os.system(config.PICTURE_COMMAND) + +def get_transfer_picture_base64(): + try: + with open(config.PICTURE_TRANSFER_FILE, "rb") as image_file: + return base64.b64encode(image_file.read()) + except: + print("Unexpected error:", sys.exc_info()[0]) + return None + +def remove_transfer_picture(): + try: + os.remove(config.PICTURE_TRANSFER_FILE) + except: + print("Error removing: ", config.PICTURE_TRANSFER_FILE ) + +def make_transfer_picture(): + try: + copyfile(config.PICTURE_INPUT_FILE, config.PICTURE_TRANSFER_FILE) + except: + print("Error copying: ", config.PICTURE_INPUT_FILE, config.PICTURE_TRANSFER_FILE ) diff --git a/controller/sensors.py b/controller/sensors.py index 9c8ff9d..19b898e 100644 --- a/controller/sensors.py +++ b/controller/sensors.py @@ -4,6 +4,7 @@ import requests import Adafruit_DHT import config import RPi.GPIO as GPIO +import camera # Try to read the state of GPIO_PIN_TANKLEVELx and GPIO_PIN_TANKFULL GPIO.setmode(GPIO.BCM) @@ -42,6 +43,10 @@ stopPumpingAt = config.STOP_PUMPING_AT # to 15 times to get a sensor reading (waiting 2 seconds between each retry). humidity, temperature = Adafruit_DHT.read_retry(SENSOR_TYPE, config.GPIO_PIN_DHT) +picture_base64 = camera.get_transfer_picture_base64() +if picture_base64 is not None: + camera.remove_transfer_picture() + # Un-comment the line below to convert the temperature to Fahrenheit. # temperature = temperature * 9/5.0 + 32 @@ -51,7 +56,8 @@ humidity, temperature = Adafruit_DHT.read_retry(SENSOR_TYPE, config.GPIO_PIN_DHT # If this happens try again! if tankFull is not None: response = requests.post(config.SENSORDATA_URL, json={"owner": owner, "temperatureValue": temperature, "humidityValue":humidity, "tankLevel0": "1" if tankLevel0 else "0","tankLevel1": "1" if tankLevel1 else "0","tankLevel2": "1" if tankLevel2 else "0","tankLevel3": "1" if tankLevel3 else "0", "tankLevel4": "1" if tankLevel4 else "0", "tankFull": "1" if tankFull else "0", - "startPumpingAt": startPumpingAt,"stopPumpingAt": stopPumpingAt,"controllerId": controller_id + "startPumpingAt": startPumpingAt,"stopPumpingAt": stopPumpingAt,"controllerId": controller_id, + 'picture': picture_base64 }) print 'Temp={0:0.1f}*C'.format(temperature) print 'Humidity={0:0.1f}%'.format(humidity) diff --git a/controller/state/changer.py b/controller/state/changer.py index 3c6f4f0..e377cad 100644 --- a/controller/state/changer.py +++ b/controller/state/changer.py @@ -1,5 +1,6 @@ import RPi.GPIO as GPIO import config +from camera import make_transfer_picture class Changer(object): @@ -45,6 +46,8 @@ class Changer(object): if in_valve_change is not None: in_valve_change() + self.fulfill_picture_request() + return self.local_state def open_in_valve(self): @@ -63,6 +66,12 @@ class Changer(object): GPIO.output(config.GPIO_PIN_OUT_VALVE, GPIO.LOW) self.local_state['out_valve'] = 'closed' + def fulfill_picture_request(self): + if self.remote_state['transfer_picture'] == 'true': + make_transfer_picture() + self.local_state['transfer_picture'] = 'false' + + def validate_states(self): if self.local_state is None or self.remote_state is None: raise ClassNotReadyException("Both local and remote states must be present!") -- 2.47.3 From d469c91e3ed428c3da5c512252d253b5a07184fe Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 14:57:37 +0200 Subject: [PATCH 02/18] picture api ready --- app/common/collections.js | 1 + app/public/images/noImage.png | Bin 0 -> 69223 bytes app/server/api.js | 57 +++++++++++++++++++++++++++++----- 3 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 app/public/images/noImage.png diff --git a/app/common/collections.js b/app/common/collections.js index c45a563..84c7465 100644 --- a/app/common/collections.js +++ b/app/common/collections.js @@ -1,3 +1,4 @@ SensorData = new Mongo.Collection("sensorData"); ControllerState = new Mongo.Collection("controller_states"); +Picture = new Mongo.Collection("pictures"); diff --git a/app/public/images/noImage.png b/app/public/images/noImage.png new file mode 100644 index 0000000000000000000000000000000000000000..757a9bf55028ae97161a4e69fd52f2b29026c033 GIT binary patch literal 69223 zcmeFZi9eKU_y_!qB|%P|SwcPh}t{CcTZ(-nMKoDfh;X|6n z2tsFw{!70JzOjwzCc=MqdTE(^nYcQ5`Pz8cBL`2r+S>0q>|%4q-q_yew7>f+du0Tn z{>RbO+{;`~SJBSZMbrjeBkJek22Uf1vYMZpjh(Z-*B)E@Gmhs~_$JG%`1UxSR^dA) zqbIKCreS~9@lb$=y-9$+sa=4xox*88HPt=Jeu_ZA#oo(ikDtrA^PY-+Dtu(Ritriz zT8wWGd5M>^3f~4(=6Z&EG+aIG_sED!9k3IZkk}(DD=Hx^BP%N*vPV)}LQ+f|{*gT( zA+0DQt0*bA=hq)T*v;d#gQBq}{?}gcHx<6KUS4jBVq(6&zM{TTqOKli#3U3H6i^zH zk_TYL0Z;$)UN(LQ&U^Czrl4u>Y3JeS=H=*meh*5~#@5x_ON9?O-4MaW?LV^TJ%6bQ z6ei|p<0d8{DvpZud!e4*|MyZCm;Y9KdKugQ;rIWQu&1fNo4uH^y{D_UhaDKsfgjz< zO;N+c-p0$-!_?LF+;1%!o^|zd^*rn9wnxK6c8{Q*jh*9p^a~;K6MA}zhtGR@*_^kt zKdh<32Q)+-9ZxH2N#bSk@(K!g326xl2`woFO)X8WgECs02PI@BB*evkKdb3#=j~#D z-s|_Xr~f>A@PB<4bp{tV*jdxw!_mk7G~UD2We-_sMaTc07rFnH-mhm*|L?rW|F6%A z0W)H#X(>$o`z!Dt^y7by3xD~~_3h7tzk7gVpVgytLof{8hcyqH`h6Jhsq#FkojTnA zvnlDTai;0XV{#l>Z4O5sNp979XHR9!a$Jeitt_m6ccHtqfX20LOebZwHl5|$cT%YC zRp4u#?l1d}Je?2fn}4=PeDa|5WZcYvf1W<~oA>X}_ZmAd4NR&q<|wvA83|9+IFG2QrzaEX9I1N>=aF~n?q;-nJZ_<+;E(QbS?`+vlc z|I-4RTHARv)9qduc|BL$_v7a37;Wfc%HVO*u{lqV z=W23m&(@!O4!tAY;<|P_FVAxEE6e>f1gZ8z5wx(`wVKwH;cGI#o2BZ!$>jQUZ(!(3 zk)TfL;U$-ItIL6*K|yQGd3lPm=T^_G(jZ7ZiogW#5Nc6kY;RyWuH|y;4AvCk&OaO= zSDhMH&@dEoZr-`UTn&Q|rcnS#d1TDF=p`DJ%A7WIad=q?-*3VtH%XsIYKjvWafxI* z*;T!?89|~iqrgA5vgs7=d!ncIP%3lgjM72hwx!9HaM_{B@RYtqMaR@*W@afI86KSm z2x6^_<_z;8xzAKiXTBy68O>_A&sNh*ST{5+uf9~w&2zH#Q+@M4NL$<+Jlo^HXEkl) z$#d3)2d_$p*7t>ZH~!C6d7kBmd~cr#nH&y#8}+yerc+{R5+ zL>IOG_9!Jz!#ZQ@(}TYE^y84qL#8Ev$v1fj=Q1G@b7-mHum~;v1EY6;DL;rBBQvC2 zMUwWp!Sj#r4XRTe?g4UnsT|t~Aw_FjaGO!A>s;Kj5Jf#2s6E~6d(%?(2XbUuxaq=Z z-<^4PGqVCFMx5{Q^-aX^U;Y&I3Mf8{@9jOw=~{jS)9L)LgOZuu&Cw!@Jfz2Vbr)gV zZ(X!7c666?K)^mTt(a#xhQ>j$S0Q}54mSYxz#*Xo;3sRHtoCjS&Gbg6GpV= z+s68k1aid0R4xwQk-sz_uuT_ZE;6C3qJKfS`b_bL^n{;RzHFZyrT%Q8FhrkCH{v4h zeOU-ebNZKSkl`VU;6*P zJ%Yf%gaacs`P@>x&5kr`YR2^BHkg^|G10ZMQ}Ti{keR>LoSB>HkFoau;@79`xWj~d zj?wEjrBJQgC!}ZzF-dnFe4PLwE>RD2QnE~Z(R($!D~299*Ku!FHoHuYZJ6e--|aL_ zkl_e937MwYrC|F*1iixlEDa(l?g8KZPo&`#f8Ai=Y^t%-l){{QS(=g^s*@pvFkMMv zxF18wT_HYph?ejcYZ8|Dkdp3EU1?f`(^k09i&Eg~C;(F5+fms_^>Xa!P~W}004G60 ziw)&&B1YTwhLrTgK*OK;Z3RZX&g3OAB>cs+s!6lIT1AgznVlv=P{*3YjSd-^nF(B? zeoFtB=(uKofu;+N;qAfvRES9T-4LF4EwFI=3yjF`z$9Dilwq?%?AH|Sc4}C6|L{0I zrJ>{4C2IK=N`GI7OrALy5U5!YsD~lw-R`>?a9WUHP04u>&Qo(T?O*iw^HC`|Sd&mM zE8*xnlo zid)v?C8~=_m+KAq^-liVlMM-c$Mmcttb^<;PgJ7hBE8+CS!r&}!13|N*@qzH`=F->zo{BC*CgYe-}p)Ph?KWn?SV-S{ay``2r^r5 zw7Oz$_U=kLVUE(tBVwFmM`FtL?7DL80x-zZn7l$SduJo1oP>c(Hbb9D66YW8J3H9|n8t_(D`QJ~b0&EDI~di=>e99s}Xbd_ zMf;M;+};FKLXLPOJ17~)ZJ~YimopjM@Psuz*+pZf^0bJxnXQ1+_#qWZ)+rlGyl}G_ zsv$dvb<94;$NY0(%yLSp`ZJ+2b=;xP91YXGUDCN}+H*6+ygZ0q$Wo2J(}Ku9$y7+I z5!N77S6vvsWW^d{$hm1DCfW0o%MywFyNkMT<{8RxcK!OtG(>H_R8_s=Kx2VR7bX76 z3H_`HfmcCCU6`K|QW_&SlMsaIz0o$~oft|?!XHA#)r|f3S|fqd#*%zBLI}d|lMrsY z=_Q2+C1P3hG05kb%zy4m?x9o-Q<59hvy9w(c@r^|;Al?>Hv#(a`q7WNT_U-HGL}yA1j)W!8=&vrNhvY+qAn%h7!v-9b z8z|l@=QdQ#xfEFL+Yg z=n#qw1WS4SV(#4S8qdopP0)r_74sF-BYZwWGpe8)Ob*UpSZ(=9I(N~mqm3gf&dH1-yiZmyb z%YO0FN}T{zg$$Hb?jPPjPlst%0Ee3!VSmA%5`yfvcr?$r1$3k8&rx}hR*qH~w67`C zQX_-Re6s5OG;Y8`9!imNlCOEZYqjb)?K zzY0r+Z^^!+T`#_-nh+8{e)w=()e|cnI$z#bG1D+aeP+dV+Ee7`k8iI>JbT5eR(>?^ zuJW7y(iFEoR<$;kiD4&Xd8i4q{}r+VP7?~N`5_Y=TL_Xf+-XC;5%qB`+AVS48SIPs zvhk){u(sg z`k6zMDkF?;IP#nrDnHTE@NPz%fjavA>XQHG5Ys`*XmWhZ)wQdC7J@}Xmig|?nM%*u z)pG50#z9`08mIBIt8-meeg4$Z)&Dg4WU9)rQU=IFT(iQ2d`!7McNZg5&5C)CU(4h! zwnIBJ{H<5N-L&c*xAxw>U)-?4(MJuUBKjt!)RXR~#9Gf_waDtr| zUNug>)3^377UlEVhvVt*&h%uuvpZ_GM`qzlnYGO3^QlxM568}N@1<16zXJhb6Spt7 zMP|l+ifBq|=#bxLZcZEUSze*j*OZc~c9VMr?%12S4=Iy^Y&z-7;bz1Et}&%;l;M>5 z^{J(`$rsT!`mCbj+4-vV)!ByF-Uu6oP*yh@GvE7EyQCo_f9-T@5f-Q^HdVvV}pX-E*AboVw;a`88i2)1F)g zk%Qx!G2FQYOj=cx9Bwr(9)qZc(lID%KJH7*ZfY+*nhyHh84zoep!nh9a-%fPs5MH= z;Z9DTS$W#~8c)5DBp)EhH?q=eTz(|1QgOG*cVk*rBbttnKAAt`DI8?f8f_KNb)kT` zi~yL?h=1sTRvGelqL$>IKM3&*DX zc6w4*%qFI;#Xw#DWU5v*_gdH5j`;ZP4XZU%djIz1q)_%&AC3c0ZVETpt>1IaGiKVz zInhx!(-oCE&%q zrNxkYl4_&AJZS%1A$8a%GoKQdNAXb0jdof6Ye8z=HDl*L5ZNoA$BuM5R!Y6A$Zb`E z5oDP&$5G0H+WWz63sT>^7D$^t0=KDJwTk$?c}xf1TM=jU+s?_2UP(8n#O;m}N1v4X z>lNTAWkP(8wQc_Lw1j*mC8Z-RX=>~y>ASyJb_8CsDWz0o7vIo|#=|jI#lzCH)`GRB z!x010A7d0HSYJ~bt;|?qXcc^-%vG9rGXGR5-)$;!b6a7r5BFg4Qng{wUu-14Qs_HU zysRG<>qZhn?EcACKck`ts0MMbKxvmOwEXDKN4uaqAYS!JtVxttz|>~GRJ>*iotd@WFO@!(r`)aO1{!>RP>)$x zd^yxBe_JlYaa@rWQ8lRTcq%57DnG&gJ`yS(6>%nZ%D7lM?$JCy+U1L<>U0h%fR=_4 zxnB=aN1D&7&+k}}C_LIQH>0h+6p~EAq|tM@ge%IHg z<3gW`v32hM;>b0!++ir9ut4MX{tf;B3~~93c^`eQyHy^4h|RZOn((7cu%i)QlLvBopx{#U2gIy4}&Tn|owGk;)K{qa_ay)C7mP=9BA#NjGCQiL|Ul_V)BxT*M&! zSSTi)YC9tHs*QWNm#F1eJ7(|q3@vCdxd-x$pHF6Q0!3PaJ@k=4_6OOpLYsq}jWRyz7i7Hs-%D5Rj-LjJ~uK97wW6tW7MdLRRkI@!8KxQ1Wnbpb?$#-GV>n6m2pLtDWxsnKe;j>vlb|ZU`=}B8FsU2 zFVb_XEL%siD6^4VvHyWee-M5|->cFWXw1>IquI2t}(cBk$65`b@~skw3y*| zL1986#&5LJBoX8e4{AAf#(kpTN7p#sPXsE-z(X=y{vYW*hG zq=8b?83X7(dq6*4SGaRq9|Rgv=)(>40RxDtf96vpjZxuzH|Iv50@YcVy=&K7zxc@Sw$_%q*d~c0Ye;*BS|f(Do51TSBkGSn!`E5Ku}!goZO>75c2|D6uR!RuU zk#0-xj#*_va@B{#P%TfN8{GZobxGE2rlv(=X_>rYAPqwCD93um_Q?| z2cxck#twD2Q9sLYtTh3noec~OY!B3rfVtET|LHGvUfNF2Y^wLo%{1iY=^vfG`X@|_ z>QMt)R`KkcPQZNZ=}Cc(R1QH-X_9-FA}O{_!ahX2ri$fHv2*|Vblq(#I5HD&`Xa<+ z3~0bjDw=@-gidWw4%Qfk8ZP1-c^}QpxDU**Q|N6A=#2~Ql%+IpidyJ&gt?jD*xL-3zlceG-Y(4tt#uW_o!yWDQ2sheyOHl>aY!h8VCKrDRZN zvWeg4HeNm^1q@wyxMaop-dY07iI%TrwHsQNRQjM@u9F~dAJ7K^nPM5_Z_SLyCVOc|Qd3>F#xRJrEr5WNlg z?HZY{SkAe+l6J|Re+u&^| zXI_o_!(R}p4k)27<6~5>W(Ewrv}~B8Ld18(&eZ16 zZU$o$ruulZ*Ws z!Lz8RSt7a>w+q7tN4Xhf6-cY)cjW^nndcW57mpNGPG3s(6UaEhPDhL(FhDUjUB`|! zU~U_KZ#kV{iI#^vUArI$T6>2qjgUrSO^2^UlCVYw_peg7qBZb4xxOGz^C}+B%}aZH zZa4b^N+M%e-wf4|75NIk=^E=+y7tJWA+`0zT0^8TlEBc~CH8q}VxL7~@{G+kgOMw6 zUNxZ@qhtUB>ba|dMs{*epW+#~=N$y{(mCXUY0Ncow8LZUQ*3mm(+QQ479{smlfV>I zp@c2PyApY>L&e)s`?lTRcajtO88s(R{=+)`QTbcE4NQ(8$Z@4 zj_HN`2!Aynl`zh#iclbk)^A-S>DcVoXp=>lK#5|Fv~_l4cEEwj(h_YDEyheai#gx2 z;0xkDE?mQc{w5zW3QJ0Ie+$5VO%6>m(`GF+k3H zJ-2^CSw%X}E71Y~N}LHeqt-Sv>yc#|AjTnA!N$R>RF&cXIaJnFGH?Awz#EH^w)%g( ztw>Sl`RR0Zd&3#W3kNx2Zh?$gf-02=Tp*a8V_Y~x14TJ80oUqrmNJZ;EtdsR{k1q*u6}M1%NkK9vqrOIN{+i4c@2(Y4GX$P@WM#UF1fbE{l`%Fn2K$0 zZy$mDWty&`*%9_uo1g_3DJnxSRn`!W+R`z80o^%o*~-hyM+TejI66DOwuWn~yIH5D zV`qb>zeGGI9*G?rikmw#lbAlN_Q!P?$y$iAAfc&_1!6d&*1ggoRat_?>($}aAfb$( zX@69vO@%5HBHmz95lzC?BzYyJJ3J%1T!-ng-pC&(J4s$x8{7WgACzGz96P%@X0@)^ zbDe>kH{)mWABqBr`e4T~n8jh3XRa8S<$MLqJm_wE4$^;@XnOyoYf zmx-OtqSox-`D2jvFO1U(`jPyBV=cBHdV0|Mp&GByb~9E!FK_g*&9Le(<>$IqF5;E# z2l#H|q51=PT4{k6KhSC=8+U2BU&VKwKUxpKY$UX!CHAeg3B=fq%C#kDbbGH@{@@XM zEL_<_V>@vz#E;e#pLYdH9T<5QZa#VPWV;3FN?mm9zy|BJ%oWaH_Csy(9;roQPJVqUd1hGy(5dGWa~`W;VqvB}~zd&J2W$*S6I ziAlt4H|P#`?#j-!32yX)x ze|(e9C}U6D!eS(+Np`Uf%@Ar$IuF2E($9~sFfKrlYYy`n_3o802$bWv|4|JF5Vjnh zM<^b!{xz?<;9VLp=TzoCa48I!@rwgC2*>7TvLeeYDTVvjQ}wmJi5+ucuV3NC+7%ck!MYRJ`eJOjQ_co(0N^>H>w4tMiQByA75)K*cE;kXaO>IuzYd%>d=TxSiz%zbXmb;tS4vE?hbZX~B99 z<}E2aDgg;F)!(vdY)?l1M*e8)V=f@*#6^+-Dr76%2kK`J!3Id@z^izH5vWEtUGRPi zWiR!MLS@uQN`{!ufy@7-K2%48a#_C)#kskt^=oqg)v$JfcPQvA2XeG!t0@G133Xy> zPE}E`c~y|->qZo82XdF8TSjDu>aMc42O2^BW?uAh?#%a>;z1i=vf;WsFBKY^+m-Ma z%&u^g){dNkP0pkEjPJN9X2r>Lbi`~m=y+(SK2zP$23o;pAoSD0Ly4EshumP!gg$eH zJEt#neN{TUhT%%ik&T>74!1U&FGBZNwdXq1ihh+-h@v3hXr^{t+sH`nbL&LDjI~Mh zB0s)d3PW&7iH`rd;?^ekg-n!!K59u8PrDSd9u1}5-? z>ueg+>)qvnpL6ohxl}ip#N+NdXJwo>AQ->e8I`RaZYXmbmwWfs&zR`LrDpORq$&IP z)3SGIq{6swyI+#h5T-Jr6%ys6Uj^XV8PY*aQ|&o3xvFetLHAK=MMOrMG+*BJq4j;j(J*uST!b-~o+{Ow zAKH5n`CEtmmRw;mpL`4geKKK5mARfZV|jjZ|E3vPix*4kLWn+|PI$4>-<$Z5Mkp)s z)y1J~CvoO!UVmTTV}58!Q97(1bKZ2g+Slddn#E4le|BRW7B6l?k|U=QO0P`8SY@mR zq711|+hTX|fY;|eS+gaQ2v-`y_Bjd9e7jG!#$@oci;D|aYCSN3SXKml6EQ9GlFITT zG9e9i;bO3N%#?lDzVg}gc{DTPTMRO((S>}<72abWI!(k6e}wcUs2BA;t8D zujZTf_mZ2#Oc+AC z_VlEf@N?x_e_6071K7SgX)0mwl__cU4)zjubDh8=ug2B9q zR9pv|mDy4~_PICbPC;xDo@_Q4xqdOBEld&#I`m8FZP(vk9QgkB+ie}U9oSSr!HVN2 zPntjriPQtZj=m3%SC7Snx-HU+Uqupw`kqmlDvEb|&p&|WWVQDSC}%aGB0GNkY2)(O z!ag{l7+K(cqE4{(1>iD-~S0B4rbNJ@c8Mg3JjrI=!Fd1|r{liO2`BiS>%*j+xV3N11VQJ8us zYHHWyz_JX^kL6s1#;T<(WjA1&x0bOIYBR!f7{nDlqE_}b6PoXoDDuI1u%>>t&2yYuA z*8Ar$A6e3%2y_oB2=wCqz8Fsfg>=uMW|`&X<)-@jOST}()TOBe1}#5M-qEYZYq%d+ z>m6r^RCPy$w9ax4r7{>OT&FjH3lgHgRWHo8g(15fW5zf_I(lGiBH&6O_Mi<(I1_ zd*wHitx^1`BJhd-)aNjDTzjoTjyuoji{y0{lr0PjEVMa6nmR(wrx$z&KAGs_HVD_4 zEa1XP5T-BZnD>DBBx=|N^>u{Q+vQs={Tholqnb|A@?+4B+H}b^5hHjVVH14&{S`GA z*HyB9kB5_{jFg13oPq6ndMjXyv;Q@AAM?281h0Wd(eoi9$E~?QK#X~~e zg4Eae`GX)3ox@#A@XVG9rIV|s{% zUS8#hUca{4VP#V^%&?WXt;vCn0%Ql0kD6i)uy2)TW_w9M*oI5M%QIga?p@#@%c+e` zRTooWW*b)dt%+%P(w(d*@{L+L130Gd}wE6jJuGph<;0BnPu0ff{iW4hM% zITMfyejM$Ra7}xdr1%jU(%WE29EdCYE-R~D@Uan@y^gJf^et2K#5`V#MOj%Qp6ptg z^ai{~LY+VUw0gY|fohTpjV}hns|m(YDQDa#a;Leu9PRA*d*O`_$V5uvq-zi4ndbny z(?pioQRwFAXPniGo`58Cx1^-RAvDyA7k2i$JjMEOhjZAv=_aB9O%Bc#F!9`EwvJl@ z85nw#m3;Lj&~+Mq3|jAXg@PY2;jDgJ!8%1)7h z58!o#>b+JzaP+*!@=3=)A~l88N!wD@uYXbQP%Cj+w#8N@{R$~&(Cqa;KEH^H3;b=V3}kTnm> zA2gucaoJ*x4|P`VoNCF{Vr;4h&40wqqJPld&>=90Xz7fMy|p*l>(}@5=VP+^Q7_X0 zXafU@T>NDqxs#c6Y4rM#G<(^fJ9?#oH&!RWKJ1x6p`%{}P@VrOK$?s}epgLDMZ+z< zkBf@1lgwx(h9)raAE#%RGF(2g&#Ybf6(3i9fU6&=lr{q0>oRc;rBgBf&aTDr#v7r~ z`?##^!>6b2nF_uW$td7MT|*l?_2jY3x=c%&;U6%Q*&qM5qAJ#Mf8KM`@a(PvtKGin z3)wh`H>q0sK8|n8r!CdeH*8>SWcvp41_+HNv5(AZD|?h_9j=sUWd?;sKx#-iBJ@34 zR7v>xM;HNd;|*h0+#9qCKH{m1kCX5-&$hC&wC{F^z^{_-AUBrIId1f_;z4yY;52yIx>Rt zsanV-o19kn|4OTUmb+A{nNalWA?eoolR!Y%<`g9o%y|%cgD`*jc0nsN=(m~@_v7b( z;KQ?p_L2>6S7TzWU#TZ_{36XDB(*m^!|C5X3`?4BRu9j9>t9w@miL>)2d;|q!qnof z|LrJQc5}Y&775x%H0B6Xtiz2~P=g)bxpa7|RxH54jDwe$>-fY31FA=k?Wt7Ul8x+1 z`!T}ea=^o5di^I)p0I%tkba@ol7n^$V&dBW5x+G)wN&ypxbfwO4<|ffwuO2OBt(&a zjVe|Cxy+}nXc*=%QFgQF-Me>#P*?xrz+4(kR0<|KBO4&tz0<KQ9R``VKnb{qQ;{?prtsXTZb>yGx1J+=L1czaX0Uwn7(X8ZvU{ zKN$l45*jxfgz-o=KB4<+_!Vkew-fmJHo%ws(va0Mb)avm@5eBk0Nmw5GT1Ivb~BgW zfL{}3+_J`m*sv+HOU542Z~T?Q%UMwt;L4urGFpQM0CC-*w~xPT*qd)vU{z=fV@OVT zTOkdzHZ*UNH;)dwM^k+mh}!}2!ph2Cu#GUveI0P`AZwyywV&GU)Nd0+fPD)$qU%K| zDnA=oaObkg$HxWUTL^063ngx1fX=e%Q_~)d=?0UDvqI z4|qlTbS|7^cGAj<+`yRX`Z2KXi%@aR-_}~O+Ko(LRgp@=J6ao9;*bPUD z-If^`K}wAJRXGz?VTt;aL@1FFHa~40$1`3#^E6YwJVTq04WH>;pOLWcbu)Bn{^3c(0ZU3$^BpKT}wY6l=b3U$&# z_4ij2XK>#jIZ7$~6Kt|~^ifq+-~If2vgc?4{AqJWTY_2^-tm(ohQm|(2T zy3!`U{745z_%3i8Fau?l4VVpTLt7EkON-aYR>h$y`RLi;U<(xa40rYmRTs%mFbNCb zBBJ!RYWa!>L?j2IzwAbT*oUm*}p(C`NXrh(p)A(0d90)@GdOCNhs_2@Q-^s9cNrz z9+bj#^FMM98(p=&!3M3AO0x?~^79aPM9~@(Lg~{7G={q|!lwJO9!ZrknPsS=9mwct z9{G7dEpa(;`7lYG#qZ)*Kz0eg+jBY8F**1|iu}A63=nuhKf;h_lFFf5OZ)IRIGt5s z!&%csyEywDy}x`yuOpJRP77L3iL6h!1G-=#s_!+{q3-)SwtXnIez^B3c)X!(zmS)+ zot*}D)aO9fBao2}Biu1?a&L}-S1QafU@Lnlxt_pWB;(+7?BP#Gsd6qNY{KFJPj|(N zY9!_y8IOuCM)f3kXNt9qt$(Dqw|AkTs54`GiPMHCi!?cgbUmcRTd@-;LY=EI8Sebp zKVo{n6m<>#iu2hCM-woO`*0C8F_pJMRmzx5Gs+6e$-&Japd4|Yn=SQ{x#yJ<_BXLm0mFTuxv{(bj&3|ob6)A{=^!TYt;WaakP@#1)ZQXV$ilf7 zL2|P}`bpAe0}@xRX-U(o6C#Ej7-3e2*)2-V%I|_tV3oK*ca13HDSMSuqGG-mJx>wt zNxlDyiWW;$_s@^9q_)q|PAqTM;di2=x4n4UucJFra6>*DE;Vb&I7FKU^WhwYfun@-)KZn z$S{C{$on4+CrOm~Vr^F)um=xRNa>=z)K>!ngT1&_)x*odN*t(;_+a+~lAt3iSMb;A zF;A(iM;T1lj$`4w4RS- zaPBs5^OGl~mQeAGys*I_yx&tJ$us#ORl zwLYl4MB#H>z9U6RBQlM{7{f1uLOjZ8ac$5$TZMo|tF;Is)BEDxUj@ zMxtOJzzVqvj9>w$fwg^W6{$+T&Fl|&ATVPkah4!Jnrk94l`a&7{DFqrH`dfk@1=K} zG~`4T10gEYmxpvlti5*TXf8&3f)NBSLXan(1Wh=rK_OI0nB&mx^a<0YH{=x?{C2Eb zRP(n6N-vR20Ne13o!=aE9JO}tVFqHg!*|9?@biu`%+}HpHe)CSxpvm1r4FS;B*r8U4ngiLAi`i6zvmS&1_?iu?wHZ5g1hDy06b>=_ItgkOYd)jNaHo5?pX_Z-b`Tc#Kz5uoXh(hMijArB)w*H_rXzqld%;N~5>#}O#vhvmo{l)XQ3hj3KcBB$^lDpT`V zu|(o2o>S-s&{@I;qP_L?_~OdSd=~q5*aiZQmOM4%PMm6mDy!NSJXfiv{Tp1^HVJJ7e1Opp$&NPv7N$>$({QlmXI>=SFMn~~G1GRQyUbNsV~56xYN(_WA; zGtwNXzMpkNH`Ik5q<9C3MeBL93Y8hI=QFh)AXlbMcd5<5%IM1-=3W>=jpozEnHIHB zmgv#K@E@8iZv8b8?i{;@>NnGn(SGt)jmf-5z(5t$kk?Z-Hmqz5m zCe`N@!|A%vCR08|&o7f9&nnl~mfhtGOG~>?m3h6ody8o^BM}U^Qrq#!@39Of)4XVK zr9=|v>xG^Y$)#=W7*^$8h)SG)-haona;FuB@J!3?#|vgPuCa4QM4qDK9x!}*hI}ur zS|_`$V{c386W6|@k&;^TBWWHZ>WSpqzcoFf?I6GM0b{SU6PiGyPQKUt9D6cV@dS2$ zqGy+HHe81E=gMAUYBeB3jAx4II`vtt8EZY+Dln1a<&{8l*q8-S8Bo3)@YsMhPb za}b;=-`A&b@k?KVxLsna(X}V$WaT_MSCs$F`J_lZOFA``X;Lv-!*m;6`2i<2gl8}j zl5mj9M=Y?zxiSejzY}y8Idz=+`}n9l0cjScd@FH9M_j$V&3KVL+sVf~Gx}j#EaSNn zUv>RX#3kK#C_7Q4s~N7aomj(_g~k$fqTuAkaP9!)THMHcXgWlq2nH^^+BNM}*dJj9&*&!F1S5!|wzssc=Vpr{KS@ zBE_|Vb+OEC3@hSA6%`LykZV^SyQKIth`g%^oWK3zg+@dY9BToNo_Lb9novMYl8rR=UaYW%5OZWqX(4znia!3&1YB z`L{7_=huU|C60$rUPgBN@Kt()$xo%3EToMPzZE4Trbn$M$%`G-BrtehLwgC3`ZZeX zVGv1{gIZpJnAulw(V$*TA5R9LA8X6J_(kL7#8-DUKS$H#i0>{lVBFsGYecp)@@)m) zo@&BiaUQ;eN+6EE7d#c4g1H(hlSn%E>4&rFT?A6h?C?fzSfBLVzE;R2Ho%9a}-1vah*x8 z*}2)UpUGHfKRP~6(1O(Xm3POdh;6v7Aq)L~LP67WcsUD{~YoZ=W-AblU{ug0u z9{ey|lEL&XlH1p(2ZQ6G*@hE6w~%CkMwEtM<6(2={Wo4+aoa>?Dt{+vvPWa zcbGC>C2!+(gFJ%Ev9c zH;m$c$0xnamEAtcm=n(FHHAJe(B!yzbR`f)d^$0~65kK`uPjV2a2|A|$%cD`T)sUA zOhjHLtA&QF42)#Bu9TY>Vjn#`w8Mk&NhN5Zu)C+{!C)YXj51mKr-Ownt?Gu1qTD@a zm&zrpr@`o7e?AT+tHyZj776mf z@mfrj-;PMF5zgkM4Z*!u*UEwxJ!V$t`}X4%e2jIh$TOACe!w+VJoOXkt+)1&a5u9t zBeQSnxh}L7CtbR)O?w}kSN}yn`eM|*rv1rynjB0IJOtMY;rDwjrK?CiGB zl`ebwq160%?^e?R9&X~7*@O4oy8C^f)g!!$tBM5M+?z`Fn3P#e*NR- zW0%kqvtq*J(P94rc%kOJklP35IBY^a$J?i);we=lF9$|yoJF?jO?J0SRCl~uup~-P zix{0C2yam9T-IlIa<`3H7O*E6fX7X93m#NR4rWNke|(IO6g3a;#3t*#D2D%I?7B&U z&SE*;(}Fp)=G?1ne>^$jL~=^fqxa~5j}W(faa%PhrZSgHx`6pZHd1DQ*`r2fN|csx zPfw^j3o*C9S$9Y3Ly>k-(h-CQTna`_k&7quJh-y5C((9% zKPKol!lufb^+=)YT%P7mFvA&}E;$ixFGoy*;5-61JbJdJwy~EPUX@DHJw>AmeScc^ z@}oWAT*dOCYtQ(qw+-V^>e?GCkd>#!;RIzNQ&W_wL<88P-rV#LPbl zLB}3&bK|;b#}_y?p|&S<)8tIKV^i}5HPHQWA#elcopR{fDPDr@x6TMq<$1UkA_b{S z!XJw=P@|UbK+Jq;2--o1lfLqm84A_w=#1cYem)C{L2urX7u~%wdXo0j&x`F#+4@bMvV1yU4jULIrSB2xj zJqA>!)fMNbv-@*};39m#P-%O_i3IAs1ZD_o6_|3P=W{>UpXW|jAlJnDg$%F4-NgC9 zc^IcmY&H-I6Ya>je>Y)k0;U&65X>X02Wkfgv(ZEWt!Pdiyki_Vy$s^dhm#UC2h@#z zCL-j8-vtlujNS&N*k;496SKU9tQ$)lU>>vCU z1X*1A{-lo;lT-6hE*jEp;M-idZKKJ_5oIgM|4fU(Fe#ZwBr@-VVqL-9fgBZDZe{3& z6ILNbL+|t*FHYnAt|Dyv_VF`Z5;m&8cruy=YAPQ;zZ`m;%nfV?KzAS(=9`v+-C0L& zvZga#O!$|DZ67$yZaN*qO_>^meDtgqixBih6me{1zB^_ueG`sK=$;LS?r~wzFu~7U zN|t6gYQo3-KHNSOrZSA7mu6pdhU9v(#>6QzJ)HyYNwC?&96VYNzn#gn<^?Cxx_ie@ z%=|MnXM?u>AEw?qAgbs4AD&%8x}>E9K}wJgi4_5rTnPb{5JkeEk!C?uBuo$_7nM{& zz@$}5r9(w&P(q}U5aBs@eSf~cXaC%tojY^p%<0#;-RsqA&kK0B89{6;W&IJyn0&eU z=t*Xh&z{nK+mO+Q&OatC=>7gNnJb$~LPZbc+8M>$L*O2*-oz7nWmoaU!aVb=fb~U2 z*b+SUB^Wx4&zT^*YU)A`z+1Vc3W_&5YNgj7;73GS#tp*x^7K;7U8Xl5US99}k=yr# z%~2Znd^Ss?xqbL>2&TVMSdV=E(vEH)!6CM$AmfW=g%DpNWzRG1DUM{1Ks6 zp#R;%IFcnW_4drS6-RAs1k0?iBw^8Mq7qK}c~{smQbIz){5w7vS#02jm6ZdVx&Z@# zvazNMZ2L$32y48D1R*EDke#!(H?Q{o z&wC00Y8#cxU%*A`q1(A|0M)1e9V@87+9H}C9n z;?!6s&BwKCH#Hqf4yjZ*yz+YE%!Ld>Kpz)emjqD-r|;#XpfS@Z9I^CQDx%}^I*~6w z0VN&#YfZl}E=%Kd&v|%P`H@k7=m#-h6UTa){b9q%_~Lk5V=jG@rDA`v;tFZ7PrBaZ78} zs$61`y5CPlddTKxp5Cd_@I$Cx(jM7XP8t@DZp;E>g2Zb!ProcpXmK#{+o(_^LMqkz zFjlo=n$)zF%}72Y{Bz;i8){1Ee8v<)S_~(3oF90FUQuz8I4dKSbLqwT8cPO``EBB4 z#a5j?LkCRKBOsbU-0_bs`rFx#MAyJcvy&~-O(T&JLQFzkl;Yp}A<-%)1 zM6^Dp#uW=(-Fit;4BZ5h5m)?di{pO@8N&PA9fyF_yx=*%wX`K0C(B9cZ7NG2p@Cbu z_n3&UVJO^4l^Aw#Fl|A?npQ;B$JY2NsHQ!(JDbMoeGr#dBXpNc{Gv%H8p`E;liPmeZx{qSLM2=`9_WB zsq;C{D^_1L5Z5w+7!F#FU8aFw8ygxLUU^47c0X|+bgYqUsOOK+SJ>~ZqMx`afyLm} zi+3Kw1fX6MTjf%kh3{ka3v7sZ5AzKGd&pDr0-U1A&DVaDw?)TVcpYOw0NFOI1jpdf z!V(m}8o+Fj6#mOC77Dclc3rjlB`{a>>OiwF`P1jmlu842g<+|&{iIZF*a%3D+r58p zm;Cy59{!4u*>||0Etj^j3NRz>ak^q`J0(#1nAo&BpeU4yNSYv^tU$vkX=rH))XWT2 zeS7@mNy2A7=T7Sl-ihSZKS5iYVV9e19@lT_x{#G&pDwUZR?~n?9HGiD7NJGX@kfId z7d^q4zXh*nP%io;?6l_0k%3gsDm2)TjJ$RJfTe96sWn)c6{fmalam-OXir<1nAh>S zZEahvMIFns`L+6Fmn-`)=U_`{T{>VXOE+R5P0DvMG;8^lsNveMZbZ@&8q|}f+LPwP9sOA{^2$El`Uax6s`avKcBizGg zjw~KPCMbv3Hw{io%({~qAkh70bT*^ySFa-ck5Y9pz$X@(Fx&D?cXOU~pH`&OJ-I-w z(AD;B@KmP@qWTFo)Y`3Pm)9ynRxT_Ky`j3?^toA~ni6eEy3ae9_VP=IlopNI0XKM_ z?3C}pXf5vm50ehXDV`tT1uziH&cw?b!2RbR4N;0XgjX-3Ga9|r1ChFnlh{A31b8?` z4Z*uG@t0*!Z+Z6mt?S0Qzt7P z#Kw`A5LIs9QSyR)G^`ks+A>^bQaZDZ4b-K}j_1Es2W~n3N8ei({K{{;&ed;YBHdtz zb^$1CtC_>nXM`)m57YvediBH)Zt|Hu6t^W2M1i}St0H9UpU~Bsc()QmNs0~IAlvX} zczAeK{Ltk)*Jggv9XTaPlC9?*oK1}6fCIR;Xs#5#_ZZZs(hX6ST>1mIMMoWOlpV*z zx9{nRWx4OKIdANzm)vjUo}*B$b{r)CgF4Z7fZhvrgZ0F&hWh^b{p-g}f5r7(k>)WE zsz*#tE+krT+_wNjG{?)b)plB%ic6wBGc2`MFbDDfY`m!&C zF|fRkTXg=Mj(xse*JYzm)cLRIM~AoB#`0SBym*3bv{GCT11m{UD@%NFY3&F(Z3>&* zYrK;{zcF27FCPDCwN(nS9Se{Y+M!R(Vpnf!8|b zZ$nJKmfhLeJUZX|@>lzEzr*YcE>XLBImoxmU?+!Hq~;*tDhX599@O7fn2^`5oR${s zruf@cT*L-%<#Q&~Gk2Q*QaMCiL8M{X$)W9`0a3YFpJA|5| z<%z}WltLnNC=6}YyKG2GElx_j&Fx&ei}1Z$=g)LmPSxgk#OCgRB6X7Hfrb@)01-}3 zU~qUiTa*amwCqJ$vnGw!Pc=eKd+?i1Rrf*9-Nce4-m_vJl%2)tA@L! z?f9i}uK}ze)}MEegB->BS7!+xke)7Q6{y|t*)AWGGt)51Ew~<>Vye?@m&z%XNoEI0 z48?q-X-~2_Zi0OU^-)v9v`y_0n+FAkhByVT;)~ron?4yC9saY|4jz!yVH%Ln5Txip z6}YyLBMQfy9(7G;PWTgFS&l;NV|YY@-6UVB5)+*vhJ#AKj<`18J*+rq_ieWf>`}=I z&#FRV-GKi+S7_jIfU+fb&4rtYc#+B#J zjPkkDA9)>1cM`;R9}sW4F<(X4TMhZ|p>tsQsTq*yaUL%=A5)L%0h7rHoguXX>fC}I zp3^!+F-^8h47i-l$9^|5d$tmif>U(q2GhB=w=#Iyh~4Qe@8CIR3nT9M_faTG*5pDV zOG;}qw56P{s!ch9p;KG|R07Um%O@gzQzYV2v(_wo{yc}SHifE*XfdU^O^IC#s|d7#UNpVMs~vG02T0WSB)C^ zOJJ@z_-wl84*pUNdyoA_sy}XTe;%vX5Y>gh-GINjEAq^H`ONfbU_uLAJv|>#gOsGPt^+8ncJRbRo4Z-424kZ+`nbpHBR2-v$Rj_R3hH|ESe!u< z!}^V=4h5JLJXE9np=$!FK0Y9f#6(Xl!x?mevg82erJ((~qN zrX-RMtR*+*Dht^%AQ(HqkIs9P_N5sSgg1`Rds)^Ufc0ryYtevo< zT393-@@H$UH6fcDL5l!Tty&w0z=LWrMAa4tM`%#3kKM;+b0c>MESgKdQKTnM*0&J3 z7T8_SPe?$|ppWA%FCDWYHrG})i$#{6wJw|$781H%+*PX*^@Z;A;HD>_y~R{}4PvHV z%gDUz__^D~=^k9I?`IB4On<>6Cx^DAuA57MQ#NKjl>C=TkTJNUivP{jvykOq*N-B) z`LONk{fj9`MP`RlZ7DFQ(wM%pH*nDPv;4Ok?psqooG)GdlkMJAUNS$^_<)Wmb|@pD zxZ!P<6Bl`A_EGJ7yRPQzVq3PFAYrbg$FNwz^;@o#l+DPRY!+BnzlAoj&UK6FaGf9y&+zkV-S@N4XxHG1e}77G2!-%{hB+ezb9v z;2-=+8W9rO4&%EY>j%uqyI_x78kRUDkq)H<#H`7+97w?SG^A`)3Kup66?PF-iXb2` z_U4DUy7t+$-nBB0rzXIx)EcUg?L+=C^}Q`EruMm)V~L^AyJdr+i?H1B;NU0^`s*UL zJw5Yz3&tca9OTk-nnb1H4d~-kYCz%GRbpY*|k zj%fG0<%Ws67*dY|!$t;AFt3A3o3@Ios`Fa%r&xuwcxxcZw|Z2x zFbGn4z7Qw{X;#SIWh#O!k6yT9c&`HB??^X``TK1dx`kF zm{BVv%oXR;`Vn%R$&^$@BMeBYSC|H#JekPA$4T|6wohodif2Xqa7!+ zKjK9^Ke^Qo=EWN1iV!H{f1WugG2MViwvQ(i6E1X3XkHGADe9`YI19z5wb3eV)9UY{ zfes^Q8chP6aY*)euS}F>n^Uu8`o*sl3s_)&BMc(7Y(edb^>5)SmyJGPWk-uc%{rvs zbWfvXQLQil0_%aLe2|oi9U;U3fM}`o4`fL}>IV+s`dfwv{f7}9KvFBgsdDEbw(ff=l5*Ke?)|J7_WtAYA8dsaie|=xul5q~mg_RJL=2DQNb4nlpP6 zBJme2?_!>pstk^Evl7>w3{ntW5TkD|@s~J z7#EL{3tag>FRRIR(M{_hkB^wGvvNL^|n8(e+xRy_uT<1M9i41 zOL!Hξ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 literal 0 HcmV?d00001 diff --git a/app/server/api.js b/app/server/api.js index 7f654d9..a13ea58 100644 --- a/app/server/api.js +++ b/app/server/api.js @@ -10,7 +10,7 @@ Api.addRoute('sensorData', { }, { post: function() { reactToSensorData(this.bodyParams); - SensorData.insert({ + var sensorObject = { temperatureValue: parseFloat(this.bodyParams.temperatureValue), humidityValue: parseFloat(this.bodyParams.humidityValue), tankLevel0: this.bodyParams.tankLevel0, @@ -24,7 +24,8 @@ Api.addRoute('sensorData', { owner: this.bodyParams.owner, controllerId: this.bodyParams.controllerId, created_at: new Date() - }); + }; + SensorData.insert(sensorObject); return []; } }); @@ -47,11 +48,11 @@ reactToSensorData = function(nextSensorReading) { var shouldStartAutomaticPumping = (!state.in_valve || state.in_valve === 'closed') && ((parseInt(nextSensorReading.tankFull) === 0) && startPumpingLevelReached && (state.out_valve === 'closed' || state.out_valve === 'closing')); - console.log("State: ", state); + console.log("State: ", state); var shouldStartPumping = shouldStartAutomaticPumping && config && !config.manualInflow; - console.log("shouldStartAutomaticPumping: ", shouldStartAutomaticPumping); + console.log("shouldStartAutomaticPumping: ", shouldStartAutomaticPumping); console.log("shouldStartPumping: ", shouldStartPumping); @@ -75,11 +76,11 @@ reactToSensorData = function(nextSensorReading) { else if (nextSensorReading.stopPumpingAt == 'TANKLEVEL4') stopPumpingLevelReached = (parseInt(nextSensorReading.tankLevel4) === 1) else stopPumpingLevelReached = false; - var shouldStopAutomaticPumping = (state.in_valve === 'open' || state.in_valve === 'opening') && (parseInt(nextSensorReading.tankFull) === 1 || stopPumpingLevelReached || state.out_valve === 'open' || state.out_valve === 'opening'); + var shouldStopAutomaticPumping = (state.in_valve === 'open' || state.in_valve === 'opening') && (parseInt(nextSensorReading.tankFull) === 1 || stopPumpingLevelReached || state.out_valve === 'open' || state.out_valve === 'opening'); - var shouldStopPumping = shouldStopAutomaticPumping && config && !config.manualInflow; + var shouldStopPumping = shouldStopAutomaticPumping && config && !config.manualInflow; - console.log("shouldStopPumping: ", shouldStopPumping); + console.log("shouldStopPumping: ", shouldStopPumping); if (shouldStopPumping) { @@ -116,10 +117,50 @@ Api.addRoute('state/:id', { } }); +Api.addRoute('picture/:id', { + authRequired: false +}, { + post: function() { + console.log("setting picture", this.bodyParams); + return Picture.upsert({ + controller_id: this.urlParams.id + }, { + '$set': { + 'picture_base64': this.bodyParams.picture_base64, + 'time': new Date() + } + }); + }, + get: function() { + var id = this.urlParams.id; + var res = this.response; + var picture_src = "/images/noImage.png"; + var pictureEntry = Picture.findOne({ + controller_id: id + }); + if (pictureEntry) { + var imageData = pictureEntry.picture_base64; + picture_src = "data:image/jpeg;charset=utf-8;base64," + imageData; + } + return { + picture: picture_src + }; + } +}); + +function base64_to_buffer(base64string) { + var buf; + if (typeof Buffer.from === "function") { + buf = Buffer.from(base64string, 'base64'); + } else { + buf = new Buffer(base64string, 'base64'); + } + return buf; +} function stateOrDefault(id) { var stateEntry = ControllerState.findOne({ - controller_id: id, + controller_id: id }); if (stateEntry === undefined) { -- 2.47.3 From 4b2b9526de3ea1d7adc15f58d20e7cdb336b1cc7 Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 16:02:59 +0200 Subject: [PATCH 03/18] picture display ready --- app/client/startup.js | 4 +-- app/client/state_details.html | 1 + app/client/state_details.js | 7 +++-- app/client/surveillance.html | 15 +++++++++++ app/client/surveillance.js | 49 +++++++++++++++++++++++++++++++++++ app/client/tabs.html | 2 +- app/client/tabs.js | 4 ++- app/server/api.js | 15 ----------- app/server/methods.js | 14 +++++++++- app/server/publications.js | 8 ++++++ 10 files changed, 97 insertions(+), 22 deletions(-) create mode 100644 app/client/surveillance.html create mode 100644 app/client/surveillance.js diff --git a/app/client/startup.js b/app/client/startup.js index 1a3518a..e4d6914 100644 --- a/app/client/startup.js +++ b/app/client/startup.js @@ -2,7 +2,7 @@ Tracker.autorun(function () { var id = Session.get('controller_id'); if (id) { Meteor.subscribe("sensor_data", id); - var hamo = Meteor.subscribe("controller_state", id); - console.log(hamo); + Meteor.subscribe("controller_state", id); + Meteor.subscribe('pictures', id); } }); diff --git a/app/client/state_details.html b/app/client/state_details.html index 070059d..b818f86 100644 --- a/app/client/state_details.html +++ b/app/client/state_details.html @@ -12,6 +12,7 @@
Otpusni ventil: {{pretty_valve state.out_valve }}
Ulazni ventil/pumpa: {{pretty_valve state.in_valve }}
+
Dobavlja sliku: {{picture_requested state }}
Zadnja komunikacija: {{ last_communication_time }}
Zadnje zaljevanje: {{ last_out_valve_open }} diff --git a/app/client/state_details.js b/app/client/state_details.js index 17b3e26..35706ba 100644 --- a/app/client/state_details.js +++ b/app/client/state_details.js @@ -9,10 +9,13 @@ Template.state_details.helpers({ return moment(this.time).fromNow(); }, last_out_valve_open: function() { - return moment(this.significantEvents.lastOutValveOpen).fromNow(); + return ""; }, last_in_valve_open: function() { - return moment(this.significantEvents.lastInValveOpen).fromNow(); + return ""; + }, + picture_requested: function(state) { + return (state.picture_requested === 'true') ? 'DA' : 'NE'; } }); diff --git a/app/client/surveillance.html b/app/client/surveillance.html new file mode 100644 index 0000000..2750fa9 --- /dev/null +++ b/app/client/surveillance.html @@ -0,0 +1,15 @@ + diff --git a/app/client/surveillance.js b/app/client/surveillance.js new file mode 100644 index 0000000..d61bd35 --- /dev/null +++ b/app/client/surveillance.js @@ -0,0 +1,49 @@ +function controller_state() { + var controllerId = Session.get('controller_id'); + result = ControllerState.findOne({}); + if (!result) { + result = {} + }; + return result; +}; + +function picture() { + var controllerId = Session.get('controller_id'); + result = Picture.findOne({ + controller_id: controllerId + }); + console.log("rez je", result); + if (!result) { + result = {} + }; + return result; +}; + + + +Template.surveillance.helpers({ + controller_state: controller_state, + picture_src: function() { + var picture_base64 = picture().picture_base64; + var picture_src = '/images/noImage.png'; + if (picture_base64) { + picture_src = 'data:image/jpeg;charset=utf-8;base64,' + picture_base64; + } + return picture_src; + }, + picture_time: function() { + var picture_entry = picture(); + if (picture_entry) { + return moment(picture_entry.time).fromNow(); + } else { + return "Nikad!"; + } + } +}); + +Template.surveillance.events({ + 'click #request_new_picture': function() { + var controller_id = Session.get('controller_id'); + Meteor.call('requestNewPicture', controller_id) + } +}); diff --git a/app/client/tabs.html b/app/client/tabs.html index 036106b..accaac7 100644 --- a/app/client/tabs.html +++ b/app/client/tabs.html @@ -4,8 +4,8 @@ + - diff --git a/app/client/tabs.js b/app/client/tabs.js index 0de0d4b..ff1bd86 100644 --- a/app/client/tabs.js +++ b/app/client/tabs.js @@ -26,10 +26,12 @@ Template.tabs.events({ 'click .log': function() { Session.set('templateName', 'log'); }, + 'click .surveillance': function() { + Session.set('templateName', 'surveillance'); + }, 'click .settings': function() { Session.set('templateName', 'settings'); }, - 'click #switch': function() { var instance = Template.instance(); controller_id = instance.$('#controller').val(); diff --git a/app/server/api.js b/app/server/api.js index a13ea58..56d1473 100644 --- a/app/server/api.js +++ b/app/server/api.js @@ -130,21 +130,6 @@ Api.addRoute('picture/:id', { 'time': new Date() } }); - }, - get: function() { - var id = this.urlParams.id; - var res = this.response; - var picture_src = "/images/noImage.png"; - var pictureEntry = Picture.findOne({ - controller_id: id - }); - if (pictureEntry) { - var imageData = pictureEntry.picture_base64; - picture_src = "data:image/jpeg;charset=utf-8;base64," + imageData; - } - return { - picture: picture_src - }; } }); diff --git a/app/server/methods.js b/app/server/methods.js index 16d71cb..4459f77 100644 --- a/app/server/methods.js +++ b/app/server/methods.js @@ -52,6 +52,17 @@ function setInValveTo(controller_id, nextState) { } +function requestNewPicture(controller_id) { + var state = controller_state(controller_id); + ControllerState.update(state._id, { + '$set': { + 'state.picture_requested': 'true', + 'time': new Date(), + 'set_by': 'server' + } + }); +}; + function openInValve(controller_id) { var state = controller_state(controller_id); var config = state.config; @@ -174,5 +185,6 @@ Meteor.methods({ openInValve: openInValve, closeInValve: closeInValve, clearLog: clearLog, - saveControllerConfig: saveControllerConfig + saveControllerConfig: saveControllerConfig, + requestNewPicture: requestNewPicture }); diff --git a/app/server/publications.js b/app/server/publications.js index cd14e21..30ae624 100644 --- a/app/server/publications.js +++ b/app/server/publications.js @@ -16,3 +16,11 @@ Meteor.publish("controller_state", function(controllerId) { controller_id: controllerId }); }); + + +// This code only runs on the server +Meteor.publish("pictures", function(controllerId) { + return Picture.find({ + controller_id: controllerId + }); +}); -- 2.47.3 From 7c8863c2147115ca89d86689d2e11308bc0edb18 Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 16:27:05 +0200 Subject: [PATCH 04/18] picture sending scripts ready --- controller/config/copy__init__.py.example | 1 + controller/sensors.py | 38 ++++++++++++----------- controller/state/changer.py | 4 +-- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/controller/config/copy__init__.py.example b/controller/config/copy__init__.py.example index 4708f59..26312c8 100644 --- a/controller/config/copy__init__.py.example +++ b/controller/config/copy__init__.py.example @@ -18,3 +18,4 @@ STATE_FILE = '/mnt/zoblakdata/controller_state' PICTURE_TRANSFER_FILE='/mnt/zoblakdata/picture_transfer_ready.jpg' PICTURE_INPUT_FILE='/mnt/zoblakdata/picture.jpg' # must match file in PICTURE_COMMAND PICTURE_COMMAND='avconv -i rtsp://192.168.1.10:554//user=admin_password=_channel=1_stream=0.sdp -f image2 -vframes 1 /mnt/zoblakdata/picture.jpg' # filename must match PICTURE_FILE path +PICTURE_URL = 'http://agrar.zoblak.com/api/v1.0/picture/' diff --git a/controller/sensors.py b/controller/sensors.py index 19b898e..dc1b33e 100644 --- a/controller/sensors.py +++ b/controller/sensors.py @@ -8,27 +8,27 @@ import camera # Try to read the state of GPIO_PIN_TANKLEVELx and GPIO_PIN_TANKFULL GPIO.setmode(GPIO.BCM) -GPIO.setup(config.GPIO_PIN_TANKLEVEL0, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) -GPIO.setup(config.GPIO_PIN_TANKLEVEL1, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) -GPIO.setup(config.GPIO_PIN_TANKLEVEL2, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) -GPIO.setup(config.GPIO_PIN_TANKLEVEL3, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) +#GPIO.setup(config.GPIO_PIN_TANKLEVEL0, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) +#GPIO.setup(config.GPIO_PIN_TANKLEVEL1, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) +#GPIO.setup(config.GPIO_PIN_TANKLEVEL2, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) +#GPIO.setup(config.GPIO_PIN_TANKLEVEL3, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) GPIO.setup(config.GPIO_PIN_TANKLEVEL4, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) GPIO.setup(config.GPIO_PIN_TANKFULL, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # tank sensor has inverse logic - 0 when it is full 1 when it is not # so we are inverting its value here -tankLevel0 = (GPIO.input(config.GPIO_PIN_TANKLEVEL0) == GPIO.LOW) -tankLevel1 = (GPIO.input(config.GPIO_PIN_TANKLEVEL1) == GPIO.LOW) -tankLevel2 = (GPIO.input(config.GPIO_PIN_TANKLEVEL2) == GPIO.LOW) -tankLevel3 = (GPIO.input(config.GPIO_PIN_TANKLEVEL3) == GPIO.LOW) +# tankLevel0 = (GPIO.input(config.GPIO_PIN_TANKLEVEL0) == GPIO.LOW) +# tankLevel1 = (GPIO.input(config.GPIO_PIN_TANKLEVEL1) == GPIO.LOW) +# tankLevel2 = (GPIO.input(config.GPIO_PIN_TANKLEVEL2) == GPIO.LOW) +# tankLevel3 = (GPIO.input(config.GPIO_PIN_TANKLEVEL3) == GPIO.LOW) tankLevel4 = (GPIO.input(config.GPIO_PIN_TANKLEVEL4) == GPIO.LOW) tankFull = (GPIO.input(config.GPIO_PIN_TANKFULL) == GPIO.LOW) GPIO.cleanup() -print 'Bacva Level0: {}'.format(tankLevel0) -print 'Bacva Level1: {}'.format(tankLevel1) -print 'Bacva Level2: {}'.format(tankLevel2) -print 'Bacva Level3: {}'.format(tankLevel3) +# print 'Bacva Level0: {}'.format(tankLevel0) +# print 'Bacva Level1: {}'.format(tankLevel1) +# print 'Bacva Level2: {}'.format(tankLevel2) +# print 'Bacva Level3: {}'.format(tankLevel3) print 'Bacva Level4: {}'.format(tankLevel4) print 'Bacva puna: {}'.format(tankFull) @@ -43,9 +43,6 @@ stopPumpingAt = config.STOP_PUMPING_AT # to 15 times to get a sensor reading (waiting 2 seconds between each retry). humidity, temperature = Adafruit_DHT.read_retry(SENSOR_TYPE, config.GPIO_PIN_DHT) -picture_base64 = camera.get_transfer_picture_base64() -if picture_base64 is not None: - camera.remove_transfer_picture() # Un-comment the line below to convert the temperature to Fahrenheit. # temperature = temperature * 9/5.0 + 32 @@ -55,9 +52,14 @@ if picture_base64 is not None: # guarantee the timing of calls to read the sensor). # If this happens try again! if tankFull is not None: - response = requests.post(config.SENSORDATA_URL, json={"owner": owner, "temperatureValue": temperature, "humidityValue":humidity, "tankLevel0": "1" if tankLevel0 else "0","tankLevel1": "1" if tankLevel1 else "0","tankLevel2": "1" if tankLevel2 else "0","tankLevel3": "1" if tankLevel3 else "0", "tankLevel4": "1" if tankLevel4 else "0", "tankFull": "1" if tankFull else "0", - "startPumpingAt": startPumpingAt,"stopPumpingAt": stopPumpingAt,"controllerId": controller_id, - 'picture': picture_base64 + response = requests.post(config.SENSORDATA_URL, json={"owner": owner, "temperatureValue": temperature, "humidityValue":humidity, + # "tankLevel0": "1" if tankLevel0 else "0", + # "tankLevel1": "1" if tankLevel1 else "0", + # "tankLevel2": "1" if tankLevel2 else "0", + # "tankLevel3": "1" if tankLevel3 else "0", + "tankLevel4": "1" if tankLevel4 else "0", + "tankFull": "1" if tankFull else "0", + "startPumpingAt": startPumpingAt,"stopPumpingAt": stopPumpingAt,"controllerId": controller_id }) print 'Temp={0:0.1f}*C'.format(temperature) print 'Humidity={0:0.1f}%'.format(humidity) diff --git a/controller/state/changer.py b/controller/state/changer.py index e377cad..1946e0e 100644 --- a/controller/state/changer.py +++ b/controller/state/changer.py @@ -67,9 +67,9 @@ class Changer(object): self.local_state['out_valve'] = 'closed' def fulfill_picture_request(self): - if self.remote_state['transfer_picture'] == 'true': + if self.remote_state['picture_requested'] == 'true': make_transfer_picture() - self.local_state['transfer_picture'] = 'false' + self.local_state['picture_requested'] = 'false' def validate_states(self): -- 2.47.3 From 4c1f3f9f432aa519b6e3ce62e567dbed729df252 Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 16:32:48 +0200 Subject: [PATCH 05/18] picture controller scripts ready --- controller/camera_capture.py | 3 +++ controller/camera_send.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 controller/camera_capture.py create mode 100644 controller/camera_send.py diff --git a/controller/camera_capture.py b/controller/camera_capture.py new file mode 100644 index 0000000..f1e89bd --- /dev/null +++ b/controller/camera_capture.py @@ -0,0 +1,3 @@ +import camera + +camera.capture_picture(): diff --git a/controller/camera_send.py b/controller/camera_send.py new file mode 100644 index 0000000..e3acd68 --- /dev/null +++ b/controller/camera_send.py @@ -0,0 +1,16 @@ +import camera +import requests +import config + +picture_base64 = camera.get_transfer_picture_base64() +if picture_base64 is not None: + camera.remove_transfer_picture() + +controller_id = config.CONTROLLER_ID +owner = "Controller: %s" % controller_id + +response = requests.post(config.PICTURE_URL + '/' + controller_id, json={ +"owner": owner, +"controllerId": controller_id, +"picture_base64": picture_base64 +}) -- 2.47.3 From d06f991610a5d4f909e318258998f150fffcfa83 Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 16:39:14 +0200 Subject: [PATCH 06/18] fix syntax errors --- controller/camera_capture.py | 2 +- controller/camera_send.py | 2 +- controller/config/copy__init__.py.example | 4 +--- controller/drivers/__init__.py | 0 controller/sensors.py | 1 - 5 files changed, 3 insertions(+), 6 deletions(-) create mode 100644 controller/drivers/__init__.py diff --git a/controller/camera_capture.py b/controller/camera_capture.py index f1e89bd..99fc648 100644 --- a/controller/camera_capture.py +++ b/controller/camera_capture.py @@ -1,3 +1,3 @@ import camera -camera.capture_picture(): +camera.capture_picture() diff --git a/controller/camera_send.py b/controller/camera_send.py index e3acd68..ca1103d 100644 --- a/controller/camera_send.py +++ b/controller/camera_send.py @@ -1,4 +1,4 @@ -import camera +import drivers.camera import requests import config diff --git a/controller/config/copy__init__.py.example b/controller/config/copy__init__.py.example index 26312c8..cdfbc78 100644 --- a/controller/config/copy__init__.py.example +++ b/controller/config/copy__init__.py.example @@ -1,5 +1,3 @@ - - GPIO_PIN_DHT = 4 # BCM SENSORDATA_URL = 'http://agrar.zoblak.com/api/v1.0/sensorData' GPIO_PIN_TANKLEVEL0 = 5 # BCM @@ -17,5 +15,5 @@ CONTROLLER_ID = '120' # every controller must have a different one STATE_FILE = '/mnt/zoblakdata/controller_state' PICTURE_TRANSFER_FILE='/mnt/zoblakdata/picture_transfer_ready.jpg' PICTURE_INPUT_FILE='/mnt/zoblakdata/picture.jpg' # must match file in PICTURE_COMMAND -PICTURE_COMMAND='avconv -i rtsp://192.168.1.10:554//user=admin_password=_channel=1_stream=0.sdp -f image2 -vframes 1 /mnt/zoblakdata/picture.jpg' # filename must match PICTURE_FILE path +PICTURE_COMMAND='avconv -i rtsp://192.168.1.10:554//user=admin_password=_channel=1_stream=0.sdp -i zoblakLogo.png -filter_complex "[0:v]drawtext=fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:fontsize=30:box=1:boxcolor=black@0.75:text='%d\.%m\.%Y\. %H\:%M\:%S':fontcolor=white@0.8: x=10: y=10[text]; [text][1:v]overlay=main_w-overlay_w-5:5 [filtered]" -map "[filtered]" -f image2 -vframes 1 /mnt/zoblakdata/picture.jpg' # filename must match PICTURE_INPUT_FILE path PICTURE_URL = 'http://agrar.zoblak.com/api/v1.0/picture/' diff --git a/controller/drivers/__init__.py b/controller/drivers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/controller/sensors.py b/controller/sensors.py index dc1b33e..70ea964 100644 --- a/controller/sensors.py +++ b/controller/sensors.py @@ -4,7 +4,6 @@ import requests import Adafruit_DHT import config import RPi.GPIO as GPIO -import camera # Try to read the state of GPIO_PIN_TANKLEVELx and GPIO_PIN_TANKFULL GPIO.setmode(GPIO.BCM) -- 2.47.3 From 7aa9abc2fcd1a97e968ea7d1c881fba5726a9f51 Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 16:43:12 +0200 Subject: [PATCH 07/18] fix syntax errors --- controller/camera_capture.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/camera_capture.py b/controller/camera_capture.py index 99fc648..ff25dbe 100644 --- a/controller/camera_capture.py +++ b/controller/camera_capture.py @@ -1,3 +1,3 @@ -import camera +import drivers.camera camera.capture_picture() -- 2.47.3 From 10b5aa49467609480ece0f8187f6b8f87e00febf Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 16:51:12 +0200 Subject: [PATCH 08/18] fix syntax errors --- controller/camera_capture.py | 2 +- controller/camera_send.py | 2 +- controller/config/copy__init__.py.example | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/controller/camera_capture.py b/controller/camera_capture.py index ff25dbe..ff590d5 100644 --- a/controller/camera_capture.py +++ b/controller/camera_capture.py @@ -1,3 +1,3 @@ -import drivers.camera +import drivers.camera as camera camera.capture_picture() diff --git a/controller/camera_send.py b/controller/camera_send.py index ca1103d..c62823c 100644 --- a/controller/camera_send.py +++ b/controller/camera_send.py @@ -1,4 +1,4 @@ -import drivers.camera +import drivers.camera as camera import requests import config diff --git a/controller/config/copy__init__.py.example b/controller/config/copy__init__.py.example index cdfbc78..36f0b59 100644 --- a/controller/config/copy__init__.py.example +++ b/controller/config/copy__init__.py.example @@ -15,5 +15,5 @@ CONTROLLER_ID = '120' # every controller must have a different one STATE_FILE = '/mnt/zoblakdata/controller_state' PICTURE_TRANSFER_FILE='/mnt/zoblakdata/picture_transfer_ready.jpg' PICTURE_INPUT_FILE='/mnt/zoblakdata/picture.jpg' # must match file in PICTURE_COMMAND -PICTURE_COMMAND='avconv -i rtsp://192.168.1.10:554//user=admin_password=_channel=1_stream=0.sdp -i zoblakLogo.png -filter_complex "[0:v]drawtext=fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:fontsize=30:box=1:boxcolor=black@0.75:text='%d\.%m\.%Y\. %H\:%M\:%S':fontcolor=white@0.8: x=10: y=10[text]; [text][1:v]overlay=main_w-overlay_w-5:5 [filtered]" -map "[filtered]" -f image2 -vframes 1 /mnt/zoblakdata/picture.jpg' # filename must match PICTURE_INPUT_FILE path +PICTURE_COMMAND="""avconv -i rtsp://192.168.5.10:554//user=admin_password=_channel=1_stream=0.sdp -i zoblakLogo.png -filter_complex "[0:v]drawtext=fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:fontsize=30:box=1:boxcolor=black@0.75:text='%d\.%m\.%Y\. %H\:%M\:%S':fontcolor=white@0.8: x=10: y=10[text]; [text][1:v]overlay=main_w-overlay_w-5:5 [filtered]" -map "[filtered]" -f image2 -vframes 1 /mnt/zoblakdata/picture.jpg""" # filename must match PICTURE_INPUT_FILE path PICTURE_URL = 'http://agrar.zoblak.com/api/v1.0/picture/' -- 2.47.3 From aff83d948adea28334626dc8f7465f8b2e96900b Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 16:55:15 +0200 Subject: [PATCH 09/18] zoblak logo --- controller/config/copy__init__.py.example | 2 +- controller/zoblakLogo.png | Bin 0 -> 18451 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 controller/zoblakLogo.png diff --git a/controller/config/copy__init__.py.example b/controller/config/copy__init__.py.example index 36f0b59..244d6d9 100644 --- a/controller/config/copy__init__.py.example +++ b/controller/config/copy__init__.py.example @@ -15,5 +15,5 @@ CONTROLLER_ID = '120' # every controller must have a different one STATE_FILE = '/mnt/zoblakdata/controller_state' PICTURE_TRANSFER_FILE='/mnt/zoblakdata/picture_transfer_ready.jpg' PICTURE_INPUT_FILE='/mnt/zoblakdata/picture.jpg' # must match file in PICTURE_COMMAND -PICTURE_COMMAND="""avconv -i rtsp://192.168.5.10:554//user=admin_password=_channel=1_stream=0.sdp -i zoblakLogo.png -filter_complex "[0:v]drawtext=fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:fontsize=30:box=1:boxcolor=black@0.75:text='%d\.%m\.%Y\. %H\:%M\:%S':fontcolor=white@0.8: x=10: y=10[text]; [text][1:v]overlay=main_w-overlay_w-5:5 [filtered]" -map "[filtered]" -f image2 -vframes 1 /mnt/zoblakdata/picture.jpg""" # filename must match PICTURE_INPUT_FILE path +PICTURE_COMMAND="""avconv -i rtsp://192.168.5.10:554//user=admin_password=_channel=1_stream=0.sdp -i /home/pi/projects/tfm/controller/zoblakLogo.png -filter_complex "[0:v]drawtext=fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:fontsize=30:box=1:boxcolor=black@0.75:text='%d\.%m\.%Y\. %H\:%M\:%S':fontcolor=white@0.8: x=10: y=10[text]; [text][1:v]overlay=main_w-overlay_w-5:5 [filtered]" -map "[filtered]" -f image2 -vframes 1 /mnt/zoblakdata/picture.jpg""" # filename must match PICTURE_INPUT_FILE path PICTURE_URL = 'http://agrar.zoblak.com/api/v1.0/picture/' diff --git a/controller/zoblakLogo.png b/controller/zoblakLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..27112fff6095abccee08712e5174cfb65780b57d GIT binary patch literal 18451 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 Date: Sat, 8 Oct 2016 16:59:22 +0200 Subject: [PATCH 10/18] syntax errors --- controller/drivers/camera/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/controller/drivers/camera/__init__.py b/controller/drivers/camera/__init__.py index c76bc05..3fc347a 100644 --- a/controller/drivers/camera/__init__.py +++ b/controller/drivers/camera/__init__.py @@ -2,6 +2,7 @@ import config import os import base64; from shutil import copyfile +import sys def capture_picture(): os.system(config.PICTURE_COMMAND) -- 2.47.3 From 45114ee10b36b95c6e59a2e1b67cd868d86ef396 Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 17:09:40 +0200 Subject: [PATCH 11/18] syntax errors --- controller/state/changer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/state/changer.py b/controller/state/changer.py index 1946e0e..f2b7714 100644 --- a/controller/state/changer.py +++ b/controller/state/changer.py @@ -1,6 +1,6 @@ import RPi.GPIO as GPIO import config -from camera import make_transfer_picture +from drivers.camera import make_transfer_picture class Changer(object): -- 2.47.3 From 1ab87ca08da073124bade991de9c4b3e8c34baa8 Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 17:16:36 +0200 Subject: [PATCH 12/18] problems with sending --- controller/camera_send.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/controller/camera_send.py b/controller/camera_send.py index c62823c..4afc815 100644 --- a/controller/camera_send.py +++ b/controller/camera_send.py @@ -10,7 +10,8 @@ controller_id = config.CONTROLLER_ID owner = "Controller: %s" % controller_id response = requests.post(config.PICTURE_URL + '/' + controller_id, json={ -"owner": owner, -"controllerId": controller_id, "picture_base64": picture_base64 }) + +puts response +puts picture_base64 is not None -- 2.47.3 From 625d9f47ad0b0b0d0f816d4d5fb206062733d40a Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 17:32:21 +0200 Subject: [PATCH 13/18] problems with sending --- controller/camera_send.py | 4 ++-- controller/state/file.py | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/controller/camera_send.py b/controller/camera_send.py index 4afc815..d8734f5 100644 --- a/controller/camera_send.py +++ b/controller/camera_send.py @@ -13,5 +13,5 @@ response = requests.post(config.PICTURE_URL + '/' + controller_id, json={ "picture_base64": picture_base64 }) -puts response -puts picture_base64 is not None +print response +print picture_base64 is not None diff --git a/controller/state/file.py b/controller/state/file.py index 1172205..f635dde 100644 --- a/controller/state/file.py +++ b/controller/state/file.py @@ -9,7 +9,10 @@ class File(object): self.filename = filename def present(self): - os.path.isfile(self.filename) + result = os.path.isfile(self.filename) + print "File %s" % self.filename + print result + result def load(self): if self.filename is None: -- 2.47.3 From 761b67003b37e5d951593ec0f15c728f2a07eead Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 18:20:50 +0200 Subject: [PATCH 14/18] problems with state --- controller/state/file.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/controller/state/file.py b/controller/state/file.py index f635dde..9bbe285 100644 --- a/controller/state/file.py +++ b/controller/state/file.py @@ -9,10 +9,8 @@ class File(object): self.filename = filename def present(self): - result = os.path.isfile(self.filename) - print "File %s" % self.filename - print result - result + return os.path.isfile(self.filename) + def load(self): if self.filename is None: -- 2.47.3 From 5c58f9f28d943faa8703be63d7a34b1ddae86cc9 Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 18:26:11 +0200 Subject: [PATCH 15/18] problems with state --- controller/state/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/controller/state/__init__.py b/controller/state/__init__.py index 1c5ffb1..d1a50d5 100644 --- a/controller/state/__init__.py +++ b/controller/state/__init__.py @@ -37,7 +37,10 @@ def sync(): changer = Changer(local_state, server_state) current_state = changer.process_change() + print "posting :" + repr(current_state) + local.data = current_state server.post_state(current_state) + local.save() except: print " panicking safely ! " safely_panic() -- 2.47.3 From 2da669b061b180056d629a90bb2bc47443e18f68 Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 18:29:52 +0200 Subject: [PATCH 16/18] problems with state fixed --- app/server/api.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/server/api.js b/app/server/api.js index 56d1473..ed6b715 100644 --- a/app/server/api.js +++ b/app/server/api.js @@ -107,6 +107,7 @@ Api.addRoute('state/:id', { '$set': { 'state.out_valve': this.bodyParams.out_valve, 'state.in_valve': this.bodyParams.in_valve, + 'state.picture_requested': this.bodyParams.picture_requested, 'time': new Date(), 'set_by': 'client' } -- 2.47.3 From 980e35fd9662c81f77e7b6ddc16fd7161c330b38 Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 18:34:53 +0200 Subject: [PATCH 17/18] problems with camera not sending --- controller/camera_send.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/controller/camera_send.py b/controller/camera_send.py index d8734f5..afc0b9a 100644 --- a/controller/camera_send.py +++ b/controller/camera_send.py @@ -5,13 +5,12 @@ import config picture_base64 = camera.get_transfer_picture_base64() if picture_base64 is not None: camera.remove_transfer_picture() + controller_id = config.CONTROLLER_ID + owner = "Controller: %s" % controller_id -controller_id = config.CONTROLLER_ID -owner = "Controller: %s" % controller_id + response = requests.post(config.PICTURE_URL + '/' + controller_id, json={ + "picture_base64": picture_base64 + }) + print response -response = requests.post(config.PICTURE_URL + '/' + controller_id, json={ -"picture_base64": picture_base64 -}) - -print response print picture_base64 is not None -- 2.47.3 From 314ea49eaacaff3c2d6738ce8cd14f40b187cf2a Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Sat, 8 Oct 2016 18:48:19 +0200 Subject: [PATCH 18/18] readme update --- controller/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/controller/README.md b/controller/README.md index 14a7a80..64f17b9 100644 --- a/controller/README.md +++ b/controller/README.md @@ -11,6 +11,8 @@ crontab -e -u root #enter these lines */5 * * * * cd /home/pi/projects/tfm/controller && sh activity.sh */30 * * * * /usr/bin/python /home/pi/projects/tfm/controller/network_check.py +*/1 * * * * /usr/bin/python /home/pi/projects/tfm/controller/camera_capture.py +*/1 * * * * /usr/bin/python /home/pi/projects/tfm/controller/camera_send.py ``` 4. add following lines at the end of /etc/rc.local @@ -29,3 +31,10 @@ mkdir -p /mnt/zoblakdata ``` tmpfs /mnt/zoblakdata tmpfs nodev,nosuid,size=100M 0 0 ``` + +7. install packages + +``` +sudo apt-get update +sudo apt-get install libav-tools +``` -- 2.47.3