From 4708c26d54d5c924650f75950bfbb8701857263e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xose=20P=C3=A9rez?= Date: Fri, 20 Jan 2017 12:43:26 +0100 Subject: [PATCH 1/2] APIs for sensor data, REST & domoticz --- code/espurna/config/all.h | 1 + code/espurna/config/arduino.h | 18 +-- code/espurna/config/prototypes.h | 14 ++ code/espurna/config/sensors.h | 2 +- code/espurna/data/index.html.gz | Bin 3547 -> 3790 bytes code/espurna/data/script.js.gz | Bin 37895 -> 37897 bytes code/espurna/dht.ino | 12 +- code/espurna/domoticz.ino | 63 +------- code/espurna/ds18b20.ino | 6 + code/espurna/emon.ino | 6 + code/espurna/espurna.ino | 14 +- code/espurna/pow.ino | 19 +++ code/espurna/relay.ino | 173 ++++++++++++++++++---- code/espurna/web.ino | 242 +++++++++++++++++++------------ code/html/custom.js | 8 +- code/html/index.html | 39 ++++- 16 files changed, 403 insertions(+), 214 deletions(-) create mode 100644 code/espurna/config/prototypes.h diff --git a/code/espurna/config/all.h b/code/espurna/config/all.h index ddbefc6d..1bbc90a9 100644 --- a/code/espurna/config/all.h +++ b/code/espurna/config/all.h @@ -1,5 +1,6 @@ #include "version.h" #include "arduino.h" +#include "prototypes.h" #include "debug.h" #include "general.h" #include "hardware.h" diff --git a/code/espurna/config/arduino.h b/code/espurna/config/arduino.h index d8bb52d2..3b6d4682 100644 --- a/code/espurna/config/arduino.h +++ b/code/espurna/config/arduino.h @@ -28,14 +28,14 @@ //#define ESPURNA //-------------------------------------------------------------------------------- -// Features (values below are default values) +// Features (values below are non-default values) //-------------------------------------------------------------------------------- -//#define ENABLE_DHT 0 -//#define ENABLE_DS18B20 0 -//#define ENABLE_EMON 0 -//#define ENABLE_HLW8018 0 -//#define ENABLE_RF 0 -//#define ENABLE_FAUXMO 1 -//#define ENABLE_NOFUSS 0 -//#define ENABLE_DOMOTICZ 1 +//#define ENABLE_DHT 1 +//#define ENABLE_DS18B20 1 +//#define ENABLE_EMON 1 +//#define ENABLE_HLW8018 1 +//#define ENABLE_RF 1 +//#define ENABLE_FAUXMO 0 +//#define ENABLE_NOFUSS 1 +//#define ENABLE_DOMOTICZ 0 diff --git a/code/espurna/config/prototypes.h b/code/espurna/config/prototypes.h new file mode 100644 index 00000000..6311efb3 --- /dev/null +++ b/code/espurna/config/prototypes.h @@ -0,0 +1,14 @@ +#include +#include +#include +#include +#include +#include + +typedef std::function apiGetCallbackFunction; +typedef std::function apiPutCallbackFunction; +void apiRegister(const char * url, const char * key, apiGetCallbackFunction getFn, apiPutCallbackFunction putFn = NULL); +void mqttRegister(void (*callback)(unsigned int, const char *, const char *)); +template bool setSetting(const String& key, T value); +template String getSetting(const String& key, T defaultValue); +template void domoticzSend(const char * key, T value); diff --git a/code/espurna/config/sensors.h b/code/espurna/config/sensors.h index 18d67bc9..50cbf94c 100644 --- a/code/espurna/config/sensors.h +++ b/code/espurna/config/sensors.h @@ -14,7 +14,7 @@ //-------------------------------------------------------------------------------- #define DHT_PIN 14 -#define DHT_UPDATE_INTERVAL 300000 +#define DHT_UPDATE_INTERVAL 60000 #define DHT_TYPE DHT22 #define DHT_TIMING 11 #define DHT_TEMPERATURE_TOPIC "/temperature" diff --git a/code/espurna/data/index.html.gz b/code/espurna/data/index.html.gz index 0f4ed80475d5713134ac395ef4e32060d9b5a39c..a33f9a6031899b9fd3c3025870d4b7a5041184fc 100644 GIT binary patch literal 3790 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 E00h)LhX4Qo literal 3547 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 diff --git a/code/espurna/data/script.js.gz b/code/espurna/data/script.js.gz index 168524e05d2feed717e2d3da40d11d9d878263ff..30e476c910efa22ae6f2d5d103046f05a80baaf6 100644 GIT binary patch delta 35368 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

gUs>dxo=&}f zCi~t!-|Hvz8&Ey-WP0zHJDc8rW|Jr|k0#+=zi#;#{N9}f_kz2dQ?LEv)We%oZ)2Jc zHm04^Q|e_--w&P7o`0VDzW?W&4>s*Len4ZM%{E@YK-2ev&5#P7;`7RM=Ss^feRPIq z!aIlAUE_`Si|T5hTmMD3JPXXHs8wCyj;^0}*1sq8FO^t#m++@2{r{l1xC`py^JekL z7N@Sv?$Yq@O&asby;w9Ve8z>Pk(swvi$~~ef;fr&s#kTeTIH!-!?Ek*<{rP z{ta*w415l-vdNbNw$xLAWabLdh&K6RL900J(=G`7)6>PhAlo<)t>2gjRG?3wQco?z zjhE1PV6D*_Fn@MSh3UAX%Vh8me5{Ou+9Tu!)oO=z*W`!IIgLz>s~%=+By>l6cM(i@ z0B+~}Ni_Y1MsP=WQ(K2o1r|hT)U+{?el|)X5!^XSlzG_2=>pf6Y*O~i?W})#ds^N* zon}k|9h_br!BUJH{KB6Cnb7?Br&sispg}*N=EMz^nt#&Zj>H*V(Vjg$Iv3YX@Zn(q`pxoO3FR2EMM@e((62rhV|u*I$46jpgWi?2negp6Dc* zC}qVEmZy&|;^HyF4<>!C5rmyDPfkY1o3FpV|F>_%==lDZhsoyaZ@#<_gQv$0H{0J} z5(golIe&+LjQ*|8=x2?Pip{$pr_Xjn0nIF3tc8T{iRS)ibj$6AKk8DG=Areo{;U-G zvsK_qC3eJuwXSju323cw48~U1i1Tc_y@I#9(kQ_?@h#mZh?qzYs5@-dq0;B6{FuZ7a(08&xEOj6IfjLiz7LLKX@YSpTtvmqnOC_G-fJqe15u zQTXfPM9^QcpqAz3WmI(XV#B#naYVG;MXwd@-m|Ygcz9fOit6???ZyE#;_b)zr4nz$ zX@9x5FP`8%>l?oNQLB@QF17yQ&OY^ystRpXcNf2HhAIr8qKU$&Ln5J!fQ!b z&^FMyX1h8k=IFOUXk4`I;Qlv!R;RArCw~;SJy9x+-Dj$Yjwes1quZ%Ii<*Coxo;Ot5i;wT2H|ejKwO%WuZ5My z3Sw*KhiS9+3_1GK|KTJ#Exc^cRIORH^D+Lvw&FEL{{erLqSe{s?TO@2`ps>7 z(uI0_;)#w(=iap0+%Hsj=G9SaKB(pKz#h#ahcg0~a>|5pXRD?wGbf6MY0KfmCL!;K zjixL2wU#XY6R9caYLDJ^*9%P=w}0I_^{dXB@fZBL{=JT>!!L$?P=0*k6kWO6+Nx3= z&UxI{f^+m%VH|MKlSZ`hpjDicWD4)ezFFc7Z;P(gZxw|wrtNs}4#>Rpp&UI{NW zEd8EV?uih=UQA6fycc>LHih<$mPnfNb`QOXzBTVQn{S6#^6Y(5y=WH3i+{`fQ^(W1 zt7O@Fh)vmCva4a8sa-C(c=W=W#IllG)c4b}hYiAGd#%UTi@@Fk7(PTF?9@Aazz1!W zumMf+I(pqz=CxaB7v-68SWKGNt3-9EC_ zsAJjKJ2q7KXe)k*s>x0}(v@&QE4GGt?h-Ez#bowYH=6$}#=m+?EmYS)|B~!zN~kmt!@wJSN+1 z>RExCyt|9+hwSQi89ZPhD^PL^i#0Q)0N89ga4i7U z;392nSU=cF)|WWR#_R-T;*OdfWInZ;fvxsk12*auyZ_E4{eNnl3z$c3web168l5!E zkXn|WGi7x24>d(@Nzp19Oq4jN_tMg#4TlKe<4F>_tTZji%Snq>s*pJ^>flf+tYV^q zjw}{%&^EokW&zZfKsb54fZ_SZ7EwdY&bps|3e@iTDugf zxeq=nro~L<^il_Sp+kTv<1ynD=fiMJjI0Xjxl-85t$42(ALRMy?7!z(bY~r}IW&R^ z%1@~6Z|Y~nw>nT*+m*l#@>~b|b{=xCp(1sXmq>Qnc7F`bf~+4QV}Uh?{Zf7UE7#mr z^_0V&H%xM*mYs1MbzIbpI5Qh}Juk}W)-gv6zdqIhfnJqzy~x8vFjj%PYz^k{bpvmQ zU!#ByZ@m*Qyj>(k=-M}FH%)j+5f<8gPmlZdaUnk@`MT7DZ`A$IzCzorphqeqLU_w^ z{4Uu8tbc~^rzJNKa{A?r&V9~0CZ}KV->+xQNZh(8SQUoA`!kN|LuS`o{1{2*oy_tQ7iN zQQ0YZJQK+_T8$YGGg!l#(}?3;yrBc4cO0mj%y_T+#B5q4!a)@b%r!f1!BmVbP0Sot z7&3h=bDp@X^(Cxg#8QJ0R7JlpaO$?#RAQ6eWO%)XJmydPbU_No8m`@bz0;QB)-;73 zTYp_WhW|^gu%o|Pc>x^^ruAKS_F5f)wga!tYq^VaL(BKOa<;aN&39uk!K6sEcCT=4 zsO@8$B0(MQe&P7Q>xjA&uyvrP!yaPuY-#z%OD?ac>s6Crj^uM}tSRiq>H(towU z8=sHW#gay~jF!`LWm!{BmizbL#}ETwwh3Bl{vtY$ke!%4n+d>s7l_d6jAm>1KYUrD zX*{`f4*c-4PDjS!9&b$NF2#a6#}>0oV=+6A7SmwM2ZLhfN%QVCb_5J2M_1qfUMOo`}a1W9yxoJ(eNxqrnVFuEw@ah7ECLFeAt26#F|eXj?>;p-bs{vp``#|tsQ z^x&JWz$K~=9lh<@Q$+>O=Ws0-96GXVZ09zEoDEfupb0dCU4Nx7x=Jfsum8n`a(vhf zz&Vd97{Xq61!!qEweOoc4PD(nOl_Fw;Mk1ky9&Yp+>NQzmJXUAR%sMjM1wYDcr!B{N4+bE5#-^2|(aQNBZKdB&9#q|putkf3ZDNTc2hu;VQ11F5$!?2y zk>m)d#QXj%zsB{F&^CTo#I|1L#RAvKWqgi@+n^@Iu4hr{Uk7z-<@)S0fh0>5l8C0= z;wg&jPA;6_b;b&&th?EuyO`5oh&K#c?0CR$W5vfK~bRXD#!s(^VmQhndCzeO@7ohtusfhq>*s zuYWv)M1PsX&;Cw7VxiS7VX^P?9Be^FzuDSJwh_(Ci50K~;U`+GG_bS372iinS4a@o zQeQMIc{rxZPtJ1oG^4BJKyOqigQ%G$Est*eN~H;6y4JY=g@uh}LXFbb&isNMdU7i* zj-+GY^VEuwqg~3l9dWlSToe4a7ZIF7sH4%^et!iT88_Ai=pqwhK<83IE_MD>s}3xq za-Y!jcTYHxMy&EKXk2Zr=#&Yj>mmLU{y^55Dre~|dvCG!I;ySi{MhK(TtL)Bam69N zk*Zkq97l{bi!U3*o~XT}asy~ecVou6Re@`e3&}CmwozA)En^0kRF0C7!*SI02`i&m zkAFhB^`?;;hGs(&LOY)ujS0b!8@IjAPz*XIMQ&6tg&1LUi8Yjf@I~}G;$a|+ju8}S z7Gk~GASLi32>gWgW&}(h47ztzz4i_*)X!0h z1I*q?qg7S$seXF#oc0AC^R;j?#^Ry5W@Y=?uJ1%m)A1=+2hbdY?Lnp>72(K@j;*DW z^&^#V24Vf!TC;?Og9=5bPdsV$!+*&Z-XHsj+v%)>yK09VK_YCnnb%D_>eef1XP1#} z1EIrZ8?PyLabkY;*kXaXf*P z$l?EexUgk8VWYmb)l|X;gQQ0dmxPH1owi8{N5q>TA=7jgw#xVnWrzaj#(xWUP-bJo zI0&#Cuhze1jq+Zb0+smM9xG!<>t5@d-25<&_{4w!of6q$wN&1~<8fg^dT;>r+6<24 zt|2~i9YaD>m2NFP4-nm)sMM7tkI&*B+6(S=~8jlQZHW&pgYg6QA^yOr#%u85EI}6{~0BHhNmpy+ZpFKP@ z^k9!gqbex*1ax{9X!r@johZ4PRh-Q^)x4ZwyN4?GxeXPLI}Ub> zBbY0T%4p7bmv@AKOC&EiVI{SC{K<}x$CVR(xeA&w2{{0EG7Bd9f~EF)aICT=(#}ni z!bz3b?k|yTmlUMx4dAI>xHNwsWAVDSMJ(63-Bg7I*qxp z*&=ms^R3qt<~Qn@R(18p*Es!qGfrcdh8(th@#1Epe|eh_4x)44o_Y0&4KAYDPkSjE z9h2sYBP80OslL?MY&-tmjgZefO1#B|pJ)rXr+P=KNiR20%F^u*S(SgJ8+MR55>I4w z%Q{3V!lDwLuLjpRDp^!Q^&svTH*r0*6t*13aqn{& z$Qvp6u5G`uxpgtRw*1uFwyUdWoFUgS!3%70-rYMfeD~F3gXXL)P&XP=6m~5kf~~>@ z`^vVhK6Kl(wEG9q(f()4@A<|b7`FQ*9ygl$Bh%HkWkKCi-E^+bwS)n9J>8+i2XtxcaN9lT!!>F#Y z2~}ucIjadbCmfTTM5L;zXpd*PVW9N25?&GUoDhlDMmq^dA+zj}4-=@sJ)se5#(yH& zTtby}Z~)3ab%ae+prErE>7k&p(G58yePGDg=X7G8zUhA-o#Uu`9H;3Sy)PR^#>9i| zBFP+wej{6`K7viY4jtZY3I~h9NUWpQ*#|lxCqfWR64GJTsZXi6K5t5!kF4KMADHNB z;~<`}W{h>e%!?aNe@71l=guft7MUT^ z8pmv(N4I|sbZlWS){*F{M%y&P(i~N_(*7Xv75wbxqssVsBcI~LEa~Cl9GYm(_Ys>` zLso|y#Ob&*h2Sp?Q1B2?K2{ub_~sQw2sHUhs+rJoUO>7k|IXN z5IWvzX{;?f1ghx?tO3)(U~4|!CZX&>?4GS6W?FwCu5CTq+T_M^0MtzTTA0<92q`KR zDa7XTjnW5sY8NoXn0;Py3Sh~7zK8^~Vjr9svnsT<*mO+$hl)+Ic4l>v)Y14;^ zF@YcenAI=_TFqHfwB6Cu`Oti{or%b)*V*4>Ya>?88>wDQ9_5b1emeDZQd&=VNP~); zTQz@9K`4f~JW!htvQk}m;mrhUaujIcAv@~tR{V_(4Y;DpkL%&kQt4RRl})`FCf0+m zD<5e|q*LSVPFqGq6X#>?T6~r4wVp|p+I44Us|)v_)5(nLL_m(Yxy?-Nnml#lj9fJn zM=d5zNfz-lE?5aPYv(qnFC*Z1XHu%M$qj#0H)xVW10|U}(LtU6Hq%Lob>iF1nVfr2*(3|mxA+RHuwylP$H=M(w?h^v$3aLUe2X$c(FnsyB6lOKBB8z=U zkW3uWtp(>~xJp(~^k9WV$2`k=o^zO;mh|)GY@Dc>C5%t!f+%@97Ylcy>Aa$KpN@Zi zku$1ikOMur&-XwMfhc%NW>E)Zbxu|>Ny6^E+bmE0GJBn-F0pe~&^)ruJDk1>A}R!- zqN@wpVY8xxU?R-IZe9f^;xV#r!2+6ytIt4A%n>oc?v)c6HZrig#K3-Yc_z7d=n1ac zNxq2J+wEmo2$-Q(lhnLN-B7w_Vv3P<81PZ#P%gJ7JPrctcl56yqA--D4r zu~UJ)czef_!N9GkWKrBaY#Zu3ju@{Ku{Num98LH;IL6Z4yd2%2DPA&q*^NmYgjuml^wNwEM`Ol-G&gyZ9TXyBg5H`E>Q zEL6RH&MP~Xan_VcyK$t>a)2;%O>AGQ^8NadCwE!^5Z2rKMkfiLmnD0qdMj;A5XNV; z|JIcppXCLNFOSEp(Wif3rQoLvV$$d@CO#bejs9C*!#U0kMv?@r*d(Gmz>ey~r#!#J zINE*E>54-&jpIu?ED~g_vOQZ7aMO+pN2_byOI)G3aO8bv0c7+g>mms=5R|+_O~ICx zPKTlq44E3aCK9%(*rCt%%rT%s1(|xPax8*X2K1Wz8_e_9fV+QNe6-bclm@^%vLi4R z3sj!ORXlafTa}Af%ZD6kMV(k2#w?eW*qL%NW+zD27ik>b=|E@VXwg>r*67i0l@jgV z>2`BEHPws5 zpAVnh|F6eS9({lFbnx)$H%|tm(R?}h+c%H?I{50VufG2J%dfvWrvE(VSh?jr4hF9B znL9I;Q>8b@{Lf}#<5u)~K;>(;OkOmbZ3yD#`rfeXPugW|$%T#@@arFFPVfQz>mO>8 z%Wi{~x_jp%PChWi*U$p<7)i!*$Jz!|mQAOqRAG*A%LsqJ&^qj{M(oU3YYBk7e^oDq zN{PMzJe4?$Z!82v3czv;(`%wwkncHx7zZta2T^xiRgzg`x$r&BPe1aacNrUdF4C-WO zoy*=zFT8*Ik-AphNhAH&K=ENM*K!B+8b(9VLPnerVR;SjQLX|m+)gTnfHR3>{xovj zPMW2f+CikMO={6w!lsON$P2mKH#+_!-4RW9tT>$1S}9E&6&@0w)b_O82`=|-p1&_u z;W%n7iL6`Nj$y!M#1dfU7|!b^lfi(s#6J2GX-j_`6ZFNci@3v#?{k)Jio(P>iNP)i z*rM`&V<{Okf?$@N&US6tsM5>8B|JwYVWt`aNcUFUDlyz=q%&=%7}XsJqOY5^X9#M( z%T%VE9i-E>hH41+jl6wFrO7l6k`H2TMTVJf@2m&e#IC$STsHI`vypfjc5aLo;Mh$# zZUKLZ-ZDb3F!KF4>k8vA))zXEQ*W{k=>6T@+R@59m2vT}&o-oCR&5Hw8NhcXEv^|i zv4JhFM&W)DH;5kqP zE<-G^e7`UB9m8l@`$?A6{lX>QI|wuoVYb@2VWB3>~u*chBm_iZhBV0=dmjTNob z_Emq1HlO_emoy)!aC?KZ{CZF(pFo6bWgQ-k2bcM$eed=>HpKoJ*5^;t`c-p`-BTOr zQE>Ih`x=u&tU#~D3ha{oHEP)!TKoeC^d8J6AbuD3PKTS|bkV1qseC%)7j(M`j&y&< zH}Sd^YTg7_528)rd;Jd_=zkgyKK*5O&jw6?A)~4HHeb>0Yb-GRKFX^qzod)j;CpA{ z*R~a@rEj4@60 z=a+F%a*A*&{tmBC%l?u0L}z7uc2;cSqDtt{*u=yo(cd+-yg6GWn|YjljLQuWF8@iB zvf3;ptvs@BBypObZ02dMbkgSBwaYUrujG5Jh55Moi`1zGjyYOHik*Vb)wZ5`B{iyGw6=!y1W@X zUYh{G!d~r`(Ghp6o!Ec8YFfCWT9)>J_6^U>TswU&uY7c?=dOno;KSYcJ> zGG3|Id@+MLSbV8osbfREHP#t z(rZR@s9Uvkx}+m3Pcocl+JKhEx85x1Y@t8- zD%i}^Wd8n0{H*KfQK>Xm{Fe~sS-LJD@cbtin*K5*N)CV2;m{urv`8rv^YK1S* z9)5|?5kyQ~s!o=Qv@&-pJ?fn9RhxKQ-*xCH9j?L((l{OmbIjbRQQUFd2}L6s|1?_! z)GE6$DFc6q`H)6&>1fo6N9IQnRG&&pMTZ%iB>T_N%c;kC9<4)isT`93sa+-z5+~4s zp3WoK35bixhxLGJNqcn;jaz3RR-F0Lc931-;&i&2!E%yX@r%itCHd#HRdD!+>)~0F zEp%L`mDpc-5jm1McKTk4oaTsIIafmGMCe5gGl73q3)LbDv%F!^%(GOe`R?`wQ)Y(% z2|Bu%h6yjU+v*UebSbU*GK#5xSZ{R&ORS|G;NyIqA!#vusjD^OeW?peh7-)r5#p*h zq84^ftx1Q5az*ft>@c#K9cD)b>Gi23OeEK61IvP6^W_C05VHXcz&9U6YU+?Ha@i!_50$O^P4I;l)tII(=~Ifc)A4`BH;?`8kj`2^&8H^~q$>g!)Aj>X*@+OS5Td$stW!K_ihm&g&ca zLprrdIJ}44bpgcAKCTc%l330>TG~X$tTTUzmQ!eG2-weI-!e42X0u^&43?RnD>x_- zuTQ~#JhY%s-6S?M;c|`#jCb#zr}Ag@>gT*249MiLmvrm2U}!KPNK{~PcxbMy7?>DG zR(jX^lm&GyE*8X&qqw;?6)uFu!FpR+Nx#||l3>afG16yDeo#m2UE%1R2X;MGeN=z# z13VaD0oBZIKZWE?yI~>0r^%gTKC&wf_7t3|IFTC5H={G|+Yix{COVOT&AFZ<;~O4K zREqepB6xLP+6Cki_RfLhaLS=7%4i-X5w|bC>@_oD{k3)9`&rv$ull>k73N@7>!auk zD)aG0v~s>mL%6W|f(yNupmuPohF*X0;P~)RBQs<*cWC&D%92M<)WWXBj%o_-b#(C2 zBE40ei;Btp@Jr#$L*!s|dS28ooI;cT;a+6#540*vB5$>{7eUE6co;&0=*uW!n=Bt#j3%i=X<#gQi4fyX^aw@#r4oIQCNL*t-rTaVF~H0iY?%-CGd zhBN1c<*|xy=p61fX}V&g4YcALGo$n1cH2=>L%(fuZdJ=>Dz`(<+N zxBM}3BoydDEuD_{CuhK0F3KOoBenGTlrGZ`U1$GWYH=M5v>>1s+La_u0DCXyMXFkLE z98GiIU0%vXf++Ky?Mi4*W(s!8^+YCvIshONWH)Hz^bOGG@i^0QKr^OExW7*T&wbrx25fT_y|BO{RS zF({5x;%8l#HFn|D@i-bENo{;aHfSOOxAoP) z=Y^mj*tZj}zqy1&|JDr3iwMj?H0+NS>Xz~?-b!q%u=@P0d!pFXT6NL3Ep|ow5 za>`kEU|5DqX`nfH1}8Dk>5sK`sc9E;2^c8mTI$8exln(Se)ZagH(SNNY=qz%Udk%| zr@UbSuD%wrb{w3<)=5|7#Qm}&e^rKFoK{rs;EZXR@?ef23@L#6U|tm|UNtb{fzs<7 zFm8B7OeIV__)`ZQs7o$%Ii^kVSatOPN|`rRe7QRKNW{s()kQMD@C-qVd=q9ZA-JlS zNB0sTD-M6=I%V(yy~aOWx0-0h5^;2v#&noJ%#+DikxLTmQMy`rCZfXq@j`ZP#U-`(u?@w*k0J*7>c6 zgk0(^$x*FLvbq?ZV@TR;Ec(uQe=GPJ9U?c_efs-l_!a%VA08`c(x-QL3uAZ0^j7Qy zCy2AmSKR~wK$EImM*(1y@?0?rdN8P*K7R7>?ZcBTT`vWLJEM~alVM#W0RxkTT~Yyi zlh|Ft9mH9(3ZDukE$rH)PYh<)fs0y3nLFizj$CcJ$Sae#UNaY`Euyv2d95k8l&1~| z((&wX?MH?Xe{7QhUlReClNDdn0c?}$UxooPlW<^I1A6)7lgnU?5`Fv^kB}&$Kles3 z0psYA0EowvX<;n^E0dRDLjf$4+hHVsUK4>)+RPoWW%qZgqN@!*b?xU{(7y$odKY|A zsVnX9t9_Cy^|3DZemIO*oIbR#1Z=!?wY`viM?Oc!u>}7vZ%HR=ESZORRl}h&AJ!NRWic)Zy1020-5u^w0x;UPo0` zKWBYbSu*TIy`IxBKe8}^P*-q^rd^gMtH^Vp982AFz3;BV-QieFEcRf~Y&PU{uUYvs z)0MWTq7~NXuY?aj6WGUr69q#9MxHAN51W8NEs*x;(=Ogwv&w;n@rLn0Y1% zdHy2*^k~nkcZ`heTPNJyLJA^(q0GAZUDA z^-aO4SI;;$&;~#f-1&c+`}Xg)jU?UQ?_VKdeKmm9q$tO8cP8&BTw1lSyVM zt`8-FGHeQ9K*+Kq@_)beRrM1974OXMId|s70vnBfcXd^DReeV%eR7(|Z`y2te+~5z zzK4WlA*gfqW?U!%&N>?>Wdz$-Dh2Ce$;3V~JKrIwTC!2Z?Gt%RFgYaW1j zlJRv}N$A)bKjt6ghfDWT&3D-xoz!KRw9@~kQQlW|;`|Nf`8=H;M#Mv;tJb){82-nf zR$ou;XZzQu#+w6Kn(zsOTUa||C=b>gD1Y?!ue-yy6eg<}H#e1k7`9P`qOEM?C5|(B z%)$zradROr@$Q`WBM#UNGXx|{j4r0de3pz6&FRA~ zkz?#4e%+D?W65`ahFohLOVU)rQiX^r5`uWZEIh}=f^QydNle6!Cs9XHNzD0umx4gM_KNsb4 z`9e1XKbOUaZ<_knJip=yfD)3#4ha21faq`Sui6zLhcQ3U8mJg#kTz_6%TK?3$G~)W zLI^rogj1_uvdh7pFf~!?aVU9Yo}x73fs`Mx{`cEpl@$zv!_i3P`PovvqMmg6O44`KxWVBiROBMv<6Oz5ony`H%(R2gL!k%WlW zKveRhCDHKjV@V$RLh4U}@Yl*H%yR z-#E+RoYH*Rshq$-+pU6{-|{AJ(J_P{Y_8`^Jp~97w0GKarqh-W3)%W~zUPP{?PS4P z-{nt0;1&2auy=unlEWq>1u~?s7n?@HB@+b`@cCI^f8}*A>(VIS2BXT)^@+9W2TY_T zCuqVmvzJs^aX|+0;O!?$G6ryTcnW2g*9rP%-Q^*p0?BZZ00B*ehQCax>K90 zL(7e@iIo)w5tr3{j%yc6>xc4(qsANJC;KbqTj?Ad8W3|nq?nK_ura8wu78$8h6qHl zYupa`umc)+0M%-^wK2*!&s|Hj*D}g5wU-C;rTr~m+c@n%-WElbJyXwiIR-sbIC=+S zSpjHG+Q6~bly$w%Dr0uHhAK(h1@jLZv_o>}8{Vun9wzgqHCOt#@^HaEi&9>7NfI1C z-cXa+e=}g>D#ygp>b+_DcYk`^%avvWOnaT*w+1^A;-RfLyiZ)n5&L?pNQfOo6~DhK zzx(6M8xd=?V|z^llG!1fdSf{=cl+Hh9~Nq&j)&hFr+6;b*apz>#%54QZtxR28Qrh2 zymecw^d+mG)CyC+MpPGvUm8>X_r5W;uLZws>o3VM)}XgSy?ocZrAEXC2U(%-n%VnGYLl(pcZ(1n$2Z|8ewe-o7DLf@bX@ zKfe4fIeO$dB1imf#C0t`WZhM%+#KN+7|ZNctDd*&t5yPq3c25a4sSqw5g+s^y>nm( z4>&I97e>wD`3?W9?phFNgzdPC9!BYOm-VnQ8bfY=XO{Yc*ndcwJ!K?m_?kg+J)^To zN>6)?hz|>yGaE)``N{X$$uWZthNN&FhM$76^@ZW+Sb(@K!4aF8G4-3Nb-_RC2wmYQ`!8GF=36YHOh*8=0&dP#@%*bI$wj~&Dv zKaxK_0t`0(x?=^U={i>(gcSXPmPonn@S%%2k&}A2r+)?S8EvBU8a9_}VBu)3q?#DM z3FjlMiFj`ou~l-mTUBU>4*hPIjz$DhKYn7N(TI3}B@$u>iAe0oewI#l)*DQg-AOoy30g8dWlXtlK5BX;y z`-ydzWPiw`7SQJRq5X&xhYuG6{epJ<@S5eJ+zdN+NF(+^iJTE&04*b|JNyFiX@d@a%c8|2|$H4)= zGMfu7lbnEMCKFp}(EHj1A8i!uFM$&;-eLnNLVq&-gNNh3i6v&>dF~rnRdvP-cw195 zz7e8jxt}uJv(9*zxiPIz1qP%ti&L;Y0!SuIv&4|$(UremoHAIBwQguz8QNBcR@(<- zMBABN^H^=1;6@as*wJixG$pw10qZP*}<(@Th(YptLwyk!n>83M-E;2%6PGDZIh_pt_P-SrUbNro{IMIJPDzO^8y)Zq(ibSUi08y|J{Lo5`+o_u z7bFfWNz(W~lL<}yx=kO^gXO#*PbTJAr%g5YV6c7go>thRke$I5Mw^0MRr86v<|cbg3B57T_FbJIbU*>zmY5v9nB?3LHGr7MGge!MV0OEx8wJWTEo{d z)GW+3mC$IHT0wm&XmMd+_xJm=Dghu&7UG9Zs7susQtDHi(%856F3|3baDNjc)?%=y z3FQ*=u(s1f$%q2@2_w5}dJV)5?ga4oH}e4(%by!Nr|(HrK5LGy)ED$HJ^S!za@L+T zXVuwab|_%N9@kG_4k_a}U}eY0wyWLE1xYp#+lCk+&G|MbJ&z>5tuFJzV^1n&^{ya; z3CYgdZl=kb49d!`6i4ZYcz@xkz?;MX?80xQ8S+8X3;@)h1sez@a&S?lo(59K`R%0} z51cMqbnEV}Tkj&@sp?dt%&ePOpEC7u+M~`Q@?6l2W~dF#y$!hPHvqx{t6*LANnWLt z+~^H8cq3vi&c5e+y3_bHe$9>lO znOj5SDOeYYnNhU}7?1UkQgV0+$zY=tz7*nwkp}-W0d^-(8aQX< zXi~}{duv*8$Eo4T)iR?j`8uWK6tQrZCrfcLMYPoAOaUQXSGCzJ0gJ0Wi9A}9?^l-A z=y4XB?NI{ro<-J}K7a7s*~~kEC$tV?ejb<*2_zDuNNb}O+MTk|NJ=*B!HEuq+gksz z`Ow93;HK-1Z7mzmM5$-c;Ks0gbB(M~W)1F~Jg*;bf$O4p#g=Hn6{lKYw`gP76lzab`=SqCKcV8+x(}4dv zv^T!m3GLl$TYq@t>a571_JEyPE#_tP=MnxSyMdg-$*`fl^kpjhjMl8N3!)ap@Cym3 zyqIk8{Bm$b27!#T2|^h|+vu(WV99rI z5V{LX)b=W?r_JHvjHqR%m&(`W2Uh)8YS(O6N&W-X@GD8B`!8CX#<4>8k5jqevW9Pw z78^&=VSkm(GLuU$E}r8klCbunA~*T;l+qEJG6Hb z&`wH`N#YpZNM^!pdRppuE>3?0bdzji}HdqN})O0QOiHu(D_f7Ti z-r}~zMSwY)5rKJ2wY+ULE#b(xR5e%ocE^g+H%tK$Wf!C>@dh_sLj0cjzQ9*Q%eH@#(qe7h8U1ZLE-z_PIo=cgUetASxgNeGJLyhSJlLx?VY@j- zSbyTiI2L=s}Bkjv&LYm=~T_Sx3Jjm@?gDQ{Bu0m-foV{>jdjP zg{AV$k7fb8^y%D?K(u}$=Y!0GS7F$i`MG#Ve4@}y`Pr`C$<1xfW}x3yEpBBdNq_px zm?XMriJQ&qejlm&)DE;^Fd1L;4l)Yo z+P731sO?{>rgKB4*A4W9#r*O`wSW8w&!qW>?|ENi&Whzy`#0%dFU7_dpVYVXszQ3b z)K53-;@Nyf4~vE-_)9jzh+; zXyi*)Vj2ZzPk%|hIK?{LrNp%U*Un7VQ&%Rt1x8+)S`%q1iG59FtA5krNPi$e?+7m= z%zW#c|G;$TL$Cm5^4WZkK~UW(ewV1-RifV(u8qMV}* zeaEKm5=W&x%;eX}+8W}Q#7h!glAuCNQ!2B&)w&ZqNc*?{j}Nt1&;6sl--}KY++df76%VE@NqgpS!`A|K*1dMd;cdWg#==u*Qr+_DIH35FA$RXS+mgim-~R^6NK z7KZ&iL4d+rOG8McYl4nJFXFo1YTFhEkiwotGJxXxMhnZf-@}X)`+uX?uk8W)`Zenn z%zVmwJ*9}!EJC#0aqKQa9EEf!==D8YRbR@t8P-^VHD=a(Y1aGTGRGDeTE69>*QuyF6dY14APq zysmg*^whu;&04_9U$Io}8?EE}%b`YXbY@fr>Ahrf&8;p!*QP zYQV-K6wsZV+~c|@vODCGULtQ96Lb^HwoUp09p9jf_k`SaeTHt?bsK>%3mXCIYtX)B zeZhu|>Pu&*p}xa4Vt%B4roXXDHu2eXb4P!+n_04nI1Fucc7KL*dA2z_o8tNCd{H$m ze%?&aF6X1g7o)FdcaOKp!{}`NFwHjzPrF&nY1^>z)@GE)`TpeWGC8|EL`>Q-{oN!e zyZCB0Jsiz)PLNZdIom(KdhtAn#sH7sF;c<7;;-bh5PV)*4B1P%ja|TF-B`LRFCA>f zzIoBu<^FT%eSak$9ZrmH*5t)$CUz*i z&f@61illI&D`VFq)s2M#gY1gjnMLUMT_2QXW zBdkAG7g@yf9?@@XXnGA5hYLto8A~2tEzt?Vg>Zd43>mn+4Ub-w_{Kt=0i}9nJXIy8 z=fBbN{rBvA{ikm?!CU@2s66ZUL|Ohsjx!KRCL& zR^J$z{C}JX&1Sw@;lI(kt(*B(u^AtZc)zY`t~QHuS!{@=Y;Ma-#1BuY(m(6ZzxnFN z&9`5D@f5c1f8)T-M`uSz>aYBIdUi_%%??v+4ZPspcXpKj+w9?g(0-BUlv+hE(ioAf zH}wA__4gJ&Vt?U(|3UQr;ZbA&Afty+?N-^>Eq}{xs;#oWwmN2E74q(KM*5c|Jn88~ z(HYY|BW@jM<@$RfIm`4Lqdl#r z$0TxJE$eeig;iryUl8%-;K1~gSCE7+U;#wX*I=br3gtTbFuJ|H9l^R|MAfkjQCyxd zw0|P}?tc35>(QSgHNUthR;~67_(vWVgFSu8L0 z$B{{-#jEbY}V(Dl?K zNL#g7&f6Dq11vLcX6plDL~QMvxY?ngYV&X6Zzu&!khglk_NCv%om7rIj06{Q{FnIe zxM`-eADeka-ELY+`-@IJjotSNDBLGGI>H2`m@PnTt_KD z6rX2*ju+~b9skd>ztQtK{i=yc&Ev(mT$tTOS#Psb5WgSvoPJ9+(b*n-PkV4=vLPCZ zM9uWKCUCWH>CJEf*tG6D{F3~MdVeGhrLKWAfuCLo>c=~TMAPdzN_BrEwY@e?N#l3f zIZP(oLS51m13iDF2PVlhEc+jEl6=hGDOh5^BYv4m5E8Cvpr9Y2)0EnA%B9@E?a@54 zE;S||`yCwMX|%2YGgvR*b2QF)rKXqaojRQ*DPG`kGU=z3{&D6C1rBQbK7Uc~&1Bvj z9K5ldSDi<^@NOaP0@u#QSsYAY|JfjiT zHWO424kq{pkYupg*!FyDTI&bkz#J;ZCHJCTsHM6=OiQ)M+%~#d!8w9N+7->I-lv<* zLcMW6u^#+r^y9a`0yKd=Wq((~tT6uF6^eaYENJEcu&A!_`;=rjg!+JwNOb`)-Rrvf z7-5(f-oSG>+mAS&&hgI&64Y+9NYLJB38~ZU&h04LOTFbg;IT-2&?6vv>g!;Z059Qb zM#q3Y;nvAXayf1L0eMMKe*|&xa@sYm+ImbB7CV~*wOh34i{&zG)PGQ9Hf~bBPOX0M z2lyPU+g>;PYnz}6hBy5H6PD~8x18sECt9eF(6ehK#I1s0(3F?OGon;{QVyt7QOII^ zt#f3B`PNO%@W>XB4j(=FAbJzQBzX^6XZl`{q1)L^{*87TIF-Zb=m=7u0MS(drG8UiA{d6~ zH5WETvQSPCKa8}~>^#$K*;%$479ga z_Vu(D(0`Z3SR$FfBB_lRj{7M}))n!ZizJgll&xgryyvJE*VyJnC&JA5JP1WZFKoH+1i;$`TK z;+?;yBazOIP#5;wL}KlKpk`dq_axkYgujz$C4b%t2f@c((!Ocx+r4)uWjs1o?~<*y zF~zUwdA(b4X~^)qJFE)Vow`>Oez9za zt$&v&m?$Qlo(|#IY8hzacv9C9Ly33&p>Lb{ z3Q_+3`v3WREnQl8r)BQ4w4;T)=GRe6dT2MvxcD`mBn|}N#AFNajRSNOQ@U!qFdi1q zgth+x9*HqZbScj2A}N9w)?RXJK_n#7OMk%YVF13Xs$p=jPq{DZjwTn@AJ2Pkh};bj z+#!rQ1}=%lqos25LRUX*d`-r`pNF~Pz|hyHuQc;QmO2w3ablM`!d<^weQ&uS((;Rs%|F6Rh$Yy4s3SdPx3YH2^j#{H(nJz%TKsekc5nPsBw{OO19pj;rr00?_x@`%S9c+)1^08vNO zEFJxt+%0yXTEY>B{G?s`v42BWg`^t$ ze%XhGzOr!a0eb*AHyM4CPFqmN*yt5#J<63Zo zAs=Z{prHx(zjlB|3?$+8FL1Z_UG+`^4T(4z_+EKcB0XOD?!i`4K)6LK6)aHJx zb#{2TtU$-+9AG~dG-~2-AbYnWBH$P8Um-EH&)4hQy14`!M1{z0O5#9-yhH{g@)7_k z)LLEH!2#9T(Xb7r#eeP-)ERWVoJPNlwD?8>Gy_OMp)#1BMZY}z{@b=)=@*eF5tfNv z!zDEn?HCXY@rrM^=OW{@fk54|P%7b6z#91>GtAI0)eS})DyMR@fx3V032{ln=&P)> zxM3tsBO@ppmx?p;J%CtTh>bzLqT(O#hb5r{M()HxaCajCJAda|gV^^|#n4D1S7Wq>m?$ zwFrO%Tapv7I%)@93Njf-kJi1wxR)|v3QJ*GvxHI>+@-`%V(D~aN zP?3-HRj0eBRDXNHRpyTL;^KNR{lmz;32r=fZAWM4#4(}N4KgN@%-5X3-6Y>J>SA`t z3nImRg_8#{J^ZdZC_O6$27J2(6BNA~dvl3~ZT}eVPN9lb{D3o&FHOD3=_+&tZ?Hl( zQSl0Ok%8z8;X?xKd4wkp#{f*z9{SjB;y4tVz=HDX<$oq1V3K+&cj9b*Z0^T9?>MGG z1As80r!}7=z&zj*(A}KyZ)}l1{hXXC#%1S z1HoU%IIpQt=?TZa2sEm3V|rjlv@1)6Z{cefB!3`GaG0Xjc?+V8e=L)^sGw+v*;d*a zv9_b$HzQ|s?>ak+XALt3mhesWwiUCBS=Q%5=blLujE-;T>%FRO_pqAy@_$Z)fHTG) zluR^>Fna~yH2(36o@dq~ajQymY3Hv)i&T({pSp7qiW@j%QhBv%1=_kh`c-Ezv79wX zHGd!y!pW&=vg>%x%Bp!*8;h{SX*h$h;cR@uk$1ZKRq&d{fGJ!Nn8FR3JL~&&oCGb0 zm@iJm^YGLnx{|<-sFe=xw9Kb%hXR){Xqv&d?|n==Q;;__UJs;pbe#slK!4JtzEVc3E>=3a8C%btxW}Qs55)^yS%0*V)sFaPCmya53!4mXv6b-ufwRL&|yb&N`U^OV4FMY)TW0S~OMxpFA>@<~w) zY6W$qxXT?{`YWQB9Jxip!TI8#btnW4hjwIZw0_)a(Gs$|r>GNmI|Mo#7Z1wA;6Wi| zl1A8Fzy>4&Pf=dr=(u3!PP{Ws_J1rgFsz8smtE?1#5w?}E}ZRg)Y3~?GRlIw$zq2; zg(?tAt^B}IAhL@@=$(%FA%3Z9N+QDyX7lO>wz!wJDZsqTN(Ns=C^N?KN&(5{1HNoF z?AhRwmPqRFHCFPqqG>Kv6^2NKQ;iN*3#afEUlk&b8~+-?lWTQ2cUjOB&VR$GVDft; zfMGb&BG~j_aR{OuA~>Sh>xvgiddWk!&`!Xfp04grGn2AiUOtYA_pp%%E)4Vpj_o?~ zvC2=JOT-;xMXIGnwy?pk49Z$?4P!wP5Ur{1vg!=zVSqM3_w(2b2V!Y*GYpl%balr& zm~;@$EP(XG4d&%>$YFl2ihsXL2RV46i7B3pz>jeO^oy4UcTv`p`SO?Umxw#=eTtv> zcAKOg9M5V1@3DKC#e20oxMe4@nA;+HLzVvi%}Ku5H5_TI)uJ-XFZI2MYW-9>TwQDe z%&MFViNG@2d@xqWfZC1CSp|ZGhEFg^I03r}ME2@uCF3`g5rgG&>WPw_#HD|aTqgzb zt;f-G~Y$u{d4uUwyD6z?op_)S^R&E4?!VGyQ zhPB;x!lT+HmPX#{i#lTCUjq2?vTCa_&@+S3P{0QML;X<;8 zaa78lV?I;fUQ}G1{(SEI4s;g-2?YqPDwNr6K#Ev{4Ym%Dl9l#g`rWZaWeS9ki>Q$J z^Q(aYEx_2%0tbG(3&BUevY0;{Pcu3!jP@vn4PF}6H!UY`JE2Hr?n{4v?%niu2NLYP z7xUNBVh$$`hrLqcr#1XH*Sn6Ym%Z&+;NG(GW$6>g0hQ~YiBYU?`4VsUGKz%IQvYV@ z|7DiQ+_sBI+dX0!cj21Y-*$`}UX~nbG2wgF1iwrM*_|ds_{&vp!}_rYlius-+B|XK z7Gg4wFz2nR^U}bZ)^>lH_I%Y4reJ4?AkAV*oJWZTqi(9+C!G%#3;RW~WsAA1;h6G$ zPF9DZ$C*6%mHh@L5WT3EUiHz#hmkfoKmrvFQ_hEx4re1{0B6PlQJ%l@7+%UbuP$E| z%Y~NnXg_=ji8(UgI;lu_iGu_AJDy)(n&&u@t5}44AAsjt6`FtPd4pr`?O%~qUE%qG zw`QyQw5}E;xrMmL)}!tL9|O!iKgg~s2|TY$bFcL8H?T3MUE6ZtJ)MXTaV>)O3lij~m>@uS zn}wqWM|Yi~zsDU+>y3Z-c4}X-0y2C#)-@tXtsanW4jF&cUa{l#d@*mzjG3rl2U+i( zA`T_N6OjTC$o>J8zzwY$4++8qp@N+aU!j#aiCejt9x3$UmNT#?1%li`Vz22#5}ospD!cZqO9|GB@oLd$H_fvkW z%e4)wyrZ!^-W}_Ou#BPMzPB`dAex*#c9hTl5D8e9Z9RL*A4Y%JJ9=U|V&7%1pp_LY zG_C)BeqJnTkwu>`@zg1v(};UDr9U~V_VzwsujYT%6P<$^Ew&b1&|&oX5x+gFK0o4* zo#u%+$*@_j5s1~21uYW2A+tKH50mm6D22pMjHHy!26H+5C;oJxil z)gL}T3N&x`^#4=KTr57h(8?Rr>b@bBUDK^w50|{O!}*Do>#x{l|NeqG%kLF0d4sYA z-1L9GqB+N>mJIxEe}4{csh#Tj*HA5ps>ID+xA&1-BAd;m z+x@3h`m_4Iw}5@V>O|}pdM(WSHk#fySzI0VTQ(Z#LUJS=2Ugar^0H73N2+QNL8Ezc z%#X)d(apn7#@^hDPIDU`cmA}O0g->3UzRuPZFrDMZV#h59eD+IbFlVm6d#Y8q|>-< zZ*gICzp5^W2KSSO_TXUmVv}K|23_IuAg(@XoISnB&a^bef>_-*yV!rno!{9P zJ>a9jD%YQ@ymQ=pA*4K^K$3{C9D~w@9|S?D2EHk>o#T*)bAR|Kodny*;`H$rf}GCf z8zkItL)px-sfvM3HT=EaV)pxBo9(Q0=o{X6#i+CAIt6s*%ooK5@T!!tJk`LUYRI!- zuc8Kh!?s_bl=>F8@~+PzNDqJLpWSyqtp>hQ58_Mf+#_1AIC8P9S{xO4iU4G|Gw;nx za~=atz5vAJ#bR-9KhN5kUM*K+&3qm%Xw{k>j(dLQrUVOP8irO$y` zW>UM8Ik7!=%uq+La%YKNefASs`J>0%u4Lk!O@+M9hQ2&JRv}w_aPWVit3LVs-*JOG z%04q@>}Zq4sAT%*=o6)a~vBGKa)q}AP%ZRhdYAu|8OS=+735W#`UCck$q_sUW` z&(Hq?W$qs9ju;Fxjr~KLL&$VbMB9=+kNHn4uzRxHk5Y?7qxy(gN3_`*C~W-SaL@=L2U)>mXnev!IW=~T)K>434-O+75XC7gh= zg@I36etyH~Qgna90a~ig9st0JwbYF<(5dry&Mi>u?9u00+i5@0TG4LZ*i}{|+`_?u z;ZADU1En0@51@|dNf@<7M%EkbowqC+WR0P6@A&)+^zrI0++xSQ{iG|oXEW&>yLXb# zuyY#)@B4d5zrJq+1zGOdJwZORb==B#0~(nF&Rf150@Nx8&BZ|97U%04Afwv12^h!M c^AFF9mVUikpFD`c7bPeE4}(p-2Boe60H&T$%K!iX delta 35360 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

AdV86C5=M`=egs}QP4%s$o$Ua-Pc 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°qxgH#4KR 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 U$%7btQF8MC06Y`!Y?iJ80N{YjbpQYW diff --git a/code/espurna/dht.ino b/code/espurna/dht.ino index 21e8f1f1..502bcbdb 100644 --- a/code/espurna/dht.ino +++ b/code/espurna/dht.ino @@ -17,7 +17,7 @@ char dhtTemperature[6]; char dhtHumidity[6]; // ----------------------------------------------------------------------------- -// DHT +// Values // ----------------------------------------------------------------------------- char * getDHTTemperature() { @@ -30,12 +30,12 @@ char * getDHTHumidity() { void dhtSetup() { dht.begin(); + apiRegister("/api/temperature", "temperature", getDHTTemperature); + apiRegister("/api/humidity", "humidity", getDHTHumidity); } void dhtLoop() { - if (!mqttConnected()) return; - // Check if we should read new data static unsigned long last_update = 0; if ((millis() - last_update > DHT_UPDATE_INTERVAL) || (last_update == 0)) { @@ -62,6 +62,12 @@ void dhtLoop() { mqttSend(getSetting("dhtTmpTopic", DHT_TEMPERATURE_TOPIC).c_str(), dhtTemperature); mqttSend(getSetting("dhtHumTopic", DHT_HUMIDITY_TOPIC).c_str(), dhtHumidity); + // Send to Domoticz + #if ENABLE_DOMOTICZ + domoticzSend("dczTmpIdx", dhtTemperature); + domoticzSend("dczHumIdx", dhtHumidity); + #endif + // Update websocket clients char buffer[100]; sprintf_P(buffer, PSTR("{\"dhtVisible\": 1, \"dhtTmp\": %s, \"dhtHum\": %s}"), dhtTemperature, dhtHumidity); diff --git a/code/espurna/domoticz.ino b/code/espurna/domoticz.ino index 2bbbcf14..607acb8c 100644 --- a/code/espurna/domoticz.ino +++ b/code/espurna/domoticz.ino @@ -8,70 +8,13 @@ Copyright (C) 2016-2017 by Xose Pérez #if ENABLE_DOMOTICZ -#include -#include - -void domoticzMQTTCallback(unsigned int type, const char * topic, const char * payload) { - - String dczTopicOut = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC); - - if (type == MQTT_CONNECT_EVENT) { - mqttSubscribeRaw(dczTopicOut.c_str()); - } - - if (type == MQTT_MESSAGE_EVENT) { - - // Check topic - if (dczTopicOut.equals(topic)) { - - // Parse response - DynamicJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.parseObject((char *) payload); - if (!root.success()) { - DEBUG_MSG("[DOMOTICZ] Error parsing data\n"); - return; - } - - // IDX - unsigned long idx = root["idx"]; - int relayID = domoticzRelay(idx); - if (relayID >= 0) { - unsigned long value = root["nvalue"]; - DEBUG_MSG("[DOMOTICZ] Received value %d for IDX %d\n", value, idx); - relayStatus(relayID, value == 1); - } - - } - - } - -} - -int domoticzIdx(unsigned int relayID) { - return getSetting("dczIdx" + String(relayID)).toInt(); -} - -int domoticzRelay(unsigned int idx) { - for (int relayID=0; relayID void domoticzSend(const char * key, T value) { + unsigned int idx = getSetting(key).toInt(); if (idx > 0) { - unsigned int value = relayStatus(relayID) ? 1 : 0; char payload[45]; - sprintf(payload, "{\"idx\": %d, \"nvalue\": %d, \"svalue\": \"\"}", idx, value); + sprintf(payload, "{\"idx\": %d, \"nvalue\": %s, \"svalue\": \"\"}", idx, String(value).c_str()); mqttSendRaw(getSetting("dczTopicIn", DOMOTICZ_IN_TOPIC).c_str(), payload); } } -void domoticzSetup() { - mqttRegister(domoticzMQTTCallback); -} - #endif diff --git a/code/espurna/ds18b20.ino b/code/espurna/ds18b20.ino index adc95da2..b4fc6795 100644 --- a/code/espurna/ds18b20.ino +++ b/code/espurna/ds18b20.ino @@ -26,6 +26,7 @@ char * getDSTemperature() { void dsSetup() { ds18b20.begin(); + apiRegister("/api/temperature", "temperature", getDSTemperature); } void dsLoop() { @@ -55,6 +56,11 @@ void dsLoop() { // Send MQTT messages mqttSend(getSetting("dsTmpTopic", DS_TEMPERATURE_TOPIC).c_str(), dsTemperature); + // Send to Domoticz + #if ENABLE_DOMOTICZ + domoticzSend("dczTmpIdx", dsTemperature); + #endif + // Update websocket clients char buffer[100]; sprintf_P(buffer, PSTR("{\"dsVisible\": 1, \"dsTmp\": %s}"), dsTemperature); diff --git a/code/espurna/emon.ino b/code/espurna/emon.ino index ecf28301..b41c0345 100644 --- a/code/espurna/emon.ino +++ b/code/espurna/emon.ino @@ -52,6 +52,9 @@ void powerMonitorSetup() { getSetting("emonRatio", EMON_CURRENT_RATIO).toFloat() ); emon.setPrecision(EMON_CURRENT_PRECISION); + + apiRegister("/api/power", "power", getPower); + } void powerMonitorLoop() { @@ -104,6 +107,9 @@ void powerMonitorLoop() { double p = (sum - max - min) * mainsVoltage / (measurements - 2); sprintf(power, "%d", int(p)); mqttSend(getSetting("emonPowerTopic", EMON_POWER_TOPIC).c_str(), power); + #if ENABLE_DOMOTICZ + domoticzSend("dczPowIdx", power); + #endif sum = 0; measurements = 0; } diff --git a/code/espurna/espurna.ino b/code/espurna/espurna.ino index 3fca7dd0..cafc7f22 100644 --- a/code/espurna/espurna.ino +++ b/code/espurna/espurna.ino @@ -19,20 +19,13 @@ along with this program. If not, see . */ -#include #include "config/all.h" // ----------------------------------------------------------------------------- -// PROTOTYPES +// GLOBALS // ----------------------------------------------------------------------------- -#include -#include -#include - -void mqttRegister(void (*callback)(unsigned int, const char *, const char *)); -template bool setSetting(const String& key, T value); -template String getSetting(const String& key, T defaultValue); +char apibuffer[64]; // ----------------------------------------------------------------------------- // METHODS @@ -114,9 +107,6 @@ void setup() { webSetup(); ntpSetup(); - #if ENABLE_DOMOTICZ - domoticzSetup(); - #endif #if ENABLE_FAUXMO fauxmoSetup(); #endif diff --git a/code/espurna/pow.ino b/code/espurna/pow.ino index 327ad08f..5196ee35 100644 --- a/code/espurna/pow.ino +++ b/code/espurna/pow.ino @@ -10,6 +10,8 @@ Copyright (C) 2016-2017 by Xose Pérez #if ENABLE_POW #include +#include +#include #define POW_USE_INTERRUPTS 1 @@ -142,6 +144,19 @@ void powSetup() { powAttachInterrupts(); #endif + apiRegister("/api/power", "power", []() { + sprintf(apibuffer, "%d", getActivePower()); + return apibuffer; + }); + apiRegister("/api/current", "current", []() { + dtostrf(getCurrent(), 5, 2, apibuffer); + return apibuffer; + }); + apiRegister("/api/voltage", "voltage", []() { + sprintf(apibuffer, "%d", getVoltage()); + return apibuffer; + }); + } void powLoop() { @@ -200,6 +215,10 @@ void powLoop() { mqttSend(getSetting("powRPowerTopic", POW_RPOWER_TOPIC).c_str(), String(reactive).c_str()); mqttSend(getSetting("powPFactorTopic", POW_PFACTOR_TOPIC).c_str(), String(factor).c_str()); + #if ENABLE_DOMOTICZ + domoticzSend("dczPowIdx", power); + #endif + power_sum = current_sum = voltage_sum = 0; report_count = POW_REPORT_EVERY; diff --git a/code/espurna/relay.ino b/code/espurna/relay.ino index 3a2c6b5f..d99c2b6b 100644 --- a/code/espurna/relay.ino +++ b/code/espurna/relay.ino @@ -9,35 +9,24 @@ Copyright (C) 2016-2017 by Xose Pérez #include #include #include +#include typedef struct { unsigned char pin; bool reverse; } relay_t; std::vector _relays; -bool recursive = false; + #ifdef SONOFF_DUAL unsigned char dualRelayStatus = 0; #endif +bool recursive = false; + // ----------------------------------------------------------------------------- // RELAY // ----------------------------------------------------------------------------- -void relayMQTT(unsigned char id) { - if (id >= _relays.size()) return; - String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER); - char buffer[strlen(MQTT_RELAY_TOPIC) + mqttGetter.length() + 3]; - sprintf(buffer, "%s/%d%s", MQTT_RELAY_TOPIC, id, mqttGetter.c_str()); - mqttSend(buffer, relayStatus(id) ? "1" : "0"); -} - -void relayMQTT() { - for (unsigned int i=0; i < _relays.size(); i++) { - relayMQTT(i); - } -} - String relayString() { DynamicJsonBuffer jsonBuffer; JsonObject& root = jsonBuffer.createObject(); @@ -50,11 +39,6 @@ String relayString() { return output; } -void relayWS() { - String output = relayString(); - wsSend(output.c_str()); -} - bool relayStatus(unsigned char id) { #ifdef SONOFF_DUAL if (id >= 2) return false; @@ -91,19 +75,19 @@ bool relayStatus(unsigned char id, bool status, bool report) { digitalWrite(_relays[id].pin, _relays[id].reverse ? !status : status); #endif + if (report) relayMQTT(id); if (!recursive) { relaySync(id); relaySave(); + relayWS(); } #ifdef ENABLE_DOMOTICZ - domoticzSend(id); + relayDomoticzSend(id); #endif } - if (report) relayMQTT(id); - if (!recursive) relayWS(); return changed; } @@ -181,6 +165,134 @@ unsigned char relayCount() { return _relays.size(); } +//------------------------------------------------------------------------------ +// REST API +//------------------------------------------------------------------------------ + +void relaySetupAPI() { + + // API entry points (protected with apikey) + for (unsigned int relayID=0; relayID= 0) { + unsigned long value = root["nvalue"]; + DEBUG_MSG("[DOMOTICZ] Received value %d for IDX %d\n", value, idx); + relayStatus(relayID, value == 1); + } + + } + + } + + }); +} + +#endif + +//------------------------------------------------------------------------------ +// MQTT +//------------------------------------------------------------------------------ + +void relayMQTT(unsigned char id) { + if (id >= _relays.size()) return; + String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER); + char buffer[strlen(MQTT_RELAY_TOPIC) + mqttGetter.length() + 3]; + sprintf(buffer, "%s/%d%s", MQTT_RELAY_TOPIC, id, mqttGetter.c_str()); + mqttSend(buffer, relayStatus(id) ? "1" : "0"); +} + +void relayMQTT() { + for (unsigned int i=0; i < _relays.size(); i++) { + relayMQTT(i); + } +} + void relayMQTTCallback(unsigned int type, const char * topic, const char * payload) { String mqttSetter = getSetting("mqttSetter", MQTT_USE_SETTER); @@ -220,6 +332,14 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo } +void relaySetupMQTT() { + mqttRegister(relayMQTTCallback); +} + +//------------------------------------------------------------------------------ +// Setup +//------------------------------------------------------------------------------ + void relaySetup() { #ifdef SONOFF_DUAL @@ -253,10 +373,13 @@ void relaySetup() { if (relayMode == RELAY_MODE_OFF) relayStatus(i, false); if (relayMode == RELAY_MODE_ON) relayStatus(i, true); } - if (relayMode == RELAY_MODE_SAME) relayRetrieve(); - mqttRegister(relayMQTTCallback); + relaySetupAPI(); + relaySetupMQTT(); + #if ENABLE_DOMOTICZ + relayDomoticzSetup(); + #endif DEBUG_MSG("[RELAY] Number of relays: %d\n", _relays.size()); diff --git a/code/espurna/web.ino b/code/espurna/web.ino index 41d085fb..5396275e 100644 --- a/code/espurna/web.ino +++ b/code/espurna/web.ino @@ -14,17 +14,25 @@ Copyright (C) 2016-2017 by Xose Pérez #include #include #include +#include AsyncWebServer server(80); AsyncWebSocket ws("/ws"); +Ticker deferred; typedef struct { IPAddress ip; unsigned long timestamp = 0; } ws_ticket_t; - ws_ticket_t _ticket[WS_BUFFER_SIZE]; -Ticker deferred; + +typedef struct { + char * url; + char * key; + apiGetCallbackFunction getFn = NULL; + apiPutCallbackFunction putFn = NULL; +} web_api_t; +std::vector _apis; // ----------------------------------------------------------------------------- // WEBSOCKETS @@ -101,7 +109,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { bool fauxmoEnabled = false; #endif unsigned int network = 0; - unsigned int dczIdx = 0; + unsigned int dczRelayIdx = 0; String adminPass; for (unsigned int i=0; i= relayCount()) continue; - key = key + String(dczIdx); - ++dczIdx; + if (key == "dczRelayIdx") { + if (dczRelayIdx >= relayCount()) continue; + key = key + String(dczRelayIdx); + ++dczRelayIdx; } #else @@ -319,11 +327,28 @@ void _wsStart(uint32_t client_id) { root["dczTopicIn"] = getSetting("dczTopicIn", DOMOTICZ_IN_TOPIC); root["dczTopicOut"] = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC); - JsonArray& dczIdx = root.createNestedArray("dczIdx"); + JsonArray& dczRelayIdx = root.createNestedArray("dczRelayIdx"); for (byte i=0; imethodToString(), request->url().c_str()); } @@ -464,46 +489,7 @@ bool _authenticate(AsyncWebServerRequest *request) { return request->authenticate(HTTP_USERNAME, httpPassword); } -void _onAuth(AsyncWebServerRequest *request) { - - _logRequest(request); - - if (!_authenticate(request)) return request->requestAuthentication(); - - IPAddress ip = request->client()->remoteIP(); - unsigned long now = millis(); - unsigned short index; - for (index = 0; index < WS_BUFFER_SIZE; index++) { - if (_ticket[index].ip == ip) break; - if (_ticket[index].timestamp == 0) break; - if (now - _ticket[index].timestamp > WS_TIMEOUT) break; - } - if (index == WS_BUFFER_SIZE) { - request->send(423); - } else { - _ticket[index].ip = ip; - _ticket[index].timestamp = now; - request->send(204); - } - -} - -void _onHome(AsyncWebServerRequest *request) { - - _logRequest(request); - - if (!_authenticate(request)) return request->requestAuthentication(); - - String password = getSetting("adminPass", ADMIN_PASS); - if (password.equals(ADMIN_PASS)) { - request->send(SPIFFS, "/password.html"); - } else { - request->send(SPIFFS, "/index.html"); - } - -} - -bool _apiAuth(AsyncWebServerRequest *request) { +bool _authAPI(AsyncWebServerRequest *request) { if (getSetting("apiEnabled").toInt() == 0) { DEBUG_MSG("[WEBSERVER] HTTP API is not enabled\n"); @@ -528,11 +514,63 @@ bool _apiAuth(AsyncWebServerRequest *request) { } -void _onRelay(AsyncWebServerRequest *request) { +ArRequestHandlerFunction _bindAPI(unsigned int apiID) { + + return [apiID](AsyncWebServerRequest *request) { + webLogRequest(request); + + if (!_authAPI(request)) return; + + bool asJson = false; + if (request->hasHeader("Accept")) { + AsyncWebHeader* h = request->getHeader("Accept"); + asJson = h->value().equals("application/json"); + } + + web_api_t api = _apis[apiID]; + if (request->method() == HTTP_PUT) { + if (request->hasParam("value", true)) { + AsyncWebParameter* p = request->getParam("value", true); + (api.putFn)((p->value()).c_str()); + } + } + + char * value = strdup((api.getFn)()); + + if (asJson) { + char buffer[64]; + sprintf_P(buffer, PSTR("{ \"%s\": %s }"), api.key, value); + request->send(200, "application/json", buffer); + } else { + request->send(200, "text/plain", value); + } + + }; + +} + +void apiRegister(const char * url, const char * key, apiGetCallbackFunction getFn, apiPutCallbackFunction putFn) { - _logRequest(request); + // Store it + web_api_t api; + api.url = strdup(url); + api.key = strdup(key); + api.getFn = getFn; + api.putFn = putFn; + _apis.push_back(api); - if (!_apiAuth(request)) return; + // Bind call + unsigned int methods = HTTP_GET; + if (putFn != NULL) methods += HTTP_PUT; + server.on(url, methods, _bindAPI(_apis.size() - 1)); + +} + +void _onAPIs(AsyncWebServerRequest *request) { + + webLogRequest(request); + + if (!_authAPI(request)) return; bool asJson = false; if (request->hasHeader("Accept")) { @@ -542,55 +580,80 @@ void _onRelay(AsyncWebServerRequest *request) { String output; if (asJson) { - output = relayString(); + DynamicJsonBuffer jsonBuffer; + JsonObject& root = jsonBuffer.createObject(); + for (unsigned int i=0; i < _apis.size(); i++) { + root[_apis[i].key] = _apis[i].url; + } + root.printTo(output); request->send(200, "application/json", output); + } else { - for (unsigned int i=0; i ") + _apis[i].url + String("\n
"); } request->send(200, "text/plain", output); } -}; +} -ArRequestHandlerFunction _onRelayStatusWrapper(unsigned int relayID) { +void _onHome(AsyncWebServerRequest *request) { - return [relayID](AsyncWebServerRequest *request) { + DEBUG_MSG("[DEBUG] Free heap: %d bytes\n", ESP.getFreeHeap()); - _logRequest(request); + FSInfo fs_info; + if (SPIFFS.info(fs_info)) { + DEBUG_MSG("[DEBUG] File system total size: %d bytes\n", fs_info.totalBytes); + DEBUG_MSG(" used size : %d bytes\n", fs_info.usedBytes); + DEBUG_MSG(" block size: %d bytes\n", fs_info.blockSize); + DEBUG_MSG(" page size : %d bytes\n", fs_info.pageSize); + DEBUG_MSG(" max files : %d\n", fs_info.maxOpenFiles); + DEBUG_MSG(" max length: %d\n", fs_info.maxPathLength); + } else { + DEBUG_MSG("[DEBUG] Error, FS not accesible!\n"); + } - if (!_apiAuth(request)) return; + webLogRequest(request); - if (request->method() == HTTP_PUT) { - if (request->hasParam("status", true)) { - AsyncWebParameter* p = request->getParam("status", true); - unsigned int value = p->value().toInt(); - if (value == 2) { - relayToggle(relayID); - } else { - relayStatus(relayID, value == 1); - } - } - } + if (!_authenticate(request)) return request->requestAuthentication(); - bool asJson = false; - if (request->hasHeader("Accept")) { - AsyncWebHeader* h = request->getHeader("Accept"); - asJson = h->value().equals("application/json"); - } + String password = getSetting("adminPass", ADMIN_PASS); + if (password.equals(ADMIN_PASS)) { + request->send(SPIFFS, "/password.html"); + } else { + request->send(SPIFFS, "/index.html"); + } - String output; - if (asJson) { - output = String("{\"relayStatus\": ") + String(relayStatus(relayID) ? "1" : "0") + "}"; - request->send(200, "application/json", output); - } else { - request->send(200, "text/plain", relayStatus(relayID) ? "1" : "0"); - } +} - }; +void _onAuth(AsyncWebServerRequest *request) { + + webLogRequest(request); + + if (!_authenticate(request)) return request->requestAuthentication(); + + IPAddress ip = request->client()->remoteIP(); + unsigned long now = millis(); + unsigned short index; + for (index = 0; index < WS_BUFFER_SIZE; index++) { + if (_ticket[index].ip == ip) break; + if (_ticket[index].timestamp == 0) break; + if (now - _ticket[index].timestamp > WS_TIMEOUT) break; + } + if (index == WS_BUFFER_SIZE) { + request->send(423); + } else { + _ticket[index].ip = ip; + _ticket[index].timestamp = now; + request->send(204); + } } +AsyncWebServer * getServer() { + return &server; +} + void webSetup() { // Setup websocket @@ -604,14 +667,7 @@ void webSetup() { server.on("/", HTTP_GET, _onHome); server.on("/index.html", HTTP_GET, _onHome); server.on("/auth", HTTP_GET, _onAuth); - - // API entry points (protected with apikey) - for (unsigned int relayID=0; relayID 1) $(".id", line).html(" " + id); line.appendTo("#idxs"); @@ -264,12 +264,12 @@ function processData(data) { } // Domoticz - if (key == "dczIdx") { - var idxs = data.dczIdx; + if (key == "dczRelayIdx") { + var idxs = data.dczRelayIdx; createIdxs(idxs.length); for (var i in idxs) { - var element = $(".dczIdx[data=" + i + "]"); + var element = $(".dczRelayIdx[data=" + i + "]"); if (element.length > 0) element.val(idxs[i]); } diff --git a/code/html/index.html b/code/html/index.html index 7c0736d4..077f4f4e 100644 --- a/code/html/index.html +++ b/code/html/index.html @@ -265,7 +265,11 @@

 
-
This is the key you will have to pass with every HTTP request to the API, either to get or write values.
+
+ This is the key you will have to pass with every HTTP request to the API, either to get or write values.
+ All API calls must contain the apikey parameter with the value above.
+ To know what APIs are enabled do a call to /apis. +
@@ -327,12 +331,15 @@
 
-
This is the root topic for this device. The {identifier} placeholder will be replaces by the device hostname.
+
+ This is the root topic for this device. The {identifier} placeholder will be replaces by the device hostname.
- <root>/relay/# Send a 0 or a 1 as a payload to this topic to switch it on or off. You can also send a 2 to toggle its current state. Replace # with the relay ID (starting from 0). If the board has only one relay it will be 0.
- <root>/led/# Send a 0 or a 1 as a payload to this topic to set the onboard LED to the given state, send a 3 to turn it back to WIFI indicator. Replace # with the LED ID (starting from 0). If the board has only one LED it will be 0.
+ - <root>/button/# For each button in the board subscribe to this topic to know when it is pressed (payload 1) or released (payload 0).
- <root>/ip The device will report to this topic its IP.
- <root>/heartbeat The device will report to this topic every few minutes.
- <root>/version The device will report to this topic its firmware version on boot.
+
@@ -363,6 +370,24 @@ +
+ +
+
Set to 0 to disable notifications.
+
+ +
+ +
+
Set to 0 to disable notifications.
+
+ +
+ +
+
Set to 0 to disable notifications.
+
+
@@ -386,21 +411,21 @@
- +
 
In Watts (W). If you are using a pure resistive load like a bulb this will be writen on it, otherwise use a socket multimeter to get this value.
- +
 
In Volts (V). Enter your the nominal AC voltage for your household or facility, or use multimeter to get this value.
- +
 
In Ampers (A). If you are using a pure resistive load like a bulb this will the ratio between the two previous values, i.e. power / voltage. You can also use a current clamp around one fo the power wires to get this value.
@@ -479,8 +504,8 @@
- -
+ +
Set to 0 to disable notifications.
From bbac070c091bb3d2562fe3b1c505e4ca340c1cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xose=20P=C3=A9rez?= Date: Fri, 20 Jan 2017 13:30:52 +0100 Subject: [PATCH 2/2] Pass safe buffer to api callbacks --- code/espurna/config/general.h | 2 +- code/espurna/config/prototypes.h | 2 +- code/espurna/dht.ino | 43 +++++++++++++++++------------ code/espurna/ds18b20.ino | 22 +++++++++------ code/espurna/emon.ino | 47 ++++++++++++++++++-------------- code/espurna/pow.ino | 15 ++++------ code/espurna/relay.ino | 4 +-- code/espurna/web.ino | 11 ++++++-- 8 files changed, 84 insertions(+), 62 deletions(-) diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index 3032f5d8..c00fad50 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -81,7 +81,7 @@ #define MQTT_MESSAGE_EVENT 2 // Custom get and set postfixes -// Use something like "/status" or "/set", with trailing slash +// Use something like "/status" or "/set", with leading slash #define MQTT_USE_GETTER "" #define MQTT_USE_SETTER "" diff --git a/code/espurna/config/prototypes.h b/code/espurna/config/prototypes.h index 6311efb3..2c4fe604 100644 --- a/code/espurna/config/prototypes.h +++ b/code/espurna/config/prototypes.h @@ -5,7 +5,7 @@ #include #include -typedef std::function apiGetCallbackFunction; +typedef std::function apiGetCallbackFunction; typedef std::function apiPutCallbackFunction; void apiRegister(const char * url, const char * key, apiGetCallbackFunction getFn, apiPutCallbackFunction putFn = NULL); void mqttRegister(void (*callback)(unsigned int, const char *, const char *)); diff --git a/code/espurna/dht.ino b/code/espurna/dht.ino index 502bcbdb..61ba98f5 100644 --- a/code/espurna/dht.ino +++ b/code/espurna/dht.ino @@ -13,25 +13,29 @@ Copyright (C) 2016-2017 by Xose Pérez DHT dht(DHT_PIN, DHT_TYPE, DHT_TIMING); -char dhtTemperature[6]; -char dhtHumidity[6]; +double _dhtTemperature = 0; +unsigned int _dhtHumidity = 0; // ----------------------------------------------------------------------------- // Values // ----------------------------------------------------------------------------- -char * getDHTTemperature() { - return dhtTemperature; +double getDHTTemperature() { + return _dhtTemperature; } -char * getDHTHumidity() { - return dhtHumidity; +unsigned int getDHTHumidity() { + return _dhtHumidity; } void dhtSetup() { dht.begin(); - apiRegister("/api/temperature", "temperature", getDHTTemperature); - apiRegister("/api/humidity", "humidity", getDHTHumidity); + apiRegister("/api/temperature", "temperature", [](char * buffer, size_t len) { + dtostrf(_dhtTemperature, len-1, 1, buffer); + }); + apiRegister("/api/humidity", "humidity", [](char * buffer, size_t len) { + snprintf(buffer, len, "%d", _dhtHumidity); + }); } void dhtLoop() { @@ -52,25 +56,30 @@ void dhtLoop() { } else { - dtostrf(t, 4, 1, dhtTemperature); - itoa((int) h, dhtHumidity, 10); + _dhtTemperature = t; + _dhtHumidity = h; - DEBUG_MSG("[DHT] Temperature: %s\n", dhtTemperature); - DEBUG_MSG("[DHT] Humidity: %s\n", dhtHumidity); + char temperature[6]; + char humidity[6]; + dtostrf(t, 4, 1, temperature); + itoa((unsigned int) h, humidity, 10); + + DEBUG_MSG("[DHT] Temperature: %s\n", temperature); + DEBUG_MSG("[DHT] Humidity: %s\n", humidity); // Send MQTT messages - mqttSend(getSetting("dhtTmpTopic", DHT_TEMPERATURE_TOPIC).c_str(), dhtTemperature); - mqttSend(getSetting("dhtHumTopic", DHT_HUMIDITY_TOPIC).c_str(), dhtHumidity); + mqttSend(getSetting("dhtTmpTopic", DHT_TEMPERATURE_TOPIC).c_str(), temperature); + mqttSend(getSetting("dhtHumTopic", DHT_HUMIDITY_TOPIC).c_str(), humidity); // Send to Domoticz #if ENABLE_DOMOTICZ - domoticzSend("dczTmpIdx", dhtTemperature); - domoticzSend("dczHumIdx", dhtHumidity); + domoticzSend("dczTmpIdx", temperature); + domoticzSend("dczHumIdx", humidity); #endif // Update websocket clients char buffer[100]; - sprintf_P(buffer, PSTR("{\"dhtVisible\": 1, \"dhtTmp\": %s, \"dhtHum\": %s}"), dhtTemperature, dhtHumidity); + sprintf_P(buffer, PSTR("{\"dhtVisible\": 1, \"dhtTmp\": %s, \"dhtHum\": %s}"), temperature, humidity); wsSend(buffer); } diff --git a/code/espurna/ds18b20.ino b/code/espurna/ds18b20.ino index b4fc6795..2011530f 100644 --- a/code/espurna/ds18b20.ino +++ b/code/espurna/ds18b20.ino @@ -14,19 +14,21 @@ Copyright (C) 2016-2017 by Xose Pérez OneWire oneWire(DS_PIN); DallasTemperature ds18b20(&oneWire); -char dsTemperature[6]; +double _dsTemperature = 0; // ----------------------------------------------------------------------------- // DS18B20 // ----------------------------------------------------------------------------- -char * getDSTemperature() { - return dsTemperature; +double getDSTemperature() { + return _dsTemperature; } void dsSetup() { ds18b20.begin(); - apiRegister("/api/temperature", "temperature", getDSTemperature); + apiRegister("/api/temperature", "temperature", [](char * buffer, size_t len) { + dtostrf(_dsTemperature, len-1, 1, buffer); + }); } void dsLoop() { @@ -49,21 +51,23 @@ void dsLoop() { } else { - dtostrf(t, 4, 1, dsTemperature); + _dsTemperature = t; - DEBUG_MSG("[DS18B20] Temperature: %s\n", dsTemperature); + char temperature[6]; + dtostrf(t, 5, 1, temperature); + DEBUG_MSG("[DS18B20] Temperature: %s\n", temperature); // Send MQTT messages - mqttSend(getSetting("dsTmpTopic", DS_TEMPERATURE_TOPIC).c_str(), dsTemperature); + mqttSend(getSetting("dsTmpTopic", DS_TEMPERATURE_TOPIC).c_str(), temperature); // Send to Domoticz #if ENABLE_DOMOTICZ - domoticzSend("dczTmpIdx", dsTemperature); + domoticzSend("dczTmpIdx", temperature); #endif // Update websocket clients char buffer[100]; - sprintf_P(buffer, PSTR("{\"dsVisible\": 1, \"dsTmp\": %s}"), dsTemperature); + sprintf_P(buffer, PSTR("{\"dsVisible\": 1, \"dsTmp\": %s}"), temperature); wsSend(buffer); } diff --git a/code/espurna/emon.ino b/code/espurna/emon.ino index b41c0345..c9abb550 100644 --- a/code/espurna/emon.ino +++ b/code/espurna/emon.ino @@ -11,8 +11,8 @@ Copyright (C) 2016-2017 by Xose Pérez #include EmonLiteESP emon; -double current; -char power[8]; +double _current = 0; +unsigned int _power = 0; // ----------------------------------------------------------------------------- // EMON @@ -22,12 +22,12 @@ void setCurrentRatio(float value) { emon.setCurrentRatio(value); } -char * getPower() { - return power; +unsigned int getPower() { + return _power; } double getCurrent() { - return current; + return _current; } unsigned int currentCallback() { @@ -53,7 +53,9 @@ void powerMonitorSetup() { ); emon.setPrecision(EMON_CURRENT_PRECISION); - apiRegister("/api/power", "power", getPower); + apiRegister("/api/power", "power", [](char * buffer, size_t len) { + snprintf(buffer, len, "%d", _power); + }); } @@ -77,41 +79,46 @@ void powerMonitorLoop() { // Safety check: do not read current if relay is OFF if (!relayStatus(0)) { - current = 0; + _current = 0; } else { - current = emon.getCurrent(EMON_SAMPLES); - current -= EMON_CURRENT_OFFSET; - if (current < 0) current = 0; + _current = emon.getCurrent(EMON_SAMPLES); + _current -= EMON_CURRENT_OFFSET; + if (_current < 0) _current = 0; } if (measurements == 0) { - max = min = current; + max = min = _current; } else { - if (current > max) max = current; - if (current < min) min = current; + if (_current > max) max = _current; + if (_current < min) min = _current; } - sum += current; + sum += _current; ++measurements; float mainsVoltage = getSetting("emonMains", EMON_MAINS_VOLTAGE).toFloat(); - //DEBUG_MSG("[ENERGY] Power now: %dW\n", int(current * mainsVoltage)); + //DEBUG_MSG("[ENERGY] Power now: %dW\n", int(_current * mainsVoltage)); // Update websocket clients char text[20]; - sprintf_P(text, PSTR("{\"emonPower\": %d}"), int(current * mainsVoltage)); + sprintf_P(text, PSTR("{\"emonPower\": %d}"), int(_current * mainsVoltage)); wsSend(text); // Send MQTT messages averaged every EMON_MEASUREMENTS if (measurements == EMON_MEASUREMENTS) { - double p = (sum - max - min) * mainsVoltage / (measurements - 2); - sprintf(power, "%d", int(p)); + + _power = (int) ((sum - max - min) * mainsVoltage / (measurements - 2)); + sum = 0; + measurements = 0; + + char power[6]; + snprintf(power, "%d", 6, _power); mqttSend(getSetting("emonPowerTopic", EMON_POWER_TOPIC).c_str(), power); #if ENABLE_DOMOTICZ domoticzSend("dczPowIdx", power); #endif - sum = 0; - measurements = 0; + + } next_measurement += EMON_INTERVAL; diff --git a/code/espurna/pow.ino b/code/espurna/pow.ino index 5196ee35..606adb2b 100644 --- a/code/espurna/pow.ino +++ b/code/espurna/pow.ino @@ -144,17 +144,14 @@ void powSetup() { powAttachInterrupts(); #endif - apiRegister("/api/power", "power", []() { - sprintf(apibuffer, "%d", getActivePower()); - return apibuffer; + apiRegister("/api/power", "power", [](char * buffer, size_t len) { + snprintf(buffer, len, "%d", getActivePower()); }); - apiRegister("/api/current", "current", []() { - dtostrf(getCurrent(), 5, 2, apibuffer); - return apibuffer; + apiRegister("/api/current", "current", [](char * buffer, size_t len) { + dtostrf(getCurrent(), len-1, 2, buffer); }); - apiRegister("/api/voltage", "voltage", []() { - sprintf(apibuffer, "%d", getVoltage()); - return apibuffer; + apiRegister("/api/voltage", "voltage", [](char * buffer, size_t len) { + snprintf(buffer, len, "%d", getVoltage()); }); } diff --git a/code/espurna/relay.ino b/code/espurna/relay.ino index d99c2b6b..a5c0e892 100644 --- a/code/espurna/relay.ino +++ b/code/espurna/relay.ino @@ -181,8 +181,8 @@ void relaySetupAPI() { sprintf(key, "relay%d", relayID); apiRegister(url, key, - [relayID]() { - return (char *) (relayStatus(relayID) ? "1" : "0"); + [relayID](char * buffer, size_t len) { + snprintf(buffer, "%d", len, relayStatus(relayID) ? 1 : 0); }, [relayID](const char * payload) { unsigned int value = payload[0] - '0'; diff --git a/code/espurna/web.ino b/code/espurna/web.ino index 5396275e..0c00e78f 100644 --- a/code/espurna/web.ino +++ b/code/espurna/web.ino @@ -535,14 +535,19 @@ ArRequestHandlerFunction _bindAPI(unsigned int apiID) { } } - char * value = strdup((api.getFn)()); + char value[10]; + (api.getFn)(value, 10); + + // jump over leading spaces + char *p = value; + while ((unsigned char) *p == ' ') ++p; if (asJson) { char buffer[64]; - sprintf_P(buffer, PSTR("{ \"%s\": %s }"), api.key, value); + sprintf_P(buffer, PSTR("{ \"%s\": %s }"), api.key, p); request->send(200, "application/json", buffer); } else { - request->send(200, "text/plain", value); + request->send(200, "text/plain", p); } };