From 197c1e001926b66ca0ceec6641e16eb5d67de5be Mon Sep 17 00:00:00 2001 From: Boernsman Date: Mon, 15 Jun 2020 22:26:36 +0200 Subject: [PATCH] devices to things --- drexelundweiss/deviceplugindrexelundweiss.h | 78 ----- drexelundweiss/drexelundweiss.png | Bin 0 -> 128462 bytes drexelundweiss/drexelundweiss.pro | 6 +- ...pp => integrationplugindrexelundweiss.cpp} | 329 +++++++++--------- .../integrationplugindrexelundweiss.h | 81 +++++ ...n => integrationplugindrexelundweiss.json} | 2 +- drexelundweiss/meta.json | 13 + drexelundweiss/modbusdegisterdefinition.h | 35 +- drexelundweiss/modbusrtumaster.cpp | 1 + drexelundweiss/modbusrtumaster.h | 35 +- mypv/devicepluginmypv.h | 87 ----- ...uginmypv.cpp => integrationpluginmypv.cpp} | 151 ++++---- mypv/integrationpluginmypv.h | 90 +++++ ...inmypv.json => integrationpluginmypv.json} | 2 +- mypv/meta.json | 13 + mypv/modbustcpmaster.cpp | 35 +- mypv/modbustcpmaster.h | 35 +- mypv/mypv.png | Bin 0 -> 4702 bytes mypv/mypv.pro | 8 +- wallbe/devicepluginwallbe.cpp | 207 ----------- wallbe/devicepluginwallbe.h | 64 ---- wallbe/discover.cpp | 35 +- wallbe/discover.h | 35 +- wallbe/host.cpp | 35 +- wallbe/host.h | 35 +- wallbe/integrationpluginwallbe.cpp | 209 +++++++++++ wallbe/integrationpluginwallbe.h | 67 ++++ ...llbe.json => integrationpluginwallbe.json} | 2 +- wallbe/meta.json | 13 + wallbe/wallbe.h | 35 +- wallbe/wallbe.png | Bin 0 -> 10942 bytes wallbe/wallbe.pro | 6 +- 32 files changed, 910 insertions(+), 834 deletions(-) delete mode 100644 drexelundweiss/deviceplugindrexelundweiss.h create mode 100644 drexelundweiss/drexelundweiss.png rename drexelundweiss/{deviceplugindrexelundweiss.cpp => integrationplugindrexelundweiss.cpp} (56%) create mode 100644 drexelundweiss/integrationplugindrexelundweiss.h rename drexelundweiss/{deviceplugindrexelundweiss.json => integrationplugindrexelundweiss.json} (99%) create mode 100644 drexelundweiss/meta.json delete mode 100644 mypv/devicepluginmypv.h rename mypv/{devicepluginmypv.cpp => integrationpluginmypv.cpp} (56%) create mode 100644 mypv/integrationpluginmypv.h rename mypv/{devicepluginmypv.json => integrationpluginmypv.json} (99%) create mode 100644 mypv/meta.json create mode 100644 mypv/mypv.png delete mode 100644 wallbe/devicepluginwallbe.cpp delete mode 100644 wallbe/devicepluginwallbe.h create mode 100644 wallbe/integrationpluginwallbe.cpp create mode 100644 wallbe/integrationpluginwallbe.h rename wallbe/{devicepluginwallbe.json => integrationpluginwallbe.json} (99%) create mode 100644 wallbe/meta.json create mode 100644 wallbe/wallbe.png diff --git a/drexelundweiss/deviceplugindrexelundweiss.h b/drexelundweiss/deviceplugindrexelundweiss.h deleted file mode 100644 index 87f72ec..0000000 --- a/drexelundweiss/deviceplugindrexelundweiss.h +++ /dev/null @@ -1,78 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . -* -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef DEVICEPLUGINDREXELUNDWEISS_H -#define DEVICEPLUGINDREXELUNDWEISS_H - -#include "devices/devicemanager.h" -#include "plugintimer.h" - -#include -#include "modbusrtumaster.h" -#include - -class DevicePluginDrexelUndWeiss : public DevicePlugin -{ - Q_OBJECT - - Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "deviceplugindrexelundweiss.json") - Q_INTERFACES(DevicePlugin) - -public: - explicit DevicePluginDrexelUndWeiss(); - ~DevicePluginDrexelUndWeiss() override; - void init() override; - - void discoverDevices(DeviceDiscoveryInfo *info) override; - void setupDevice(DeviceSetupInfo *info) override; - void postSetupDevice(Device *device) override; - void deviceRemoved(Device *device) override; - void executeAction(DeviceActionInfo *info) override; - -private: - QList m_usedSerialPorts; - QHash m_modbusRTUMasters; - PluginTimer *m_refreshTimer = nullptr; - QHash m_pendingActions; - - void updateStates(Device *device); - void discoverModbusSlaves(ModbusRTUMaster *modbus, int slaveAddress); - -private slots: - void onRefreshTimer(); - void onPluginConfigurationChanged(const ParamTypeId ¶mTypeId, const QVariant &value); - - void onConnectionStateChanged(bool status); - void onReceivedCoil(int slaveAddress, int modbusRegister, bool value); - void onReceivedDiscreteInput(int slaveAddress, int modbusRegister, bool value); - void onReceivedHoldingRegister(int slaveAddress, int modbusRegister, int value); - void onReceivedInputRegister(int slaveAddress, int modbusRegister, int value); - - void onWriteRequestFinished(QUuid requestId, bool success); -}; - -#endif // DEVICEPLUGINDREXELUNDWEISS_H diff --git a/drexelundweiss/drexelundweiss.png b/drexelundweiss/drexelundweiss.png new file mode 100644 index 0000000000000000000000000000000000000000..7b874959d370faa99924585fb1cc581a3093b6e8 GIT binary patch literal 128462 zcmeGDbz4;5`#ug2-R*#MNv9y)C5Q+}BQ33T_s}UNEu9L|NOyM`gmg&^F~Cp*!~FK^ zKE9tvaliMU!?BLptXbE2U1zK~ZB11od>VWZ2t@QsO<4y7!mI~@(0y>RflsvL15H6_ zAkZsi1wFsK<2CO*y3riDM=kH0zLFpB?d+B_sozW4;YysFJ>_>B-}A_#+hyvVi|Xw$ z<@dGvLC^7A*u-wK(e`;wnb{V;!B3%S4c|X!$wwc)EdRlOpDa*#17+izeqnH+}m9hyn7U;F|`0Qy$5Mh%$t`Vddx*rAqme zNnx`2TtZ;fg8ciO(W)TwyBz;xs*Xbaqbe#U`y&%VANM&dSN*|QA>%>jZF}tc8IJx8 z8ZL8uEm_H88YiLxGA_h`G{z<4nN?QSdyL#R%RQLQYDcITjSFYAKhg;4^wOcHVke{1 z9>F2&4K%m&9~}hiIJj}Zh7y!9(fcJTFt^Q>t z3{O3$SLqI{T->Dogozhz@;x~RawFYo>WLX^9D7u{N z@kR8GVU`%R9ipmqGu#JeK>^*_LV*(#{7tp7vFl#I_=yny$AxENwAA2D>vA{k>ehBg z+6XpQqjvqA9ocQlST;W$IS*$&qhOD< zk&)yq**NEUp0mSJ8<#F+$dBQ+d5^zK1|Kil=|ZlWEH<0agKjzb>E^3>tzhL4g8zkZ^@#lg`tQy1H5$TC=*e?@=DF^pa#O^GcU<*l8IY z@J=f7XDM;-S&md2i=+6c6oxLZ;~urtL>(mmuAKJ;-)uE}Es$4XH_=;-V2;#DZaA90 zD(-HgH#GAl;{N{Kd?S%VlDMK;>|`az0<}%W`NY(w$ykjGX_%cWUBtv(gnbDL~PJW^BDi{2ws9Z8uEYjxe^P#h8S{8H|?qEbR zra8@twRgKC1~_O z8u{T5VoSVd1(LXsc;(8XGG%9dFmJz*rYqrVTs=cWYqb{q!DymiwxU8qf?v2k8dFNo zD)oE6H4(;4a_$W?(NtFEhzDK}9mH9w3zgwwj>5NxCCY~XZDcBsWMgBLdIRxE*Fzvu zbhK>v2Jz}6zANCWl{1z9F2&!duVIsvRYyy~FW@LHLkV2?Ze^V~fD#HZ;4g(K+9x;H z>DmQUp#tjtm(pf)8ON^fK+XMIa(=1I20N5iek-X~9{wra#$iHwo_oPE9DU^mZnv

z3KDC$+Hs#77xmumeTmAQP_7Kshkv|i{_IW7QX1lGp^0~->u`2O{*BMCHbWS# zniK1MuBp&>!SmSG#o)l$BsOZVH;=XAv;d`B*I1%qEBPEBxNf|~8gGup4&|n6LGNR< z!(|p%k6QP70*ZYUHiYjhK64wM#p1?bpQEX1RX&?-u%yg>qY1q4f1EFBsy>CB=;@x8 z#vAzllqs5GY{@6(HHV)YCKKpS()13dRY;gsc?Ag~i9%lE8X+(SNHEo~KOA1M4bpxO z;jX|v9nG{I4j~RVRFuEFqMbyUz!#=EhyeJ*i6JF_D=6W;OQlVRQufjqXEw0PwYUr0 z6tiiH)l_?d!L$60WTJ!gA*e_!iBwxB>e8DVt5Vue{Rqb%Qw9g|T^pU0k|`ba!TaN=~Uro7rFsT>w+0^N!iQ(Ifq+*mn+I?=T~kDh+N4!fuM2x zgB1eYUEnnW9yn*kKR7&8u(x2$YiUti_#VlXJ0Vb#N$Ms;HIa4zJ;9r3)={^&d2W^o z)Ik_<%zj3Oe~wowh(+wk#kmy`4!b=qzSy_)H)~BfC_C8H-}a5Wi$dqVO+N4@!!$&0 z>Z$01Wv_Cyc51TBqMcNt7`Kl0yA|ctNz}PD3T5d_M?NKCeOOjdRfhlT1Gd~TWLOLL z03KxUFLpa!PFf|qS_OQkdk&*8SAyH=OTRD}5;Bq@iIKjv>SBJ4_&0lbGra5XG3`O4f0}2w$kErYuYLP{+7H0YTRnj8x7u{d21PFGqa7olA2sk5c_k ziMKqSp5#)PMJsD@Bh%>S+-bIQ3BF@Cf>(W+K0{lwJxM^r9?KZWW!t4e{DbIgTQOfG zr}1ONSF<~^P^Q=AgxNKI!ZYU{+$|FApRDNgRyrJTbHQtxWCDU{0w635xiSiI@59xX4w;#Bow@xC8y-Q zwLmT|XPUk6(v~ZfCwy@KIV%dk4_9zPK@AwNjkF+z`W&6kt{0y*m2Kmxjg)O`dq;By zUDZhJO0GgUwnQLeC<6IAt=l*QnG&#!Tr~K_Hcqxj$;Qc3r*em)cCLZwcPLu7YToS4 znn$q!^{k&c_5nY)b$Ga%Ey0?iv>QcB*CcJhkA>-kF-L^(f}d$|F&P)uhpl;cTJ#AI z%pH~>1lcaTA8U30StuRbp6*u z^mFW9RK+n{@_FUV(Cb=`fSwYl2tCpKCvH?S=pnHEPQpW2tWIkt(DoF9h-*$2qbu!4 z1&A9MpPt>6*Ty_fgj{%v-A+B-oq=Y>C{*ttHhCMWnV4RT3HeCxxfShXni@bR6T$6n zXb7C|L*5X&6}uS)$W7Dd{UJr8Yi6cmOR(9-qeU{ZfzDqmj(1fd&ef?udb}lPG!VB zZUu=&=tI-VbwEiwmP4^*+0u^WBX7RoPBug!pTuJNM8cB&pH@@DjIyl-w$X~wZpwS3 zY2x=A@zlUC*gx-8Z^S#tZK%$w2=>=n7;O`+4_(KNUFTd|#f54GzUJ`}eX+gSA8~MU zjLwU8_==EP^ojMzr~Wp)o##%wWA%#kcrQn2BtwRgk+kR-rl50c(&}2?M!qS^+>30d zu2FL-`8mJ(cy!G2%s&>aZH3tL4dG-Jy!i($=v&w_BdX!!)ze8HgmdFtx@B`}uC{WadJt;qAUt?=1v0^j z`i98$sBR3Z-rz{K5@LFaT#h&2A9WYtqMm!@G}zbwIA*gGP^x}9DUaPr>`AWzrD6X@ z{b&7e^a88u+)^6_pK8)TgL9iYOPadX>JZ*jnGa81equ;;vQ^pj#L!k(?7L{X9q^QR zip289?)Vzz%75qFt8Qk}Am6H^t)pmUfPyk87YVfl^k3g&MlK@4~EjEqdra8gxfv+<%$- z`7`{qR@3VWj;?c*WV52EuMe)Os(K!DEBJ|8jM#I3dU$!iAc4BcjyXfKKaD(vyv2DC z6Avx@7>zhvv?P6O|H8`Jy=agTFMz9ZvrI>=Tvw8b`h<>uLi(*jvil2y3T?M7f0yhc z=J*Xpy?P`n-wQu!Co;o^`9bsMC)nk6oDpS&t#OLZw!@`Wb-?2IP70~hs{u+(xuokCvb0Dq2B_%=Gge}%Oa-~r7tCbXnA@%z(T8by<&+zYp z+=AZQGLs)rgh3NE<}FQIC`9^eL_Tj+3+_|MKiPi;fF;^A*XV9t}l> zh9Y}f7`$xVmlvx*xlXw+4s1`>J5NF~V7LC@Z!z4Mft+P@gvL(J8eqDj%x%&#oRbpzkN z>Hk#6FvfjnRo4&t(!IE z&d09a?(B_UH9<_)>nehNDlwl?p15+U_xD!l)YEltXcU$>NkKYg*sShHSr@Yg3-UW* z0|c|ymlbc8IzN(hNJ=3;mg2NHD{ z#*^C~O8WY+n{mPY(yyfyF<(fSvnB@{YnGa8E$nCFLOEnW=xJXyUaIKtZ5li=vv)H^ z23FNb)krU@ShzA?AFn4O_)O}pSA2f7{Ma1ZDjnPR>}Vp}a-T7(L6_RD^% zZ$KsCxtCvlEe9v^hcbnYShEC!T&UPjed}dG&duJ+1mOBuA){ZNh!(l0{|-`qu3^e z>2Its(ag-Xk96w;b+z9)o6|}Q(e(86!O5Wlf1#?qJqIxI14Y%i%v<%nO3-5nSEEpb zz_lGtp69tP@l?_&o46LFkldkz-$Dx0JH%Igp=$?|D#ds{%WIL83qhK|uiZXgFrV|q5s<%Bd~Rbu*)DnoKbJ0y+I$=^7om@o6c770dMESD)G z_xfJIy+*!7LOVjyxqliHGKv!JjH1vcXJly?dv~10cdCWahMFwk0XX6|kHyP>=3*Hi z_aVj28VdV&h(*-_I)kOw_vkOMkTjJ77(I zzn36jwQ}t&{(JRP?F@gn^h&g|@#Dv<3+c)trmcTwMlDgPT8Vw#zUxuOP5YZadH4rS z>N?1ql|F27QDYL|q%{l8#l_hLrB6i02UbLC?s#sg!@n4FRQ2KE z-mv$R?S5X(%_0fs@zT3woYNkv*N35~?3Ld+>H{SUWAy+e~3X z2G1evd6-Q;chQHp5zePTU#Z}R;6+edxhRBg)1A2g;zPXpy$7>lYtGI7WX?~N}`j9 zZ@CuTMR**XozG`ys6_u3Gh=_U-Tl7Z!n~giqH) z!9lkYmpnJFi3P=)nVH|oAALW>_}BWH9)@Wg;^OjVEawyFgUP+QUyC@0zE0Vfl|`H4 zTzZE3#T6CU-byj=Cw9lh@AEv}mj4*O|5t4+$@8~P>zx5h?AR@sEdTv~aMhp_R=r+e zE{*7m@VkA)srOlX_kvzLG)R_Yvcbxv#f1)$ak6kN`pu`$+srRlY^yxWV&vCkmi72& zHouMgjhF=f&L^R@2RyN#!7H51z>xd$$&*OyVzI2jFIUjHg@t$x(eF*;;m8+6n0=ly z;+cs#!u9~RGwY^C*%}`l9*xKVbs2No%_!v~TI7fd@$&W^d?gWT%gh#by3U{>`vT7_ z-z=bAXCU4t@d=i|gIO~#fz(m!7D?-R^xkCQK+DO>{MuqdycbQS7md0`WhEaAWL=4b5Jl~^=Z(Ba3O=>J*)prImh{21j#0c+#Q}rJ00C< z+||tJET1Jom?D320)GeryXKdtaxEor$-6a|6j{=ATjS3z)V#zDvZJaArN zSj)a2mN6L{O@!o1ZQ)e)V(PMCnxi#dnDfV72L_rdXY9(m9J zJv{86iC#8unO z7_BcUIFmi%$fY(^!gGH!|E#CjA$L1Qgm*SY$37M>Y|)@6d}AYs$2{m<<;C&exbXdw zk`nYv4Vpuzm%h8WNMl+XJ&gL4dFqh4u=QY^FLP~yR0cI>5x_wo@m@4f@O>N(6!bDO zn+to99cX*JgcuD%^{`jJ>&8}1qI=1nh9_6|Py~JmNC<@sCxD6RWuyOJ)koOn?>lA~@N#a&#^VJf#aQ$R%~9H&zl<8U3CnOJC!PIxWS@Nck3*ex*&? zVLGu8&fKw7b9SzlwL7(#AX#FR33xIcLu~bn-K;R^pg6{D*fwhi$#_Q^ok&A1?XZ`Q zbsQI~ZV5{GMhL$8d*5FU270gD!!@P!SS#d4LeD@?ao{p$Eix^;AbzAZJjC=WmRad}SrrYI9A}jB z&iZlFzJEO*EjRA=7D?q52wDZz}YRW zs9;|;Pvc@`1%Ke&4Y(KdU7&jyltX#eGoapdq^``Kiw(MoR-45zB5z2}e^Y3cls6OQ z#J}MF&h1?^;R??=^R7^MY7@4BK~j$42kdf31r2`R7OSe;{+h4V4$$0pIppGKH&8uA zhEn2o9By)MQinf5$mOrX zovj##fgX(eRQ0c^<932Wu3gY99^@(pGgFb}SMSVEV>V2fa~^O`hG3YL)8Bf$kBha# zau1VK-a8htetqIT^<*}IRb$ODoS2?A(4m1AdQkYhoH|~%PRmOx-3?D`usiSrjbHl>uq*a&YOBn9cNJ=5z*~~`BDI5 zlwk#r6E9)iASbKhD+huvK4&BKRWy1;HBmY&xKk>ve=sFGIy&5@y%)E;2>5X`4TTcK zXo{|yYF#?-C|42RH{M?^@VR9u+v>A%>pfGQ=so*VWGB7EvUPVu%M7pI)DTVR31Tb= zJe6=-sz>{{P~C5jI`zyJfw-hh`&B~+Fe}2HIGIykeSNEM9JgLoWd!13IR2@`b)~J# zSd%X2G7S4N+v!4 z5*I!kK$uNQ2&nj#RYVC6UuA#&^;AX1QJsWOs7~AfJgj$o-xcmc`X%~@4ymHTY%c$M znPBYz+0Pp63Efh@Ti-1n45hE8erbcE7Z$KbRS*>1P)s5;=BN!f0`fC!G{!aIW5Z)7 z+O-q<+r#9LPbRKCSb<-z4<$0Vd9Kbcx{9dY;iByw$>;oFxlF}W_~NE!_2X-ENhA4P z5Ced(oL|0NZ~ZE~BV4~)@13htfw#-!DUxWp$&l;-L!+wPx#3cWH_w-3y6BUoElzAt z20f$a1_j|yLi07P2LDaB-DUNT^ah{s)az&pp3CVyBizS{2GCzM5GMq3S>v$OL%$r4 zpKT=xVoG-;1P%Pm@H`CoR{d>AYQqzQzw4Go2Ct=;z%Hq7Q-c#rnEDfgV<~L)*X;Jn z27+>-+8AT9QQORf{spH#3k5T88h%R1*A5GI+iekbF2KL@`#D&_X9Byb3#K;%{`;}7 za(EmW@fpnu_WhWi+$x&7Wnp?kfenP*FojM0)V?;Il%GWCFXmsy_dJ3qs=x)x3IW;$ z$M!^zjaTS!l8}B%MKL#m0s<2J%w7i1hiL>F%G^FA30or~iZO!C67RLxNLeHn&s30h z9HB6z@cYNA`CrRXu8#9xjr-V)Vp?qY7J0dF#>1dU`vt`0=W>m@2 z7lLK`TY;!E_D}iJ%;L2ZuCS~;YeAYPSF{HcLD1H}g@GOtE{NG&*wKVxagnyJ36#-8 z|Aoyv3%gY8UL9>kg;z|um~t+P@MiNE&zQ5?N3bs9AnCwg-*$$Dm@jVwRf9-+gzVG{!LgTO3W6r?l=_>qsR0w zy{WOe&Pyi`r19BS3*-T`114!K5)2E*ju@5xS6}Nr7x}AAnrX63{o7Gr!x(i)n?tT> zj=yaX^Q1+8?vTz|>S|__z^{hV@Qj3A0ad%R+@a>jqyFcxm|KTgp18=Ug zJj|Kgd=*TL&8^wrsO+~fw_JW6XzZrJ66SWfT$4_xx1o4ZU}a3XQ1>n|*QEnbOZM&= zeiV8mc)7k5E2Nv5-0R6B(U?u|my;(fc@&esx13g9I(v(lwubJ_@{g`}2c|%e`mfIX z7aQch>n(9%Mi$1+N}oRofmxuUvMfTVb=39r4X~G86zbLcP#WO=x&ERMDlRI;qx)2e zuUsoHt2$`f712m!;iT7qy(|E^n{3C0eVj!)59NDz-pEUkyz0r= zbH8?GwHAE^G_^6irv8lx_1KM}ma^~~FI#F-Qf@+efTG6@$OccU@f$9pSA?tz&BnDKFe zDJ(xpU%M14nD|MJF@Q`sr`C4m0D3sA1stGtwm441r!cSr8~BGqXIxVcv2{I9V&XeDYh zL%!W`c^J=SyK>BkXX2B%%KIA*vUUQ_d=-JoZ;U>h1{KMzxgfIA%y4hh3hM>90M_b@ao-i)Rpw5V{t@p}WA ziKy$#kym$+I7re}*50%7$Q|*eUWlBi%Mz(c$i){mJHIay$39LQN!|C?hd5wPY65he zIx3J;SP*}Vv9SI7{sEP^2+9!FX9K*?nceCg<$5lsU3W4~TohF8IRL$8Ae2vIA3#zP zE8~>dRf6fC`VR_h^G+igk^msX1}1r6dysqbg<5l>mBwPk-SMdwY-oiXdT{nmu)oKe zU&@1+Z*rhx(O`$i;!%hp2&Mp%x7F7-DhRhs-#BDLBM2!P`~8P}uk`o+_nAl^fZarm zjF{b?`u0Q^8diU@)w=Oy@>(0lT#4)x*5VsA?0K-2zFGBjqr+Kt60M$Omc|}Pc7I*I ziPx8qx$m$k>Ys-^`*rw22y9$!(n-02y!Ng4p3D^F44f*}Q1*KG&nU50#Fh3qL*8DC z^~6O-zqH}uwp@1TuuY*&tcUtgTmV`avG9 z{=Jv3?-~6NPog63E|4OD7CzF+p$@g?=I4_%MCt9$)zXG3{RQBMmM-*EQq za}eJ^K@f%XWSQ_%dmxnya@utZylROGzflR#1(f~sn2n9-mIVVjjI!HGy~osOHaIM- zGL2)WPWI~6Cpuwu612GMh0G;cuB-ZCeSx|m-`-1lr;Q$&?55Gnro$;{>AocH++7$J zUU_ma+k5cw!4+w>;o%m2P5#O4BdQb67r8|YknJ@4ouJkB=cHjIX%UUWIaj&W+PNebu(t!n{1tKr|z-bj`Y z7v}PK^#0gR+&B=W!g_;j%vCRE!gQN8yV<<6FCZ}gc!yRUc8!L6wpBsae%&l`JB#wA zXmMJLcM_$QZV=9K*GZqI7^w~qBqmS1a}rG`C}5~IZo;mGVxv{t{Cn%MfzsRC^2bi_R+F~sh z`H0>$-?%MK{C8z#erfLMY6OD7?qwER`V9VRd!X3gPRRicZsnQ8jfSEJyyouk1mPK@ z=;h0Q(R1h}oP!0% z%EGQB5b#h+@7Q9^n*YXn_&d+J#YQLIoBL}9!*+6(b-Xm`h4ERHH2)y!Fr%hvdgqhd zGsXuo^PodEJp+TI`>6e>u&RP%}XCxN$STxMHHW!Nek||NnC*6>*)GyQt+J>QEz@ z^eZV72mUx3Fm8$C(F-^!z11=zPBk;6=FXl*>R+WCDQ>}%9_N#IbnLHAz>3=aD4CGB z&6jPmYuj4(KbIhuav{FZv$)S&uNc=*QPnj;49N+7{ zOOq~xXFTICm~8taraC!yFWoNvcReMZ;72i)PQ}x8+K|p^H0El4ZE)X--W(5S<0-2W z?^IQfduu(qNm2Ol%x)_0tq`t*Ipd3fV&Qxstaf)X`JaAiJ!)=va22@%f{h_bHixs! z6}k5gUZqeF8F})8G4X3-{vOeE?aPR5ceR=Vc66bn(R)aEHHqA1mDP&RDm>BEdgtGp zZ?(1jlGx*yPSAJ4(u+~KJWPd?r6~e^d`~vPrrFHYfZdB>(a4lKZsS%~jwSbahU%Xb zs13Evdc1`K>4?427o@s=%B!{eHegrGL(TPgPJw&LmsCd~SU{ET1TG?B-wi*IeRkk!_UK z?CIJgUAksUu-=b1anT_^e!apC>khoOG_-yl)~PGTpOREsM~MDgRk?{Qv^ zCL!DN54&npxpE{)s5@#*L84#AJNMIoKd|D`4aP^%ymCn+MQ?ky#PY0{wng21yu#$~8%y-H+Jul`auTHjkM1PEgzQahv?T zKjCfmfY|Tt^PDUsPVyJJ)WP4A8`r~mLr^=D&L9$mIb$Ax%`Xl$oywCW$^d6PN}D!1 zNZ18aJ~%QLO&-Q)8EnquFdW@fPW0a+B~X%5lILlR$fw^8>l7r@xQ=)UB8<3~q7{ol zD~*C7@TYm6|BS`Y@;n9Y7o7BLn#T4o-O8!xMjiV2me_wix5Sga|E=ryuW}C|vOpW` z%T$6&5XYf>D?yI#QXE={gsB>JTG0!E!H-??_${{m3DV7@dK%to)8TLogD6u5YtI04R`63HigH?J_;Ku+yIX|{?oXM8?dS|H+cm!%BObN$(PxCfI zjprxmI%mQd2pvR|)C@M-P71mV_~pBh6m^%lX=%>T$xe610pD2QlaQuEDfXK@6FNTx7xFX@~k^ zlhZar4u2t_-n;2K_kR-hR#TDZk3%<Lk#<)6%1U@XE1@QdnNsd`Z-HGVF`32Nm7V9Q)PJ9S8|CYL0RNjQ zO!&*J^nOn)Co#PXI~=Vlj>+}7Ikh^cAE(nCf)P!xEDn}pxew^NCr=*>31=Dq#BfT-Y>zTY|qt+0aGUq`J zaaRJKJXhTF*{1(QQPd;5M$U7YHmxf|1Lu{OfhM$~E~}(Aq3kg!SyS&nt(NjZI3H$Y zI?w-puX{fMcZ8Ki(vK$*6cohW0zJA7FVN@gC3o60} z%EtQL8LE?r1s*Q%yG_=GM6@Ik19nFL8=Jc?&Zz(qwWr!6$d-0zq2x1-v|?55Yj9o~ z7&u+)YyhNmI!EWQ9dVVF>}u;WC!Fp(X`oxw7zH65+m5Sv;CVK2jnth<897QngVw<( zc1~vff#H{)$M*@#BNZ}%=Y1F?fd5IP-~30V!J8Q~GF>|}bichGloyN;GWA3@XTR{r zANfT8pH|4KnzZQ#ghzs%MNT_!%;_XOo;pI>LU=n(v!_I`tX5#ABsc5+RQz3Va*YC^ zcCY+NY2+;VQj0AQLooa;e9v>tz~BkiN=ab?P@hRVo7)KjBVa?*FuXMO_wa-HdM8@I zKXv%v)L(A$hPonK#2UPImDV>c_M=Fa< zT&ZgE_VB(%-%hejbmoEq;75>RKF@I56A*v5btWK>N!at2c7_`KY!~w^nSaA3@QX-I!A+ z%I1OAAq7FHng$(~XN!J(=}yW$uj>-MWSkU5yK$ej^dki0@%CPhNgpu$pv<2#!xDBCoL3(buX zVCcA)?Yb#tjCZZ(3@I_JZ2Y|u@=_T58SSr!^#F1Cj0NwveUxUbHD-5GyKEEPTd(T( zqiWAKp8L%+Wgp%piw2}olPGG@l+r%b61>KpDwLtxw{SzNcvG0<#ZEMPIk&p{iK-x& z_`7)^k$>wZ2?;NmU;K*{@2}zJs)4FF0AWk?C!mTo%yl^PO>XK;S%j11>&P^}(ImB9Iu*Gcy3ziA44nF4Xv-#pEqgAN_M>*qfqez> zhZmxv+jC(N_4N6C*r>~Q8_)-RL>Q(qtln*lWhzHX?D~#;vg1U8LEa;C4IY%n5;u4%2|qq1^z14Ak9ODQTASH={H ze+BznQT7GHj}=I_pOPk0Im=jBeJ$ZzKCX~`sq^vKAQ}QnDjX8D&A-sP!3Pxr7$fSO zh|b03CHrSKtXq7wO~Wt%e5yO0rplO@QD4qzt_&%~%|s zuDOU=gr=!)u`=EX%HF1uSRjEs!byRb-2{hN<%>bSiZ z)iE)@^$d27KGyc%%Uvks^*9-iUzxg3>$lxrOu`Zhkx!robzv9JB6sBawLmHO?1elz zIbirBBdY@$7~W{t^i;})?wZ~KKLDz3>vOPyCq7VUsLr)S+R?hgm>D(pprUh^HW|Qg zdZYDh-3TA)OOP78ZNxpco$o|phB z@t4W82~9InRIH<)22hWiF$bs6h#h;jSKXcpvvn4;K)`RQrwJ`t`j$!ds(m(^=q1H1 z=e%GGA8kSU(|3k3Bq4Ctr8XZKQP(=v2~Fr$f&#Yj`wKk?fAw{(CV*kcwu7Iq1tIf_ zejqw#*wl{JW;7*@u5wy#rf7+Qtzl6t=&jqYxq4A~Q$w z36`!rcuhZcvzJmig`Go=wAJOR1d>~SP5#0@}YS;PCex3q8O`508)`AL08Y+|oR01#G*n-Q#xx50*zm0tabf$TaA6P_0M3OtB+%qqQm0~jF=o%bn z&5xHC%Y-XfZay}dB6Id|Vxv%zaznFOw#H+79uBMXp|#hWff1OjF>kgr`Ab^cFW+8Ihci|IXxMs{Vc{Q4+QNtEZ783Vxl*8->iM+f0pNzz=`h zzb`g0%RNaRN)Ftw^80Y7+jbpA<$kqNr|O5Vl08o+BNAfPpMLgPVUek)eDVVvZvx}_ zXhl;eZC%#|w`7N8ih4O>x}HJ@lK1Su^v>g(fwbV zRi71Sm?@Qky06f?itBPX(`y1s)8e^5D+z0zK3mn7GkA+RZLUU&n+K_^e%^cPyBGfX z^St`wp+9ocA%`U@x3?U!ad^7yhlixqu=S5gf%?uwvbXceo6REM&w&^m_T_HQ)ys%O zyKvO$jL!?vmq*X_VsqRL-uMSLbNNY_pY(I)H< zC`t4HSz+m+&fBi%eRRH6R)#rCJ^pGN2-XsPyqd$t1j|*L=t?S0Tmd3)>f~OLG|AwH znD^TU?%Co#(l3b^D{n?IAH3lBndKWdx7Nv4vjsAbU0SJ5M9kq55}2U9!%-&z}8n|<6_OGZS5{LoVp_g0L>PNooER_Xq;=Dt@;M( z_LZ%d;BJ32vAAS-C6F(Iwwnq^>WjT$refb-eA%IP4*DXYr(5|%)M+_##ibqGEh7UY zS)^K=N2goY;}=>MEY%z<(G)7SwA~2WHcJT3w|Wsq^|cQ5MNfnUMkfIJ*lu5_GI35D z$3n+KeKM{O_&ZsHk*_Gu?*xL-00XcKSOh5DOzQ^uDAsOZMZNcdr*8PUR ztX;TowdEx<1`uy(&$m5i??*YO3dD`Aj75#Pzgia8l{&BvJyiD=Sk@+TBPzsmS+O`)Lr?j8n#-Up%d&-KncUH7VW z-pt|tDL_DV;?-71Yd4Vv;wa1gukqw27xikV>EfS4oZ5u=czn}5W=om$n04{B1gHY4?p*d+Z{U6&@;aca<%2%y4-y0HCs?*LEoXgz zc@xfL?csrN(-e*}<&Pi5fGi&q&R?pjZhi)G9=zsk2gQ95KH59znpl&V< zOn!!D-r&c7mkdU({_z$?RTf!z4Y&Pu1yHvAGnfk!;*Owyv`@jUDVl$0~K?@KTT`Uy|BC2epb>r zNXT0vrY*$U*U0PT>%G&U$~X*6Sb&Tb&+VEin4^XWF8G{+6-OY@0b)c!wMu*rwAB_P z_%Jt@I=;|W-sZ&5l$5`Q`J!&Ig0{{c8Q$FIrc`|kh1&$-MhXyO=lMy->1_S_^!ZPg z&ESAEmf*X2cQdMqOF@@o@jUmD!)#$+hUny{Nmi^cnesIU8!vhe%c07DZg6f$%+C(_p0c ze~cvLF2Q0z-@IN+B|~LWdikxEDRP%WYkT5*?EqF*7gn`mZox-eJCEU+@TLT0F zKnF+i(__MbQp@538EJ}l?IL`;=y?pfBKNeUqrCL7K4VGz-2^PgrFKsabCYI#!(Jrf zAOW$Kd@Gybq7Aks&alGHOp+k(K_i>SmA=ck>8-Y@e`up}UbkS`sp6cRLhB7wHSXH9Z7@T~h$kcKH>CPAq8p-%@O8m5r< z_Dr%r8h-D~yZVwa_`?*uyuJIc3&p>lCB#`a`!jl#dY!NO-*)z*q(0Gz8Q7~EQ7V^h z(XVOFUJBax2R%&hqloXXPlx~2m=rnDS95{K_&>e~?j(s2n1mJ6D`o(g z)0Y};3HUqUp9M)NyN2WzbQG4*fq{19bS*GNb9A4Wa6tg%iQ~&l3(O${k&e1knmBvx zK187ZnrBywUGw7uC_E-0`+|Bpz+(-a{Y`eP?WFxvQ1m|<62eh9p3 zjiG7M8IGHIr@6(kF2BLTrNj?&Be*5qm|q ztJi=Y-Lt;VB>dvepRRh8yk0tTF%RX>IBk@z{Aas_N*2+35LK z;n?+3Z=a$lck2=Ce9v(l{_MJf{o>Zv)&sxv89EI7f}RbPCX1*jOIh(ICuYnU@f|Ny zzx6u@S65XP6&39m9d&tJq$Msc+Tx%50M?kqa1XF0MgzqwWv|bGmLGrt)*k{LueVcN zU0rduxxkd!1}`EAC(xO$>KhfAlL6T7xM4 z=HD(0goFX8n4ZS{<%ZMXmFze&QACI$*)U1u1(F#XAHk(s0DZp%vuZp23w68O%d&nf ziFzvIa{PRegZH(4CYr-)4}B-C@4%(BjjrRRQv-|6Vc%4^|E2rko-0-n?X&SB6^|7( z%ZuNR*)oB|3)N<&fyT%!a=VSvGc9`|2WQv8OCaia=h}{||3_ak)M1pTZ2as^=y%|T z&lzegrs3$1;rZVq5jS4i%#+y+FtrdVkx#`>$K&NDT9S2NxpdHJ7cz+{g7-^s5l8mI zv;Z*Z&hBfQnI-ymUDCH5d~PK~%rCKzH9Jolbah;06uspvizm|kg#G_> zppqIE$=N12=cA}C+s~g~>~^)|~98bU` zqOPvq2%6n0)IH?88Y*UZqT-^ReRZ%DjTnmkLWj>HXXNa{n~2E`If{%Az1CtZ=I~4D zY9s`mvr^kXq_2;QD`b5i#gAI!uvZs)r^zOKok{bX%i|$(W{V_Wam9oAc{}~Q9t2ql zp^aZMno08&z*LzBlqoiS7Y}RQSm(#s_h|={8>oLGZ+?-%(lKe=6-;1#z)bBU%0cP* zHi){X?!j5tr$1W9!1)8#<7uR!jEWWsq@F-zgnL2F0>R0zs|t@!^`kyATs7L!hizT! z4E)Z!8{=`xPe8+;HSi_^!(sPlOO8N%bX=h_arti~43DK;E=$^8dtVG?JJ~x4b0<(5ZwCj=`B9-^1+O#e7du`OK&LFcsDegG# zmz|!p5y>RpAI>frPH6N=K&GNk95y7raVXG2ko2(`fkY!9&l3**T>EPtqtm;WE#Rf4 zU2DO9=kRvfN>L-MpmhX!R@S^8K~Y)v6QM+uC^J({>-Nrs>AQ_`$I-54=P$HL)NXid z`}v0KLsD9nB=s}11r5dTw3G;vTJlY)Xp4G==W9<&$Gl!VJ=#8n0FCmMc~L}6lWjlyA8c)1TGfI7~B9 z*x2~X&(p^?K|HLq_%nJdvZBEfp0Y2By7*crIDzh(alV6f;Yq$_HPM^{hK4GhfxBWl!E>k_}tK4hFIxRw$M`o#@zbv zFG9gHh>uFqCUQgYIb1*W2m{!Nze95Nn(u4M4_1Dm++Q6|rSUP8;LWGZ!3c!TO&02a zr2UU|tq|gYs&2A=v{}v7$-Y=^f91YoayR{XPP-jh3ZB19LfPS7rp`JMFQMqI=cx5? zjsW$SnaaQ{g-g$aM2P%6GliHJQii}+z*muX;*iX#JiJ`2vV@Aw#r({XBTXY1O)Rx$ zh-9s7MnBBTa@+c=6`5T~!dhG(eRt<^u-HltGKn#+RJW4EL-=6DI#+9n)BO9l+J^XB zzCTEBgK&Xb@GQ-N&{%ALHX*Y7DiyhU^yO5(WHFb@lNoyw^E7RL1iQYlY|i;mQ^(C@{&YIipXj~=D6Bs*su}qm$Nji*flv@nzg9~IC<6R z-gR;ln5CfN2B#4SmDPd|gF;F>-%nPU`_AM3WP!CZpED(}$~h3C6ZW&;I>THCY>0uY z@7Y1wH}H+_5_*qjKy;Y#^!q(<#ymuFqEpS-4L2P=j1@S14wDo9Z<}2&taelcWAglB zCz{FU=ls`x`|(K^!o~fDtKWKxs7|g<`94F$giBJmPSr9ZZmLX#Tpq(O^_)(7Q-#dY zDV{zj75#~aprcBw%&r#OzD%V*K!TO`2WQdfG&lAb|^$Rd3+x*i|x(cNiiaE5zzsoWI=BM@G zI02^gm%Qsgp~=6Xut#{kE3c zS;@3mucq@6t)Jsfr>|L7vj|+Wjqy}S^v#`Cdq}MJX2H#suNNZD@)O*`r%!=6L00bL z=LTs_do8)$@_%mHcPZi~zCg|^K_ea zytc=fEo1AdQsp}#6p*I!*HKhn#a7Efx7+vYfhr?n$uph?z$R>St*YLm0$9L-?SE2+ zZ7M4Alt2Ag^mQ~4Z~puM;6IOE zyX}Fw&c%f$;}25zZafhKCC!wgz#?yIwrcI8c2<#$ip6*s0m7dt$sfVzI~zclM+|x@ z@#N_&k$Q;~NzWt`h<;a`O!w|N_e+S5YzA9%n7FMTZVtu;GU4-=O2D@*J9XMQmQ)o1 zZtOB{)Qrpg58*CN7x-! zwv6tx;uPsZ+41oth1)bjDa$vv%#Se}gjtJjl_lBo?W@ft73E#g$L=^BUPHPsvfRz< z|JK`VI=nrYn=jcpIw4Pk+z`kzl5s&4z;45|r)mxSN_YI-nieruOCcS?agz1OPx{)3 z240#@CuV=NaT%I@AD#a$tb}xf3%$X!qm{*`s=sjQlBDiRTAGoloU1d&Kjz!xJz6D# zggsq-`-%@UJ*1QnAQj4o;`Rl~+3^yyc7Nd=AYC}?>gbSSvhz#pDcZ&%#G$@M>TS9> zwR-FDlAOQ%ouo%67yGgsr)VZ{Qeak=b-&%oiUh~I8v~DN2OSYa0faZucd~t{d`@d~ zIxj`S1_)+fut2vU+u+wm*&Q8hj2yRzrb2UlJ@~v}srEDSOa}o{-Pfy{#Sjx!2Q0(3inC#n_ zK*SICYVE(Mv{-#hJgo1XLZ@dytlQO)0CENQ(WMk{44(J9X|)#EUgZxq^O}aS+ufnY zZ;kEb6wuy<<*ybd;yZVT`4Fp_a$z`;*!EK zCVM@18rBDow$cCQtxuRXF5jb;FiZLiYUJ^HI@(-IFLkwpR$ znSknv0xHiw_aj%wv;@q6-&~)?peUWS_K3FRNaoVhlsC*uDtWOb#R2}9G_zAC7P&Y{&Y&< zm7DdB)m#cOjHSlJ`koQYwb_tg9omA>+RPU6V=d9gxdHj_XNae0MX+W6M5-k(s-4y? z_M47(;+TwEnW{5N=Z}GN?Vg;ONOdZAP{lHK-BDt*TZXX3ZBe~xW@ zdG0UiiUvnelzKlR9TSj3Z=NB zntb%yoe{)UsA=qmHgh|V=|yVwOHl{HD{3L_>85y0xBgFaTM`}AU(0FFk&Lno;$656{*dved4rOQ2VypGAzUo=^l1JAPO6^*XW z#PJa;{HCX4YP%7_QB__iK`i!Me_0P-w9f|C&Hpv)FOU4Pn*Rh zOAV}WJ%6bVMricwUHlJvYfE%A#iK(cMS+(ta!<%C) z>0)Xx@q=lVZ0Y#&(fv{Mv&3U-3rNQ(5_9$u09QnPg|et&;ge3G0$X>vK*AtR#>lH< z;wsN$g99sTnY&*fU*2sJyoBAWJx{6@V>zM07$hcZ>%A||?k@Vs)5$+6Y6Cmpsv&lf0rg?d>+ll%8Gon9F0OLTwS?=B=0YNpDvjpG zg&vNQb>A32APpOGfdmm}-$O74NDXUiXlV4jL%+!?-=>h^7x0M*4^zemOe^ya4OxDh zm8gm%jydH$Bgr`?NM~Hip!#T!sds#->To6>2A_0DZj~)(jY6_lx;|T& zGrm%hc=}_uHB)EJY7#&J#U>1&?^udQnCz{kdKRqHc5u%?O^Z698&ktn+U*GcI zXyK#AZt=ck-N-yvMa>RlpfEH4Ahz?M0D`__-~=yr zlZUhB3BBh){}Wj7(BG0$QkwGL(mU3MuA1@1ztN-##p?tha3vQ*iMTarg#ELVt3SWC zmH=eYw^NM$7}K^)$pRoIAPJz|>fvm?SeWSZUD}>TLSlC{5H(adm7DmYIc3h5F$ugY zQGoSM^`q|LlaC(^%>2nBA%k*l*k3XK*|i!gAge|MID~tPQ3V z)~WGI`g4XWg%sVeDI!|B%{Rc9JlE(>z|p#`@B~lc0kv5SiN_ZvBYQScMy2s^ zH7nDu66-xn9gf@o-UfVE&IoC8+*uDV&IW)OG@(MwJ@MXG;365z*0-9gr#vU&JQJpJ z;*a%Vu_vh)IFQIwl?eVwl<2=HCSSBur&r_lhCwO4kQ&tlji3`m}F9Q5I#Yhut^f7Lpk{actfbGJy<5H7-#xS1xs~bty|nyI316@TNa%Iah9}@gRbvMdcLf+_mBB*F%Klzb@AM{FejS zJ@_Z3sL0B1y%0vC)fNS$DpR**uHjSwaZ%BYXwY*&vlz=FFzZdbrw>9!I4HXMt5-b# zLqX3S;0i#HVxp=RPnm^iS~vfiR8d=!YuvhF?{)dvP~eWc)o0sY|Ls8S24hhJw>@M` zY9UNIx!9hIG#X+$R3j( zoQ-e`3kf;Vi{KRkyT6tCcr@u#t3^!p<73h%W#2^$GUQt+_;=XU{`(&NUQh|{dA61F zw)UPaEj{=>%cHcukEI=^?fnq=CNFFd0JiUrKJL7=osdScr|2vzNlqrF5cSYVDU~-B zBxU1r)Fndp*AO&}=eYJUVmcdB?@iceBtI>R3B*O9q@)BmC*CWnb8kF6cP^=e*}?1J zw~AYODk@Z!kaqEpGG>&23OOXj3whjOnGw=6hFmPo90UXZj$whiY7du)l)oXqEPqp3 z8#%xrHt6^9Y^6LgAi;slVcJStGc_;0h3XEi`HBWu4P(2KFO0ek7CG*do*_yqMlV2f4_9r1fhq<+bq{0V?{?lO6zw?m{j5T z!~xcsQjQRoC7Z4VG0u(je1RqBCk`QD;qlp7Ehm*IXR@3A5zH)%kN`R=rihhT`Uked z0jzOMzDSnKrq4b{`$K>lk@TQjKJ|W3+mFK|%5 zmj0)DLXD)(QYN3=XpnIKPY@e~oz;ZRP;>ndQE-e7L!2A;+dNNTAcEw3UaGR@=nMP2 z=aBfO)p-LiNR`d{smmXRo}XLe9p9U2m*= zCGdlG(|<%qEgQ;S$BWS3V(}}qhYiaoY!&fGnFk6*@h9D+qcHkcLZpDR(b}0$y?v{P zJvo&o*mwwXivf9o}< zN?3aSrix*{*t}*^vyb`uj`zlt;o(IWjiS@5Q%F0{(EAtL3XmTqV%{R~NVQ(z%?Yj^ zuZs=e;M0QsK9&R+AHKqqGCbt}3#m}?M=g(dgGB5lOgSxA5}`$5Cwj8*s2?&LqVwm} zqV+HsL9(BDXyvhOTG16Bh@n~GAZqt73Ag7ZTce`vdnJ4vPB5?4=vE0^XFSik@JKL4 zPk{GVuSv$=|A54Ua(ACgk6?nx?wf0ao`OOF%)emf<9CJd(xeQ_yno9aMamKzqxn1MAF8q_kE|C4RzgeZ# zPR63|e*cdgEP*SsgpGw=2ERz6f~-_*sd}-==_Gw;pn}D4cJybS@7>P&q7Nc?g3p#{ z8wwF8RG^@tC!4QS!tq)!`nUeRzPWC!Dwjcg{D0`D%IVaEO?Zb51Ve+2FMI;-75Zv} z6%JEw_3MU-mIH&cdEVUYJXycGDH6iIBLHh2V+j{7`#JwVL_)!1`maJuc?cYA4;I!rNx`eg``92kM zn%vLaah>Vsi{W9+F}IG)TN6L~sqI#fcMu_a2adERTee)3#DmJO!7^h+Gq=8(4CoW! zu65h`^|A4?9=aC!^dRfC1#J(?S|krJeJMh?O(^@YfRC8FGR z$3I_GeP;LB-nnlD_J^0Vz)t$D+5hY9y`@K1EL{)og3F>v(tCM2Eq}qsy zmvc>_Z5H|F?p6Ne=f#jAs@>G&<^!b-Rc7A-R>Bxw>KW+{1v5LD%A{4-WC>?yXX}`b zO=nFH@mG>xcAHw2Oi56W+XuH&52gjWjD)n5;@lteWwl*Kbq01YI-w?&7y*J33`t$j zcE=g&X6vI2kiMC3B8ZL_4jimcSfnkI`S^kFyHXZ9V-~g;=k{<;=pK&+jo|=FW#mt2 zjT3Qd;@mG!ff%a)W_+dHS`X)2$$^#}a3~n2Je1R7sA7Q`PA zWS*X$2H>#jrw;ffpGE4E+xA=CCC458J7ex`TFyHfVw@rp<2a|T&B2=;Ul@s=!(I`f zO#vfCc8=Q11Ji@n-krQ%A4G4lK@ciqen7yL zmdFfSp?D4&p-AmC2v7?Jwdon2Y?iL4+^Wz9RXk`X2QSb*o=B3~>X9P~D0kaB#hUDW zS}7gwkbgm_3a_=aRy!}(#J+`=&TVk!t^0K%xBPJG8_=1W*NBb|G?H zpWctCsOBCXv9|XC5q7UqlMm(vhVA*G@5Yjfoa9oA)}MlKC8bAN+S?t?xf>D(iBB2^ z@w>*x_iZiLw;D>MIJzoe0@CK9Z~NoFwTK1y7Sk}0BnJ)%5zMZKQ~ffV;2utZLW$KCC{SkNtY$ z1Qe0aP_c6T+7}XT<*^iEQHQ$w&bj)jV`%Iioa}_?sr>Hf#5rxe^^Ye!t&L*Obja>d zoPO%4fB~4$jqK?u3U0=Y{>}9VM?+$X&HEd!(_QZT>wT-+1~&34Ht?W@L!QOTPAA85 zL!K8^AhIznHF_@+hcEoP_hB&rf6dDq9ev7Tq$j2AiU^=fU|_-^50x^+MxG63EP9H= zL()mGfK4!7r+ddznbITa!8iI?CM4oZ!KeK|@2EaG`;+v43H0~ZcEs%c;7)G5oX?ow zqi)($6ig;QE1r%Vdtu}}UKGDE8^8rV8K4t3W@Bi{hMX~C%az`#l{Ux6-&ro!sAc{1 z)4(5VIT2|)dxH78{hR1FG5_A)jEgrB4!AVC(XZE`u1-$5s~#Sx(&0^rN>#6R-+uJ_ z;sIEljS&Ald<@LC^kTEi>$?QosL<$pvkk!}uPMJ8Haz7||FoiF8xUU81Pm?~un$1a zB~mx&lY-(s)?N0rSVZY*VG%!WEm=w!0=v8}3rX7ePqR1glM>~DTxuj+sbM6eO}r3O zx9Zy7N(yi$7YXqf0T(Cj@*7&r$nv633e3#?lAcG$%3 zv0fIhdZqT^U4-rr?yzN%$!^i(*D?K-Kx<3+_Tonod10VF$|_*k`TaD+&9qc!aPHPZ z@)f@A`*7+_??D}UBR2vtd_*-B|cJ8xV&y)wOznml01)QMbt54j~Fi+>juBEaR?8Z}_g3vYo& zuo{^I-4&fi3d)z}y@Y9_XcWb(D!lL~Ri_H{wBJ0R5-F5^T0t%hU1-j$f;Bb7@91@6 zE?)V@IC01rl4G@CI6~iSwTUB0lKMl9nP_N=x=N}|Pv*y-M15{HsMn;aAGzdWDd5vw z*P4MJyYsTe=!kc*-gzdGPt|XwT?-A*I=%|2u&h|5q~u2rn3ztxpYGrdbgHFNB==Ct z1H5HkHlD>6_+<5L?oFMjJUv$4aTHf4v-Bqy)(Nq&!vXs;CCuFT%=IybYPlPT?+D;;He&;MYu z|Ix#(-;ZcSQq?CKT7%R=qpY?RNlj@|QW&7cwA&Z4Wl5ofN_GJ|96F_dOL z`|_%vp*gym_8VNb$2!`QE40<_fe|(7#|4U{Fi%Ti&y@l*u(bgJh-&F91G;*_quhV; zjV>@^Cme}XX^YRJ?OxabvS&*DCG18eh`T?}tja7GPJUbp!?rgMg2p6 z{O!@TiVc3t@?jmRGC`fav992r9T>v1A{|G?P=K~@L18>GMwYO(-7b(bW;}bOZ)I!q zF6&9A|Cv}?ZHhv%=&yQGTLovV%SFu;Q^n>dd2Sy{+uR)7K$xEw?DUr z@NlROXRVl9+lnYmPQK=D+&2mmfA*`ZlH!UANTiAkQ6;&v>S==}R|oSYKYpUc!mnR1 z6n>uiZhVpY#!jD;34XiwwUEY$nJOABFdl%jJvQgx)90?igHf|$rS@xl+EpU-lO=@z z%Dw~)Zp41f4i0mT>J8xF%8|4faBGhYM?*N0+)mB;^&dv2Yv-*tm#g16Mu~(pmrNd|u~OY69zACO*9DIbX&|eswjT`2C;?m-_-QfSD>= zzSBj26(4F;|2Nau7_G4)62!?BD;p`El{h}z-`M-JKl{rn%X{kCFRjG9UREKoi8~G> z&;(ICVZ=zf#hG4T$xBRTI`-GZ{V9C5$~~-Gig#;0ZS0qnmtv0w3#Dan;C+jI9;US~ zHd^w3u<(zC;MoNAl1Css4|9MfX5(p>5b`c%ztR`l=DmAxB=bk*FUsBEfM9S;Q;YgE z*N^gwp44hn+8|_R3_o*{pNkY)W9NV=D(HO*$#hwOpg!UK`=0krNhcclmTgM#-9aJx zg`wfhyne^$Voq71ERNgs{BxS_i0Ze;SeJ`-=OY3IfGo)D`*tEk*1;XdXBSwG*M9BR zi@|EQIMT3Q`*dAjL=om$2O9jAJ8pe6 zNMFjtD_%enS7l3iZfNL)EhsQ{9-={mw~-_*MJ)KNlgvUGoZ}9 z(l5=GrG;eM(Ou8(@mBEspmuy}`S6LKOoK=KuKP$M6>Za2`-UnggQ1qqB*!X{0s> zp$=)TYri{gfG`(Qpa5gD+ts|KT6+`2 z(1(uKAccMtwpL|>G0+?OGI4=3&yUp6V%_#>52NHq1$F>a=e|kC%aNsUOSPay+Ph`s zDb~;P%3NdbCZGgFeJ8ffCW}*3fx=(!5e;-N$$)NI@$!1gDAv3HGx2XBA>f@0c{bbK zA2w`U@T9CQVQ`)Vs`U#@m(}Fse@yxabVn3i%_?VzZj1l33&7{V&1;<}rw^EW)$Eqt zx)swS%}*r#;SJd@)jJ@iZsWFgkw=!O7GF>!L9%XK>O!Zr=M$jx4dP%Ksak1iAex5U2$2Ii4fS4@h1)rozB}uq! zx~E&G^TdIx8Aau;rCdcN&bwGDJR^u zc~MF;#MI~lGj@ovDQLRI<+qs&tK}4-MoGUiU2b+35~hCx-vEJQgKyxBJ)N>N*&{99 zH)k#i?Uh0yR)4m~WCnw8V`KKH>cqYbuF^LJ+N}4cAbl}c=OX(2f-M*ct@hmE5a%j( zLQyjN1doh7RR3-Ns1Gn{s}2r$=oU38teAQuhsOvp9_arRAb^O3;Qm4FNw6>Mm4}B% zK!^ORsK|{bfead5waVD`6(^xX9AR=VQdab~bnqVO z4%AgPGBHNq9mdgiCSWSs6yNHf)k5|5Yj?)F4{py-G~wRGQgn8HN1SVL<*fg@5_S1y z))W?U3D&(i92hZJ+ETN54}s|qO5r}XI>ca&9z!J>VVNu*+!VP@oAL8Ryxrc5F)^$J z2~;_(7)?a<-<)o!sj0!wFQ9*m81sj)gcaphdCq>V`&FOU{GDNjoPSR4ldT~&uu#2% z&Y~>Z<3=U#Tav5j&CxC85XlZK60OV73oOB{K=SdzS=37a+5kSJGVYDBb*M>#Et@K52QG3pT00;{OuR6Y1z#5 z-WE+Zrv{6bn8ou!=C|Ih^Z&01VbS+=@ z&b7LI-;vk6kM|knjP={)E}{C}S5qH`=q!iv>FJ=5kQZz>ZLRV%*{%Q z+9!oFz{Zfwp&tRnlS^-5h-S>k(X#)3d{ZutnU z#f572`!4tGA-M%p3#Vth;A($gSh80*Jz9wLjh`@Xr=F#SK=uh!=2r-i(^aa zpTi{J14IC8qMof;gWy2VYt!RwQ4l^4B?3&_UJ}EuiV(Rh+JBaA=?5HpgIwg8Yub~i!At;P!_S@Y@LHv@${CxsX^>eHUYEh{;yI?TZ# zl)~d47fgLBnYbGAex_Pi!{7|{topnm=j7^2moySYSr9&Z)iB(tk$67^eGJBUciM;P zvF3N-ME_-SbI^{0yrRsrC(p@6zSQ795Okhc0Y3F}F6PpQ>Odd2&$t=tP9k@2MVb{T zN8fxa@3?BPR+ki+tD|ne-5ay70)1$EN^BXgnklA+boJH3OB0Io%Nj6*uMZ}qO;nc> z$TZTy7e;aQb|(i=93HX6u5og34AdRO6<=}}5`*e3Xn|w6omG6Sr<*dRgxEmpTK04?-K}cgXk^U5c=6l^Vc_AUkG+@zHFqL{XL~!UT76dnxG+im37w0Ok)`ySQ)V)2V;b9v+wo^R3#b! zNPyW{Z;I$O;-7w#06&Cn?#mtDy8HI03oXluWB#OctCP=mi{w}b!JH>gk;!~{bmv`@ zi$5N$>4xgxI1F0>aupez-7TQl_W%Cc!CKi8@XUe9Xs|}h)|-P8V8ByWu`!XLU{lK2 zZi+DZ?sOl11U{Ks4i{{rBY&UJq@6L_UEAZi@QpVv>uzuzMWAyE;I7?nZ%F_Q-X}Ih zRQQ{+rBPaLaW=1_iVZIFIbd##;gnxrUJ+%bh|4k};K6QJ)ltL$_}!}JaNDr_7c$*( z&ZrRm$eyh4SLIOa3ZMp@{MWV>OSAq+XxV`Kso<-2md`&!H5FgpeMRvnmb{pzo$tZUvFvQ=EzG`>Suf{F zU#*IfiQs#lXXcNxGOn+~+F8N$mk;)kK`uYCCfD?X@`|Icd+wogU3gMu}!{~09bZzGcmyWW}{HeGn&pc+mEi~kB zWqH@5_XjJRFH>5%<0_*({#r#&9keN9rl;$@+71h!`ya~LoGU5MO&N(t zZGb5P%`Vql@NHhu)eZC;^1C+0Yqvy{pv{@R-p;1FNNN_y7#leH>W17-p;#(zTi%)S zYja2L#FN$G>kDl?+zWr~C=ysLyLLV0(|k?9hRLnUd94k(RBw((=FcBJ0Y#Cg6DmFw z(3{Z)`@6m3P}Ue1F*!uLN0k-)&4wx;p|1^cK%6l*$VU^9UKP#lI!4#9q3Ic2d zixx4CE9|+zcBWxb8SN0ErKfPQRHa6q;-bPV&Avqg1K7HhOcG|k95-` z>eYA5=-Xuf4v%`hU6+HCIDbm8B%p=v(hz$hzyYXW)lJmYNd-AHFnwWR&UwZ*jri1Y z1*&DN>E_UTJsrUt5duU6YX%JF)|19CHwo@W-S@-L?t?#*x*u82 z@!^R5hzovfO_zY+koz0!B{#D7FY}Q{Am!c*Q@jJ2W`K4##d;LK_#% z^BkH#y^sOdGN`J2mH9L5kaL(n)q$&o?&>b`>-i+lSO=+PcKCZl zePNJ`ei`n4e?^<5urI-^aZ-g{)U{Rw4AJ{D9=z7eF;iortiyUlr)7MNp>jfkLAY+_ zy$T|atAV>#$A0o}*IG2bf%dK1aRB>mrM`n372H=soqL3;Ikl^?4Ab!D(Bn#>wBqml z^%5^pe&bsWB6`(6nP+N>!gnFBWU6r1S zoW{~v$A?c`X+h4w&$`|-C|}fV9Ceqix@3vz)mxO*y--TJmk%A-p%A*$Wg?6zlLPFr0X}N75y+mLaQ=s5Qh9xP2kJ#U>K8lp( ze1HyTLJ631c+$P|ydC zB{NEtc=tW;!83@<@ab-LfC$~AF^qa3jNb_)io|l>fymAP9v$9TqPpKC>3hxmHCvxq zfRAWcb|L>q?n-06hZbUP=9?eQPKyL$DaoUTK{#HU(s9|LvWm&8lmaany6rF<34|_= z(O2A|D$GO|{Nhg!7>%$Q=tg(WonYaG5%ZT0nu5=pK+U2A-WN$-6%vXkN%*1=Sec>eGsxC*Qi!XVun-k7AEGdq91XC5 zdVd{|wDdi(fRi}zlDbSHIF0WcRIUPtb$}38w$ig8ZG+FJ%T42>R%cW`yK^{lzZ6_o z4G637A1t?`Oj(YhZ;PV^vfRP3mOh|WU%qi$ZM(O>8CbKsF*3PjpdsMh&cjJ#qz?#m zr8vxjTqi)TtIbsJG=#G6&76eu)BHd$+-elhSkJMqE4k3UgWcrbK-J><<$QfKV4aog z{C>fzS>6HEo`SJpqA@r5l}eGZK!BaC`ONcZw|G2}GS>P2ZGYzKryEj92xo~`Kpj1* zII5w#ZeWpRW(Y6}Qf8mg;Quvrd;lMj?mOXZ!&CevDcS+OH#xFW?Mwqv8E?NZ0I#_Q z_dPo5*esCLxSSVwTbeESCO-?`EX5|14Ao5hor>NX6G0q14`7_?3)#P|JRaJqGFvlY z6S8?&v#sL)*1Fo-scs&HzQN0K8PTK z15p5kA`;-dsj~GYsM$XMb4EQ`(3O`C>YKXyPS%_U%xXKUiBTDsZEe5wrhvdib#W=$ z$B%#SvS}|ZO;6tL9h-{fbVrp=okVA}MV3w#zYAfZL+zmviSH8L#!N9V)F_8do>TsL+lzCb^iNIE*8I`UEwr;;XTt zI>Dpe{;}PKYVGK!)56hsh()|U5vKIFajfk68U7`wsSk7WbEu3-Gp!CR)-qQO(GxCP zXulq1PRA=B4L#0CwL$&>1eRI=CPgS|Ql&ofLOy*(9riQ8RCxE_USGl(TR%WO(FXak zwrXpBD#c_2;6RT$X3_+;SC-+e^?p-U}es%$9)ZfaMAB=XFo?4f2$!?Xdb+c|@ZcBaR_}w5S{x0xc z@2qLtcOD&Uy;|byWk=i8Wn*rHS3@dPzVQBl!xr=*Lx3*nz4V-vKo1+dIXq-{gM$r& z$_eM@B(EEk7lx^mWs1(H?(ZA->`s8#M!-%p?8uXst`{ExbhEi#RQHenoiEDC-x9DI z{!(Q4V#IFIxy+z@3c#I4kG={%ZjRB{(&Cd6A>84eOP1Xb^18%JM+|!pn?+ zU97lp4`quvzL1NiK;9Z7Eq>*Al!)G9S*ue=VbjSDhLaDT*DrPkkopQJX(4}T-2S#a z(fHgJM#Fv1ldU*7G> zTK?_nu>EynPgS!JDAGghst>eIdmPqn#04!EA-YU(K9)f`7EZ!&MmtCPa}DiifNEk; zaQ*HnbBu_~a#WA)Q;p2^x6FSIz<)q_X3UZyn|}Q98-NdWP7s;*Ww?2MbvJ z_N(tX2QU>^GT=ewjt%+qGZdVPyMveT%)$eS@d$Cmxz_Mwkho+&M!yBxU!c%1#%4d< zDL+Ysfj-97_c#Hv-T}fA6XSkAbsJ0+VVc$z(1;P;h9KyyZSRcDVwLlvW=s7YpEb1p zgJGplXo~7u)#GkW@HqVMqoc2z7`H~*!h2dB!#kEvU&R;kf$g2QCP~Lvx?6%4uCXH2 z`yM;jXYp#Feh&rDdj3ay<@J-z@~2oW&7V@H(gmY8EN_6bP?@7v6f_ihq}H^R(9i~U z6wec@TLPt`KrU8}Ap@=cT4j<{LB`kR*`Y%49pTNFQ9Y%MH=;9kiZ2+HkZJCmnnI~O zPz9h~JkM>0#mKjZxU{+Fq)wk2Y;@+9s|cVEXIiJjJY&0PYc`6Oz4E2f`u)59PwMiT zsVbMm!|9^r3B}4-HD1GB1Vu9m4ipDVO$6e%YftODc@{7v8sC*~R$33j3qs&o^=MtQEPB$VJ9h4B&?P}Us^4#fV=f9 z#p?LqMX1!j1yk;+uEcL361i(e11CW3eDx=conUC{s@8Z(_o1H zxmdrRZ20gAr!1`Xd!w;Y@>>8LWHV?+GHAxlJCR;2RXRBOByp;!z-#;C`&3qmI6w|=6~PR`z^1B&v+O|{)*snJSq zJi%3;OvnTS0_Vgm(wGHnV6x9yCPGKpM1iW77Rk^YBK{VZ@mp@JJ(Q#0I1Nun{7%1I zk!S!;YCGK5 zHr!GB)VbN!&p#{3hnFo7@FleQF#th8IW-J8#$Uiy*L-Bj-~$%#?cubNKbkl1mGd|6 zl~yOph3D=s3Q08JGCOvwATrhw+|P7;cGmBF zv*m-|ComWf&)IigF&wKDrJkGoF+VeK5gESA)Id*QQ0^IT&Xd>D%mpDYkf{f6o(Nc3 zdAZC78+J$c#wd>WH*ks?E;cJWwVgs%ZpQBFYW{?OWBKeaS%%G5P+(FG#%!x$W#Bp`j^%sainAm-?a%ac|x zAb5@)f*mZk5dyga^ECEP&>*EBW9-}w&{6^De5w@MVc4zz4);1aeb0EjcEsDlvxLcV z*>=&H;TCZafIz>V4TZ*V0w|I_*a=C$4_0CzU{k!VxQ3qaN4vsxK1lVqR(iaPAk&mh zI#$=1ctg|=>dBtjdU6Z(YYL={ryP`+L=)`>? z&a++3NY#JX0PBR3WH~2@#7t~~i*;1w+FB`-3-4>^W$pX*=%`%yLPE$MF95%W+4@|5 zaoZ{)YGxFw*3+Gn95>tlK63Nu+8)ZcKTE5Rhh3>U-i<=Ej&%eqoD&7__9foH;iVdRQ#osZd|f$^sTS@?gfUL3LesZ&FLNh+;2^UBh?3cN1zWvES%Hlk=pKh z*|k6*C(u;5Uu35G0B_2PcpZy#0}<`W6k>ePXfL?a;s{?FfHssMIcl|?XA_z*pJ8nw z%BBPG-=^!E@2hFO`u`!Na@&yg{g1h~{)+MqyGMuakOt`nkrFA9?oI)vyQHPNyQI4t z2@&ZOkd6@q0f`}1gdwEi-1B+g^F8N>^9P)@Sj)9gXP)Pd>)KcCokUajLZ-BcA+=D+ZFxQ*?*_Gnwlhm2nCau$sPa!IfO+EEQ! z5mL6fkLtaP1Nnoo!VXgJ;tOY7CD7?MRBZVHNJ5`KXq}GHH}9VdzzchK|DM6r??7(# zd~{_Fx=@Dl(5BaFxSf--m<8S6DG`#Z~IV7MUomdW`6 zcKDU_wHS=RYN9sAl4yOP7j*g=GnMyw<9O3% zDH9LKo|}kEc&yDNNsYx7#+B2upRT(tDoh@SHo=J-&Cn(CoXX!j(O~VD-~x5M??(Pl z|MtSvoz|Brrt6ruGK?8Ksrs5wud#g*vX4tZu6E)>Cy`6f7Qbo%9||^hCLv&v_Bq_i z@YybjhA#n)+4ayn#fo#Q_LPtm>~YTN6L({BAlqr+waX7|5lS&#BJE53-J5;5A>g6E z96E9i$B86?us|k?Hk1xxD=d)M0GZE=hXA9XLp?vk!f!H^cYy_9=3o4HfWQJeqP7n@ z{-6F?a$~>dB+X9`XL|dz{c!h9(^)Ij_1bXa_8kYrYwk(_sY`{YgJj0&mLw?fw+m?F zHsc_jRCxS!RGC9qx)`#tu+X@9T6E)Y^lm4k{Uo=J&+*k-D2+u0k*dF3iIJ+h&B@AesL$jIw$?NyADpR8 zaGGqfw%sUEpdGotQ)@<-cj~3t_pk`EeSwcp`*!5e`)BB0Q7}Ft$_6ftjm5n~48d9N z`hawH_OD;PDn(f&q-w?^I7fa!5P5)Uf91_e%Clq>p1Epn7NbfIZ}=MpfnutgHKp<~ zxp318r=VU)~;dYWJ{tg;c@g57B0cl!9$puKx^@pX4K6wr&`Jugc*p3qHBEV2wj#1+D zA2nmN^u$y6|=KZUJm$rbAA}4NS z?Lor-fyP!pW7oT^k49ZR!=om>R#}9hBto(nf-IX}jD=TFSv6{zJo@jF#^0$A$m@LX zy#VZTZKK~|pQ}dKc-L?RD3PS;35)Y1nMssObVaH>ew7D$`8+JyHvF*tE?%R{qWtUp zENpEO=IKJ6p!FWvg%FWG& zhp4B~H^4-!$(|TYT*mw?krMvXMk0}I<2&U1v8=C=Ao%XI2==vP$j9|}_)_~765!pO zol4i_jgY~>-zd8f@81Lb;XVT8biZE-979OCzYF&totmHLescBi>3rbg z?r~p>*~zxJ3c$a6X(;H`MMD5+{Djv1aK7i}nKQjoh=Sd<(m8ZK4$bD;B!Bq+AbtAD z(dTbPRug#CPR4YoOY-(WNn;iHQQs-I$<;!m>)ns%OLAJ?n`RWPBV)eQO?I0W0$kZe zuX!jQW318&3BGJ6?GnExlo=W#6v3}W$GlxS?ECgw-~i*DuyEMJ(Zlb)jrk52cu$r| z|E*lSw)HbSJIA*JZyt`(O>S=1OFvdU#?LG06xED97ZUPfl++?!auVb+?(}qe|5cZ~ zqfG}re4r_eqlqHHK19pln*iRhb%oF7&H89Y<&*t`-wT1dn&t*d*!49y&l6Fc)G>&( zkw-Q1`@taT2HeL}N)JjHFV9DXFGGmx2P?ofE{-UbA|4^px8jrK#fknl>@I#1ZP?E| zBFQS1Gm_dW%D?L&?MnSTwh^emBul_i?{8*;@kI`d79BQgZoY1n#9njn+%rYP4~KDl z?mm6rIPX08*ivs{=1OS%@@34Zr7LSjVy--OG@DBb<0zE?UT(jPGP9W=7u~%kI-`f~@cF-DBMHiSR=v z_J)U&CE^Gu*9yJuq#HiV| zwM0!t33dCA%li$A<@jHZmD*+Pc=C;i#X6 z!-P+WDlGf>kKki!?ySEq8hY=|58%JOe@Z=Y)o?Lv*5&>T(^W%Zi-QFaSch{Jj*^8~ z+DFwSSG_h0`-3&rPD!*h>jS2$z9$uh05F}GhU-wsW}u*3(o8Y%%jIVF3n%*JjUub~}G zVlDI(;Wp=BRMS5z(cV9OF>=*VHMW$!qzca_wogl;p&N>XMM2jb>P4cFrgy)BhhUya z8)=_^j_?0Il=Ai_5U?XW^sX4P_~j+VnAFA4f*L@nZG6ii2@JH#g+mP;*xTBz{G z79;5HSKZ}F9S@(!E*A+Y**si`1bY(<8Q8sjE5}YU&!eze@8ZWC_sxeZiBfOjG8~Zl z$W_#cv*v8Y4VbB-tA-AnsS;1Vzy8NYH+leLJHBY_#a2pXIQddici%T>5w@3VgzMh4 z?9>^q2Q*cVUE^31CU%sYt2On|az{$&2_KqA{A);Nx;A*B<)2OvIZ$S$KuP`lo*xEZF^5x011qUcg~c5aRVVgQr@cSMrca(^_sQ{eH(DfINwf9N(%7Wwf;;;N`H5i^OSyML9r=7T6TQ+BM2|>6_kK$p4 z4R7GH{}i-;Essmv2B4}H>>>bchKr|&vHVY|IDYc%eHXX=_Akd-m3p3u&mx^}E->X8nZ@m3Uc8FAMp=!xKI3dA52WUVFnh~eiv zKEfD{nbTiAY@uLL?g7Qr)gBu>@gH~1e~)mK#~WIAyK_cSKw^d23IGiAtJ*HEQ;F&E znc3;bdowahN>~o!;R^rJwT)WN!Y8>W+%X+2cg)ATrpG1TOB7i(9du{GnNlo$%aLs~UG>kLhI%(M7 ztqfBGDEC$)m>D*n^uK@MU0nG=(Aua;RY+|tTY6k7MYXlZ zXtzGQS4W$du`p0eiWr5>tjx9&{)A!L$!QdMKwQG%bag5KT*8 z@y*y9&u}G)I1D|IKNX98N+2P-$3G=jcq}HMC*%8$r7-ZXf4dJHSzcaYQ~fn2a*ZXP zOZ&mnrmLhDiBWd|C3#-%M(eV@pS1{*p_bX_Wpi)g{Z+@mkEtM18;@e;Hrigmd-jTk ze)umY*Cnr5cboB2Kp*d-#1CZ4Q;^ex_YFQ~3r_GC{E6`u+e$BVS!f_zlGlb$E&?02 zRmlD+GNv9mUcWQ2+Z zi?wGCY zzBH2G_%h-?Cf@0doY;SRF2n;K6eLvdrpvqex&5Y#UPOO4x$>8-5Y{uH9AZ6ZV)J%` zHBWF?tEty@PHv9>1%DRygc{M!r_$T>6itdBNyEd z^5>R4+w;mUcs49+)8s`%Oav0RS}=wu`~rmn*>o3VHt&`}XR4B|Cls%=rhJ zP}Q5N*!&Q~ch9tp^&OX^CQqyQ@&QeQ1rW6Tc0}*{^RjSmrWEc02`M!jKH0I$mY@r9 ze&nxXEkjcAdvT+J?g6u+q9SF>%{bO_Ut(6$5jkg~4z%r@+gSbT>xgT&?#J@V7Mw67JWx65foe1}ztb0xkVGTMuc zz*W=bhOIAuU<(du@$owcN`pVy0{!DjELRD|%<_W{*K8`(YOD+AQ#LW-ZPmXOLemzLa>Fo$hu#LFu zk{bIy7Z8p?+%6;gUi3V6?dFv~9+5C%*Ra1ELWGzbb3h~z-om1*Wv!~g524Ch`8~h{ zNMx?g6E&?^boXb zx<6)`CBhc?6|P30KleoZ`46kBCvnoj^{Doi9eL!@+RSk|@7{so@e|XnpBRF>uCQW` z-d!}?@5RB!fmc0j6~TJ(E~Mip6sxi6T|M90RwS?#d7xX1 zj=E{Sca$#NLsI`-&Cbm|LiYd*4!b$(ldw;ePdcNh7a3Xjr0jw@^(1+$Ea97joXq^- zNH;HYTnpch!{5{tfux?QfgjqVmuZzX-@G#@>zdyO0RI{%$1;3*+INZb$LHD}6JpVv z{b5`Ar)REU2bl-tDsS)n&4!gy*pTy3S@PS_3%~>>-k8jN-H;xPI`2Q3M+qgugPg5+ zAr-p?F_G~YE04OEIa!X1RhDnRffkQrW8>gdpL5yI7`{)zt#aE80Ca%7a-kVDg$WOJ zwQ(mc?vtjigx&qn6?!(&k@7PXwIR^L9t~7m6)=4QG+|hQ>LkK}sw1E)*7;*?t#l~5 zp-yo%m6hIH1G320$G1*@pU+5P8S2lA!Ox(VL&3Mn8>d$z91y5f`r`Iw{(_jfv&T0Ow>B7eO^|8XknMRZKgw65BSyMt55(^#Mt62GE#b*&f z{gUHX`@MMpphHuH3sZN;*m{eHOFYE)NxCUD@2pM&@AMu?$Bd5xN{B}#EJaR6&SR4B zFdBRp49h8avDx~9FmY5H74swVm--EUahXUwrLZ;U(d3{L%Uf9RHQTX|m}WVuY#v@1 zJ99Fgt7odr!|vHiq%b}O#lC_7(Nr!uXMYMB4<3{-h##MuQbDxDl|O=k=t1X&_twKL z!2>PcDOIW>b1}PnZEWC)h{x(kg>)y4ZrPwbR6r36E3SUy??N41{{b#xF?M48ttv)@ z%OmLG=mz_!?}8RJ53`FMIqHPrfXSNGRg5SW74RU!2Pb%bn_F}v%jj!L5yV5urM(;9 z#8-KVHQe;i3CP^Jjuka`ZlS*9&SRIJ69_n)Q}DT%`aJSfEWx^L{aZQRn`{x_Pfr@} zU(yDhnKN5ax-;@kQ$82|{m+rh)`KRS^j&Tx1H(j&Jfj^LS24XUdO(D#Qg_kYD}};3 zBKs1^J|@L*kNnLS40l_*uhKret*JF3f7q3{tq78-VrS%5(j*yn=R&F{nf6ylnk?-$q7HZ?ZZsH28w<{$5YofaT!{EvGqh2F= zbM`po1oYL_E#3$FC0r{&Fhh z*Ze+c$kB@LySxaFJ?gt9>K5lI_c0b^zYg2MDRtUmZ1*X(@{SwUW$a+{s6>H!!27NSZ2su(M*y?2|G(^GfKtSs`64|10upu)>%s>aX*#}pC<^0fOLm}QCNNo!!WFKy>z=q z^cgioPAMcDB2xHuEN(cvNR}}afPEHwCE0Y+u|Qp_ilDWRVY1T7BDn%ulX0wT(lJA@ z7e(@haw0+SzM#;?-b>^;A6!D87^O&5-z>~5NI&s|4l53wCG}aS10=V;n3u5sA6sER z!TrW)cAqPfWYQQIUDMsnBKHZnnKHb60E*Ro!eW@wL!s4-;V4B8Q&QxF)I?ZVENH`s zAuUbgHO9VZgp`)-OZxDkz*7$^w9KOcReJs zOk`RPY(-v|zD79@MWf&v(Y+I_1mjn*#{1J?=|D@A`%R(7lDaOO(kXPjCS40EaxR<5 zgk}?^Jhq2Zh+use#=~~e1IORBGsxw3qN>ed2o3UKQih&>(0BUQ*@JP**|FSqOWC?+>kE^~7WSaN#yFH`m!oY&_CB=txo^-7qZ z>H;PwxWdC_GW_`(IE0y+q=P9l8t=wJpc}3)}P&+yr8a$9zAp26v=MyMi9Tm?Pvoq$MD4tA} zq{~W54pfn6ErTV(-;QMi6?q3VVY1EnnSqk}Wr$-8ZOIy&KL zTSR9U7g~EiQMG#og7|}x!Lnv@8HSBqoq9&^Kzahb7{|lhENg2A$uTz+g3okxr>Y0m z@3vgrer+p##iGO(YPF=|t z0HD4vnt)uzEB9sI$~WrFgjriLqcBJsxwPxpuWj8s8*4R`87xKSt?N14@N?r2a6Mgp zn8y8;#L#M#-|b289rE5xL3lZ5r-Y{Z=YK@)%~PrAZRVQHj~lx7t;r!Ia!8F1?T!n| z-M8Uc&Gx@vkz{4=-y@G1In>&S5;#(4+$pYlJpKjWd)PHC>n)+}yI*LW3pf-dQA4Y8 z4JqytppU!tjN^li5a#HNp}hc5VB6Q}NBbk5-dp~^{u@C)cjCsr!+Sz~{1mwI;=E{N z;e1nyS~2t;@gE$(;rK##`-cEA)En?`x$1HYI^%kO`?+(SlUC8TO_EGvRsaa7ZI(pS zjWB6Ajjq+{hihC9=RWhs??$5A7Nq3nK6p^1>1r$BHu0gQ_Hk9JV}yM$ZYxl*>(q{d zLKtdRi!&t^vA>M1KUYH`2_gT&r?4T!fq5WJ)7+$ty#QHsZpZkzJ2|k4f&t(C-3SAH zNHHUpgtB}cgbVb?VN!FIPi*VGcyuP=q-b(zXg+2WWMVxY7=K@X!oClato8U|Rs3o^ z(ttV6dp!ulR-+(PrZpUcKyJZ>RGAbm;^&x_uV^-G{N#8^N8><2RW-?VG@~RcT@29* zv<@VR+b_Yq0Z}b(Dm5Q{ki5WtuV6?yaj_J74(bDp*X~+5 zX9w1L_)sL+R&Uz}d|*fJI2PAGBOTrTeHsA2A_olXQJfPS!nE#Wa@37T6+6~To$sC$ z{%l487^Ch+C(XKD%sKFnW?F$MN{TLV%H!#;6@>Yf<^n`f%xoj`*)qOxsxzi{KMdMo zG#$wtg;(2dGW@;URtyyf<2?A(!l4*6ULyGUwaM?xlV{!XR})4&T8|(iZAjr3Vd3IQ z{@t_zM>jkhAM~CyAD!|v-IOJ}!Uxe%Wf<1z$VPrJ=g1V>`{nAAQir6K5+R1xU-Vw! zF1}p?P9s%SrzP1T*Q9XFs+!~Q)loR|j8rCR1k+}EV!}b2U4=4L(&HDs`1q$}PruBw zsLaj>plqNSW7W*j&|h<#v17=Ej_%0cb;~c&SX$de8V;xnmP5?$2hr@R8x??9fvgE{ z;$H3tBhdMZmqLn0YP|zmtDBvtSe_8dLe)Hd`PH$CbS;+ucsT{1U~w6@;dV(}W4iR6 z3v!!wm%rh};e0CN&XuS_mRHKjv160}7zzLN$^HePAG#U4 zYz_G{sBNE6bZu@>K=z)nBy9gMq8qZW|D={9@5#aVh*M~-1J$Ytfmgth zA(vq%Nl|bcIbHB2!*sz*(Cc^*=jM)D1Wm4=zaKr_(AE6Cm@dZ7iHszX*c?T2X^1P~QuO_T}wVwEt;AITn#n0&6&ewPTrQL&PR2USvBL!$xYG=n{ z;REd~6yD!r2tP1ewF(H89n4$jzwpOb)6bn<{s|_4vjYmFD{BaSkRs*eviUyZ#{=$w zdD4ky*Ez=a9$Lt?baeFJ){E^}7zGt|#F}$-uTm|Kvs8p1^G-uKPaYVj{U#*H>M}f@ za=*P2D~m9RqmrgYL2BOGyh3lFc2tI#4(yy917UJFI@GD_MuwcTk==Zunpz}&<@IHs z@W`i&M(-Q{!Oq6s+x5;Rq^8oJKObbt@b4aQkKl56t`+h$!=KDgt z?9XQ6amsNzTjO%63`#_~+(xqcwpq?tOqkaV&byCZ)WpDqjqMr%q>8#a9rbeZh+uyd zHpC?S?u3~LOT_R2vS9jX7+1^vS4EDh3-yk*wL!0g_4wbqtA})9yuqulv%DXk8xL&C zM1vY`kWrT(USLm;B74hUk9hX97z=3?PU82YoC0cc0EGQn_Gq#iv#2-eLRZOrIs{0+ zAG5#w~el#4yCW-1TEBayp2euQ<+4&oCcC*jf)$d0C($JCXi@ivoaEUcL4>II^SD(fba8s0+JH#g`1auQ58 z&@PdYzI}TI`_OeX0*dPhg=&(ltoat0FKz0aR)GIM>Zouj2Q`@=!W0H&I;qz)^D9e#sjIKs=#T?(M0`q5Fs?qBwvr91D|U}t$)fv4(l4>jm-em%k4os zoYR4)c)iyrPx4;|q8}a}#>g_x`dCDw{feIm*v6%z$40K@LpuWSF=GE#u@?Al} z{=Ew*+;)V@LVB+*;%@_~7sJ(#}$351W~Rv{+W*aqYYg-=*w-lG(cL$J zF#e5;vX)M4ymLH*PESfQZj+df^K zPuIWE53!h|fOIxvB%z3z&?!$SYuXVmlCW?9*#jnsE#8ow^0N%Mm5-@HBpjU)2B$Xr zlf7cc8_||YcIJ6eT-EjXu;K0g-(Q2X$ArQQJ~{0{E0*^5SmSkdnlCKT!N+Nt7{>mf zd{%9Z(oER$y569YTu9G|!*|-)uxvEoRC#dHJiCvL%*0B;NS%51=Z~nLe`&$MZCO=U z-eP1adsNt5vT9~VhNX=$4YrDkrk+kB5OSKVu6xFA|J>o}g)z=|=oIoq!ouY|7_y#D zQpd%|XCzCkG{}!F#cZ63jyrMxHk~W-w-Opa8r4zG(=Z4jK6=Ih!s8xh9fL02zW^XXK5Q`IOw!j6xmL!ESs z)fSt_Yo-;(Qzxth!UON@dgj75HyMojylB>pjS&Nnu0E&%?vwOat;=t#YwM+_5?-h4 zrI&S=BI`Z>RQ;i$(ydx4UUFKK)psX8w`R@B*&}AWRacuNGRYf4h?Q7cR)!Lef`W+_ zj)^9-Z|A5WMi$N<(;22fsA*^zE2)KK(mY*-O(oUJjYefeMKRje(r94K>_d%SZ{Axo9Le7?(*b;9>%a3{_e?vPM)tJXB$H`jvN zWU>({FHeXam4;o<=O}5YwQ+i_X(SrN7Fhphl!VB}+EqN$KwhqjPvX@~Vjv`isdQIR zxPYs9eT-!Q#h2&&I2 zBceLU_hy37G1h2E@g~sSX{M(8J(-G79VCx4!B}Xp&@k6jbm}zOjo1rx0JA{=ZRyI z+E$P2Tqo}YP31~E#M2Dth8XQB9<)y?C$_z*xrCa(UVfGO?41AgX?;h^RIQ56bN09( zDLa8->0+`BN(3+&#>(xzJ&P;ng6d zlVJCBtFW%+4t6l@*~228YR-@i+Cx@xyx_Mz#vtn*3VuErXSXOmK|SDY)b@^k1s zd+As~-$KQcaFk1$ne&76;2ANEsp94G_4pJ!^2l4R)l!E~x=xU9JVhfF`rj&7vWri# zQar>Hl>fD^YlOEI+N-1RyD%FB+02`%l_%~(1)oLorCzdy89uVpt+bZun`IKg_UJ70 zz>k`E!#az>mb;h7QD?NPuLj-cQ*^x*-of`BQ5|Ph!5^Q>Q|j^!A6)I7I(Y88B>4LcG*B;Z%8_+wBy{1y=O%7 z9a?bI68!VqlGxO&K;m?wZb|BG5>p(0p@!Zo>t^>!YbTb?J*WwrzYZ69g_ssuqS`Bw zh`bd`Vn&^niAn<%a``s598is|M6T?&;C1#mA*G+ka`!z9VSSjrp3;O> z7QBH7W z){&~50$r-}do8%U(5Jz`mR#TO(FKdS=GG-_xPP_ra^^S^($&Tt?|o0Bk-&God-x3 z#k2?@@->e@H}87}Yc zU%c|ME@z|0FRQA;sI9GRC2#y5-_p{OUms49!*J)|=}EG-w$_?&Z744#g#J6ZD>4u|Amk-Zn*#EL8JZZLF&ycsSY z-oT-kj1~!dT-bBR#SQ=U8(M1oQ5v2l4bokAM+b*No*+05m?Y66;S&=}0D5DafUAV= zq8o0Yz)mvw!MMxyWLpWejDZzf4ZK*p{CUxmx82uvBFbgkh_>KlND z3fh^M7u2iHLUnJ@nFthvV@E*&a|814?;4Jt9@yvGdx+N`A1d$lz>BvlRSDz&z|RY70W32v z0RbafJWBQ|(+lu*Ac8=|#{D>$Nl}E@k=ljL*b-EUr40=<0~@Z|p%v!fY^5q%4ceQp z7N-1rlsq~Io&xNbPmlB07Uv9oQ_}}<7$hoTbF?0>Et)A1EhSncW}jE)z^M-n4Q-tv z*42{(>SoTX?MPM7*woZVlZ7u|INsLQT7c6DCiq;1slQ==1mu9VwLgt^DRf2(+ERLn z=LGPWn3(MB?L%wF6`4uW)6+E_M^|X{^!Nk>A|4AlOOl{rA}0fUrbWV_H1A!|O;Uer zYM&c*cSEp|p&yhuFd%g-rjc>k!Uf%nj*fnmDBm}+twFi#1q{;Mk001daYztdQUIF& zygF2h_#boawjve5Tm{9-n?kRGB1z49w!#(|_Ce0?mqK9C!sobB zgd3;6kgtStk4fZ0=`jiA1P|}U&2|69lwuampeV)l!{AzInQP8{4X7&z?RElB7|-ta zBI);o^ey;!jCFI0gtB^|2N5%SWiv0Rlk(?5K>G+2wVH(n(r)y@-p~Y)=m|jyJWO|m z@&9X10QX)plla*0B_XQ$Yfh-gaJB|GbA9VLc~hBZq%8f1UTQBKY04JBxEVM|U|oT1 zppc)xKZuY)v(*+{SU@d&&AE!3M2wc;lW1STZkG|?t`Z5FkC^(BljRku9K6k`3QEeV zgs|nXws4qdiKL#MT`qE2YB;>DIrdLeo(rL_9w}#47_{9j2?hm+d2kaG{JfMF7Nv^f zn((cNbn~}#@~^FBq)q}qAu!ahEkV>#)XyN5Rqx(EQUkrV2VLXbS-)SXPf@)3Y<>U> zM^{4;z~jJv0vnuN1=-l}QuecUqAM<9@EK(B3?$fd;VDYB7suO$cX%8!3%Q1JUmdXq)pZ#CxEK)2~n0$Gf@j9hTm7A>(NNe@H*rFk1rc} zn}i;sa$nBRflQZ0rNAxq!%2{HcG*eH$}pV$hfcjDg}1nTWF%rgZ*{(^CIL zDdCmq`*-oXR%T+{X^PCe!oo4TyEfFqu8^QUI*F?>jmpD8Qy!VqWO$d+&+lW_n_o}G zz`h`dLOzLt-BYjf$bXLY=1YJOp`4;HwY-7?hI*k`EN~*Qoh;K4U?8jIuq^#R84}rB z`8(fwz%TcXjSGzGj5hWkn{c11!%OUa%R;V+{iCC3t|uTTTU+Y_W)}KUe70iQK*b%B zkXXVj0kuxbd+oD-s{TuDIbjSDIw7p`*HG0Kj7PIEA@fU1F`#cyLe&hGBWjvvWk(%66-!NJfluetTg75jt)wMm7`gS z^7zz(I`ZptaWQ1&v&-nqi&dFXBt1PXBO}}@XwC^2tV;?i0HlC1{4f5>2~f!a4N720 z3IqQwr`sC5d*|*8dPyB}2|)tIcR7aCYoZpH5wh!7eb=pqiSRAv+UU zwgJwfpKwaQV4*8C1wRCVP<%e`vfHGwLT3FywO6?&z*V$BLluvcbIw)oNE&Q5CH^|$X+dn@SR--N7ggC5##*-qUHR*>8S z2+d&Qy8R>uV5w74mdPZ%uTiZt9s*ACKioP;yHA`ig=HKidCsUsL4+u+{XU-O669RZ z9?s%Bqd$q>e06BX`D0j?ho8bf8IfD0-v}Rt4eamlm;bC6qFEW1z1>C1nYdTizFez4 z%e$GFiekY;?lz%AM&gk!ITn7f`mD01%JQBsNR_$p&7(al-kbx$B&_$3Ot|4|s+T#1 zLVH8Ee`LpP7!TShS#@ZsUW^nPhvZMX{k=f@?=pOTxy-r~D?0tWH*2DcIM%j>but#4 zwS%jjKQfDBrXzE`!P80XkyL7c-2F`Dr0Qo(e$nLV3bFe6ERCF9ZCm1+?*U&c@YKBW zp_FAQF1Hd{{Ns!IW475 zXd&>g`hN~#<(ogpCw6QE$dxE&pQq0Z7=&gbr;>qs;zCkjVUuaDZ5c`H~e_Ax}H-#&sO9!kHzwSjy1uWbHZIY zkJZ;H3IVdcSdZ2xka6i*HL{0`|KA4Pg}4p;13ZfZLk z$6E5!c=+XapfiAFZU){Rl$Qn%oHt<&6X_|(OKTf&GirGXnwtN-=M`7wN{k5D%IAt~ z^qporB|G4GW$pgV?ak9nW~I{eXNzQ)dAUdKN>y$F{M?yJt5;tfxm2`B>|g8(b6blu zy>;fyNoadPdfMGgO)96(`2x-v z?8{of&r`(9`lCoVN#$U)!y=&Yte{zx>M`4V#gi=Ggs<4U9SP~5v`l#W@}BIr2#xF1 z*3OGSx$+El29zI7yDeIqsn=<>v07&-MOp6u2cX*z=}|f>?e~BG9zwuK-q%J(34jUq zSgfeKQ#=8HSOmcx1ui3Pa#>Roq2ppbn3wp7n+F=s(;_xE%>}^VeN7EkPY>X`J*=3) z4htC^lm<}3Y&u^Abf`!IrA2Cd1!aQ+kkvFcHU_CwV0^(IJAZzxr>6(5eCAOeo2FqV z0?Pp4a-F~)$Iio3CDdR3q$@NO>FoS`?l{4E)QvhSD$4cn$8)0&cQgQ~fZ0_5b_}$T z1H~EOkDJT}kb!PP`RWISEnu(!R2TOCOAP=~C_w+XxVZg$rzNeeBxq=8*+MR~VD|VK zlEUJMJLQ{MtFp#M0`T}8$RZv8IyyV4Xp`B7RyuOR5bY^ivkME6;IV!+tvG)!>+YsD zX%yNq{J60Qeyz!lDn$qalRsWBz?|`>R~PWsKttH=>cm<=NT{r?4hIDV1tivitWtG- z{TE<;=YY9SOCzd{;pG<)ujdV0ek970D& z*G^YpVPRqC;zFG{1+{LlrNh8-&w;Tq(A1#@E%=?8nOTA$*t4CHcXNZc2ichP|GT)b zw6ntmJ^}_W{`3hCf{_GWX=aZDz^~o&fXGSkv2jFPX$*LVUu+Xgy@>!M_=qKey+t@N zAo7IRN*5O)##Bt;o|ToyH4O|xIy`=BnVKd8FXLwUPJAc$+05RFI3-V4XXpMcSY}WP z(0cVseu(d%cik}I)k~sSGb<}p0LZ|1Qqa<(Lr{^C+Si;Xv-wblhlg9&WXrS=2dP6= ziv;-5((Z1t-|~1@McqOGH2nVF2%E1^L4;9Y4Z;Q<2~*>zXJnMs*W-enLmXjWUlAj2 zedYTE^E`5M5bFW@-I;vM7rUWmgL()eKe0@Sf zLNPd$ASRstBpH zo4`3KKf$GJE+H;@4epLtmzJ(>1R!=rs8oafJH86w za98KX=0ABsNh~TV>I#4=*oji`8sXvLz|usoA1TPmp^Zo17F>Ylaa>112~mhGpFDigeU{ ziVqeIvX=AL5_Hb0o^=5xVY-Bjy1?^M1Wa4Wm$N%y96&$^VkIq-W&rJg`QHGO(ET8A$H|(o=CD)sWShS<@%9$_ z8E~qWFZwRd#Dy+-6hwTq{(tzsEi6>DMh5Wq5y#3NkFgb*J>1{9cC7&`4q&9uK}8m? z%g=5%{kHd>@{ErHE;plv11@=isQ}RkF#N}5ba$9w-uUtissRU#ud~CNTx4aLx^LgU z1TCx_`0ZO|$cwXe};Uh)Nc>`NdyQHNWBq7N>z2c%H>YbS;w2rWN>sZILZ>`A{0g)=Mt_jg-g zu)y+y)lSdM+)|LZegx7IP)-PVI|Su2NL^(A#rGGZMKG2vHJPJ;aI@KVh8BU0%gV~4 zBEzwhh|NB~V89duc`R2D44B_tpK}PwK9VZ@FB5ima{)V@k*NBtl3L&oEA0TrU*h3v z#{MxlBJ59|{Kw(b^|$khwe8BP7q|h(3;L#;-)eM#8LfN^tdoo$X-uhesQKp=`1$u6 z#O3>^kK;{mk=3h+!{`y_7@-qRtRa>$){q#J^cFh>v$uaz+yO#6yWTBm*AT>o}Qk-Qi8P> zJDZj9`BhW3;VL}u97O@;=j9CnRDT=&p(vhOTuRz4K^7anNZ&sgM}vzpWfMNLH*~Ty z;r4KMQTE{jm}h#EVGjDreu9vxwXJPl+!bVw%=V@W00u$aIG_eHH#a8*W7`BK@ojHIh5+?G9yszAq+i^q z|MoouiCyoP>E;A|M7TAVo^vt)SgIE$z|n()mm56vs$yrIyrN$(c4P=R~cGQGt?T&S(#_@z&d(_}N1o1R#xdN;Gw#-cQM0Ih;A+ZN5WfzLa*lqS32&cXq9a+KUO zAhfE`4_hUZt7Au~km`2{FrXkH9F8SsdxZZR_9PX9R4$Y~u2lmOMFXg0&=(Xq?qiMn zUUI}Z35GBPw^Qr4c*02_%Q3pKK~L+u{iyqLD;_q2UsewmGF0Eh7vYk?t)rl!8U6)Y zr{`68K;)SZD9sR&Cy-*w82EZq%077D3Dh9~3c~;fWeD*5&QEs%P-q%-#Ug&SO)NT6 zQyT{oPhD$RlAp5=|NQyl*w`bVQ3jF;WbuH3RL*BN2XuBCzvK=u%tYKS&b$;iHmXFB z(3rbjIrI`PP|p_+q(Y2u0%p}_dnoJbyYI%jUahqX+Fm0w%u0jcm5;$1l>lwlR7F(q z)gVs?_W>f0Z$YjMOe|hd@-DG-rYnCE(b@%QsP{ss6QfSeDo*&LN$fC2m__F9WXbXh4a0%RAbr`3&s zk3pnALCa$rA5UDV8E_snGzG4r=bovr)wQY~L8VSKo_xPTRS{~Q2EOo|*P%jLm=ndOSkTuzg@bs zj5~@+MuegKB-rL)&B3N1fGDsf_*KwIAg~3S5?Yt_I0bc2{f=A<5(xSN_B4a zIMGA8HIns?z_j(={d?05Rttf6!9SuQKc~YF>j$S5U_LV+AF#wk-VWh^iW(|wE#KxYBbz4z0}_m@MPykP%8RKfrz zWI=NFzr3?wR=Nhr6N_K(Ga+P!|9w z3lS0_`nD0LC*h+2OCHkU;aj`6Js`0Sx~Nwun$M7RA=E$|HC6E|A*yjG4g`4gf^YHI ziVrl@)a905!ql|-bg!%x?vsZtzKMR{bfr&KECvx%m1h!w)pd1D%lhXc)d2rJefrer zd_mt0tl!rb**yqMYtB_TBJ~kvPtO`+a+BM%H^H_jFq6}^<*fsxGjape2aY$R>9nom z=kAcNOf#*2uyPx6#$tJ81X>%@P9LM8MR8ZpK)%hT( zwqxf`V)Azh=@#FCst=&7NiT|9o5t+;>k5jzmoFQf3OC(C3my)Y95{hT8n`-|W^V5L z;ox^#mNWQOIy{E$@4_r!y^vcAwhd5}CI1C1L)SaLfC z6%0~X5{SjuuJ6T;@Z-yjF}mG#7KJt%-Ne}fME|sDOHvSI0QUU+Y2+FxFx-0}4yV-A zXoyR=&&-`1DsXiVD?)^{opde0gABD5Qy>CmHv zP~%cOr=EnIG_G(G9Sao<@kk+hC^4FN&DD+Xrc9weghwP?c7Cht_fMj2^Uc&6EDL`v zHoT|O@4TmZva9&2t!7EN=8UV%VaZF8^LP@|ahKj+*bOKpARvJFiK)2P(eF8DspQNM0M(Qprcf|w91=<6q5TIQ3^v1@ms7OmVS~=X62Y&&izBF30E)`fpOSljiSKIT`M! zp(0lQ?w_4-yck}8s;?M*jQXpKckH-Fl8oEV>eW_<$2=lWPHn6_@u7?g2}e1c`2IaJ zm|ahgSf7c}2y-z`If_l(r7S?{-PZm4R^QC#dPjFmgV(<`?D0Rj-~F{2*xe2#Hl{%r zwO#!bYEhOb#lslML4Fv#a=jMpAb6sTBqaQlOj?y@qUUtQe9FoVU2Pp5aAmfs3sKf5E zvSD^iYp=QIKPFo@fHX0|EBE{R5zPO@d?#J52K4Uc^7pqgvz2!M`r&&{spSF=q^G9` z&>v7gl7_U+O`Gtx5)=Gs4sU-zHxH=(ocrair{}mhp3~<$e z2+mzR6_&DxX2C#^$nPd2^I56o08C;1g^H2nk=jrw+pb&wwyCLJ@Fp(H1=x@OVT=O7 zew()JynrnZppAd?D3KO)y0gJVOJX-58B0Tg%Ng*T*1!~KH&EGIe0a+F)DFCU1vHoD z)VfZumC5-`PKMTx@bd97NwPb!20B!&ZYCyntUs6kV($dRuo%y&athhavCvU zcv-<5f7Zw-_^|Pd5Vew`?^2rbLce>A9WG-1IJcRHS_iWppcZU;I&u4JYu664wl6R; zGuL2I2(78%!dZa{{BvSy)dF9EHxWY^l(AG$y)CRhw$oT=C0!DIoU1AyuACutV~Xya zUY(Zjnl5mg>~EvLnEvAi=*Q~h5+Z`EPKYztReAq@Y%8SQwtMK7L2kUp*GB)2fq^jZ z-*SQJ+`w8Mhu*fh`fNA8rn@%KH3zHJDai&9O6Z2l7JB7n;=J5P)a+fAe1* z$>l$#=fVHp@VLc*qxzvYk;M%Mzeb9L)bBri`m`e?2w2FlD1lsN0X_Tg**rOr1l+etJzw35fWwHUH7pSo`>eIz(gyf6StFxL)azE?Jdp=2)JtBBvi zi89*Y_MVGgxgyIKUz>(Vj~`6U*<0BdF4&A+1H<*o+VoS5XpcDx0P$M9^`k-#fs4%_ zV8+CA0(Hh0`}1Aqk---AVMK0!J7N!m!wG2b#&l0ck)s@)(%t$I1w_LTbA3naEfjw$ zn6}EtQ`CygO8REr=QbzhS-oWluXkKqw5|O6 zXUoKoA3&V^IPXcherE)*O{{Plxiag!L*v^z;m)t&y?hSOpM2^7ZW<6)M>aB|$$uU*ne}VI&Z- zAZ`EmX5B!0bYC5f+Y_$#taov=4Vc(%VB3&fb1|sBu`jz=AC??1<0e#k;hWS>K*@Yn zuk+`lwE8%!qa_q1{99OT6GWQWzVQ|ng=YEgYAiZM-o8opsj;s&o(D*LZhxFs0vtq~ zZG^&rhr`oN`B9Af(AWJ+1z z3;~K2nw0zD>GAWU0AsLQ;C$gbZ0T=7zXS#n)Wb-9xGsmKWg!PM^BWAu!AwV{IzGi; znX@SAeTEfSTwMGOnBs8{AZ3_p_TkoVMdiheh?fNpaRzWS+~H&kXY~TsUWs!xi)kiF z-6kN%-9I;V9R3{#A0L~H>jj*zIkFxT)lDC`i{mcm*7pxrCpt3>FfP125KNNG&gFNV z9SGeew{pv#v+j~8-Z%mk{q@Cp7d18YWBE4_;e7vGO0#7r>n+$ER$&f3fF|pvZE6~T zVcO=h1}oVP*>>>Rx;dJJ_-1F|COl9E#g^M*h0Vjdx^$*44Pl7lV1XH#4ZXHFx z@Xy2xK~x34g6iqj*;hm&8ZoLG-nO8-N2Ty+ti!Ut7^x&{rQ0YeFelY?bl?DL!T1LM z8*{@v!1N*3xZ?U6Sk3c4B2X_8uL&iF>gCT@F~Qr$qHS3=S*CIC` znPiX~TaT_@;5$7p(@O=Zl5G13b}ZyW%H9Fw=lIGRa;C?Hg(LzuGpY75{gwE@2B_qo@EBn`BW7@1nHz$`pCTe#*nWft}^Z4FOA4hE%n~aY7Xm_Aelc2MgzTc#Qw-HCs^EKnY>YE<< zjksfla^p_*oZ=&@Bd{aBSv`IIbo?M(S$Dv3sBTd*GZNyG=63|AYv#~9o;imUz$##MZmyd9kGT`j0Qtz+L*EQwW%P2K_w@CXC)-)s#Cz-eM_n zV~j74?Rpmy#;mYq#R#0&;jmY>!X|{6F(!t?M*flm?wc^esr+>of&%XAx5ns~fB*aj z@uD(9rxbbY+QC$&4NBUq>u~u1a6B>=Q>n$!jJf#zuo%Pri#TkM?=({@T0Rx{w3QD} zeFECxttoV&81!1$fz48V->&i(_q|9YlhmUm`JT4LtKH5!#(}gD`xW`)>&HxS7gjdN zzke#)glyKqmmWE|NJ~pgg#3<>2C7crZTx)_M(u@G;4r!&Fw6Hj(%JR*F9dGCR z^SqA)p0}sU?^5cNoV1iChqM$oDNELds#m1SXTwwHPi*9wGmoDn+$P>M+&9wek?T?* z`sO}agcHNspwI8aOQ-s06Dkv z=U4SE4BtP$l5h3f8dw`vSojYV#fIGa*3&$0v|(;dFHiqpeSp)EWXk;f`p`+N#v=fE z-hKUgw#zrGYMUeQCxZ6@!xF37Iw)yha9)=3h8FTa=Ds+$6VJWD z^UIJK=03xR;g((ha_J~mr`N9!^Lb6mr9*v%{+uA&7E>Xh?0QZ~`yF1M=|9)ihp1FFWyxm6Pf%6>+&eSGi4Tw57!+h#1wUSH2K zTQfVb&NhsExcUGCL2B*yG3RC6G)rWf5 z@x|TfFgkQ`-|EK~hSCDrYhYg69;?VK6}F}QKC;4VO4{Y_^Qgt1O=~s~X01;HiA)?B zz|742IGiD6e9z7;z*w!zM;uyov7})6{_c8+c3XeY3NFKw4yzYZT=GEYAtk0iUUSNR z*HN=cIqm!@B|0T6GEBm+7ij?v+{UTy!-T-L=c##W$(^2{M{&1>I6!c7rOL%FHFTKlS*CiXVHQj9G>cMdT>EQBozkFk zz(CSi`Y!GFu_n`wGNTjLAs0=K&vBlAY@>6QY^~t&yDMEo*H7sk{aJYnFxe0ovdkuO zXC|3s&D_gIRbJ(9d&^bnkMt5sVn{}I7{+o2S7^O~_DuHo_mXqcm-F|IXnA`dCqNdE zn9w~)Y5Fc>4E6ukfzw2V z!+Q#pyZ%F9QA0-`Oft!|&Z?b^f0DH57B-&qYu$G`2kb zF_b>w-43Mqcy7To{Fvw9YEiEpR>!xeb{+RW&F&y?GJ&`|gFxkD41!#?bal;-f9*zeh$gcaAu#-_+$+10NIgan;e^l+EUfm~ zvk{lFs3u}xV4=pJttXhyDI2Y_*shMgzU1NRc9TD2qKPAv5svM$u`IqVE;nwZ<>VZ) zRVh@6vLyDefMQ2Y(LcxG!GvZruHpyTVunA7#i!NkK(pBL>>!~LfVQ*3wiIXe;5>Qn z_u+BUz8HoHIugY{;>-j?2hkIrnq`%_8-JvUWTPTmbAN#c=O2?G?<Y@!)qK-e z?9(3|-rU{6@(jP9=Z~bVwASRR-7bTAN32`UJbZg~B&9ia`I|#@RbrH>bVg2OQd#QJ zs|+db!`nctb`^^WDTfTTCQ0wbc(18#7jSYvW>p+9^g~U$>e4x(C?%ZAWAN+(IsJCB zesyo)t+2=$E?AjOumog2ty-fdkRNDk861n^DYc&u71gJ_X-jT@Gqe27W&P58Lw|0q zf%RIA8VRFT5h{Ach<&MX9xpZKJjf+uObn)c*Z{T)9=eWMsJrh+$RDYeB^@0dqFHzL z?NwR%r;XBacub)~F9f>)c&A#ZIPWY?FNwD&N(zs@RH{(Agu0+dr=|Bg!ktRt4P^3x zhf=+03H&!BTrL=>3mxu`Tkw&;jo++mju=Tqh!#Q+YFuWYB}vB=eu!z`IBWgIp_%}M zrHYn{(^Ezsb1gGcPwZ1Vrc|WILO~L=!vKKcBx&(L`d?EM6MtkycLeYmCb5ig)dxw1 zWw7kZ&F#n=0a*B?=8|VrB2^D2r=MVBI{xelQWSL?ToDhuV`*oE4U-u2dFb00KrU!O8uL^=$d`HhJl`^tV|mftWVf8-tTzCf^p5cW^uQ*Lxt(t$S zl4h?TatXIW--J0Wy^5!mMKLoU`r-RgQ94LRv2YO&0dtU@eGn^nwnnP9W;7w2W+A}k z*Y0l(gh>+I!30MZh$MQ5qmFSXb49B3cD&NPa2h@#(zQ^>r~|5u2p2^q%fL20Q}WH> z06u_tmhQWXB|tx)f3ko#c!YIqEaTln-d6`ky<^OG|UdF8Ti}5fATNk{;a;L4N=*p_qx|#nex$$ zPu|sL?1|A5gC+vvYq1xHqEewvBp{aU+sVjooHH^vPvv@SMU8ZICS2W<0GYr`SQ>(B zXy-jKCmRlF1I>Mg!>V(0CzEd2Tv21K&-!8~uD3yw()Zf8q$S9CaCyLB4W=F|D;`Xa z5LV>dX>2s775ywrr~ z2reaS){F)&8cdTu%e`iw>?k${_-RPRJY~bH8*&ZBe&~7xQBI~q+Uo4R`<@ny_t4`w zUtp8{UJd%f6;DN|WQ8x`R8BNL+d)wueKWH#Tq7WGXY( z)vU1ZC8K(DOE2c2(LroMqP?=_K~m8TFn@7ErWEy)^-qGTs|hK|Xt_G|B|JM&VL^$e zbDjYjApkGglKT+b(FS}LI}_%nF7{uCJUDN#Y^8(;DkZJPT5P&PBLrqc{SNh}Z4a#F8%)@j)H{D+>9=B|p2$JJc*jV^2U|(kyJAwY zBh9&`^HO6Ke>~407qF?`F}6)(A%OR0TgvhKgMXIGh@GNr$9(0jnZB}-$sA{SiFoE^ z|0qk9!X>pSUo6-DTCF;$ZGZ>~qS6uN`ahU0E z{+z?GZWDES$bnccZr^uZ+_XVo#K;euzDk*@D`a18<%2s-UR4qrT^y9~?WWE^X7%1f zYoz5%ShqtU0rQ+$-Fnsgx(br zIi0|UckQMUgQ6rCcz#ZBM#!dLtxP_=FgFK^5M@P2sW4ZSkO{n=Y5nfdkZVTP&!G>J ztTaql@95}6WWqU~Tif;Y&YqVPxOVeQU@tAnYk$qmi5EN4w>xl|^Q;(n%&Gi6Lxp3R zIV61;`$Du6>K43$&BgLoKg)*U{&LXf60^?!N3j zkK!D#_qLXaR+_|LmiuSf(YhI(ojWB-AAL-+?V{_e+$SlPj$&sd1zP(B@J4yT5r+;7Tz(Ivnqo<&qbS`;eGCMjVx$ElvUB2qhqOQ zwg2RSFYc<-DL2Db69jmv*`^?$CL}s+u3>XIEP>kz(E?;MP&_{RlhAMksxcJ*8IBK5rSHIED$nYGzfUhyTb1r%Z}$8Slks<)q8jFcp} zRu3=G#{DH z3r-ahQLRX(e34(rfwsdB>c|rHtv9j2&BANwIwgK9zThbQnn15QiX@}ELPI=At&H05 z>nl@I*w?o_# z((r@6j81iqo#|w(2MKpF?QD}2Z?jlTUW1nv4n1yJ_`Ts_*pU!(0aUSO&XL*qrW>c$ ze(m^=9y+Rg|IokK7Hw_qNc}TS&wontyFd)2nJNc#D12mXX&NNZ`}|{(XI^t9`zz2< zDKD&bt-=Nd|{7VNUVsmxl(V_74^4iU6m~R=)aZQSzY+@y12qjmSwHx(_K&q z6pXJ-b)vWh4W8}Bz?0-Os2Uw)xW}p_h_5D*zGpPc`1d#6*dBc;%ePVG>?*If`H}r6 z(IOO!!13QTG;D>0MqYX0=5g;UI%zc{`W55i#;own?5T1L{9;{VwWB-#rO2R>^$Vkf z3BQi~5J@4{ka~bcyB;Pwd>xnnTRwVoY%yxq%J43KHy%tV0_%VNd^E~&7 z#gxi3Ys}Xmb}>ahySV>L*#9;s&b^7e!C*H$_^!7EKgI`EV(KDD#D0w)aVnS^yWPR4 zX{(pYa!LN2jGp|Y9cDr$6y@*rK#L^H*qypd`Reu&4Rz~_qWx{L(y1gjr{wFQG<)+_ z@@quuGi;e(E8XSIRqVXm(<4EkTM!u!ChQ{LP%eI-_(d#DK7;2l-O2-tz1C@$1$VZk z?{wkV&MIlP8dGSWPkH|J7k5k53P)|aAjM}_KW-u98n{vuokkbb_b9B#Ogc`X z7JSvGxtD2wC|LWRm)%F?m;yb_qAySd82*T<4iN z5M9hfhWYJe+MZ*}n^=Lr;OH5W^rA#hcuI&UjnymD*=E_N<_v@zGGO>+m&!I7-z@)f z4Xsi!=iK-P<Sck&JCR}0@Y6m1l#Uh~@^W6-syePFf0#r&#Yu1#m(iTM&y z4lnD5Br!M{(6>DWwY2G`Y?}Br%0u3k+p$Of4J}?$6yeae{O?g|Eut-)_fD;ZpN3%5 z%6xy9y|+x-O5yryuYK7TfC^bzJTO_Fi`XyePdIJT&hGY$@Ze$%k4^G-ZolJE#+&LE z8tX-T~%is2WTwY2U+OlvzqGGOmFKLG<;$n$37{7aPO%ui&_$tfP zoeRJH4o*2V;&&iUY$%GFjq8Eq;)}vB1Cqi&7e!j{XSoc;Q@Ir1EVO0S^S26?gl?YD zn*oi!2T%4Z@0Pe&xOvgsp}B!qWP@6|TYQtY)k z=M!1IVECBZqFp*vF+rXo?$5}=D#RiZLTqCJX^wV}2KPUC<0LhtI&!u3!PRdAzfPnk zx*KygjnADqWjE#iI)45>>saX)yNyr0r&)aG`3~d_G9CNpX#XK~ zP>eV|1XSAkm}K=^#J_*V+IqAJxX=HytnmHTLe?z!LrGf^is`V?Q+D&=bP7Gk{byY8l|y&3uzrr?k{7-|T#DZDE|K8~BP(nb56d#`F+ zn4@uiH?2Sifv`i8-thQ6k9YjyllSEsWfWW^dQ~@E1w`63R>dMMqr?bqQtr=+1$b9% z$N2kJKbWY-FJV?Acuj046H5D(3Dd^%vMbOGDf>#bT^6f*@*H$NezR`4E@jscSSoAJ zGh)Pdq&`l~KK5n8%ibLWGXDur-01ewTjRRVjnq5-SzrAYxpiZr2jYYxi~5~{W|fRW zaRS#2@HZ=+?r;;sS^pfB9o`?Mw2coRYaD0Ine~Le>w(ILq_Uuhy!3aKd9n#j&+OtC z7{lwe&z}z@)}oKqe~w~Yn=Eh#n*8?W?CU^|^{-ptuzC5!^yd@hZk>RWd3Ear+#fx! zAxq-V#&6Jxz-;z6Q+vsAfC&gAGAnMpw=0{jcbc`l*lO2#;m?NVn@{>d#i_i>h_)as zK8KCOZRa#T`Xq&d!-$d-as6cx@b#LvBU zskUj>Iyk!5u~KtQuKZ}&OG}Z!m`R5W<697DuT{{00!ydHtR z0-|FU6=8waOk+5Lhu{Gbm9ZUE5!^MHJ#oy-Zi3^u@g+43*bZJyyfVDOVue~v&HHg! z#rQ%N>j*0=tGaYye1UgD^{bdTa*a{z?wO{7*TF$VP6~NBb+&R=2L>=?ecl4;flmcp z2(W;#1`!;eiO{|sA6HHtL|_C+&z+BRm8l-tMTb?%NWXASuAq^MRn8M>u)*UB@6^aZ z@`Pnvc)F#SzpsVYs=p1-r|8P3Ab*K3c}_3u{NX-32|{HI-IxNG=vd~5t=oiOTb&u` zP2A1uHa*PD@oNRS$r@I<8BnzlLRR=Hs2?OsT&saD2ddM{y}ltEkE?yjIxl~ZUE_b% z{or&$Ls|IfeLZs_K|%45l8U2BQmolD+nhaHEZklcc~$gq*j6QU*_8D?8I(I=F6-v% z3ZIuuxVy`AILw+#OeJ=WcVQF35k%VC!MuCc{;&+2iQf2b=_tVPX!3zT#ilFl%1r*U zsfB|1Db>Bt6Kb34kMZgz@8MyXE6TPtYO*#iwB2-z(9${2SQ>REjIYu8&N#9QwmJ-r~G+mbP@_b=vh@nsJ%(xA?C}D{Li;mSh(&<4$Lodl#gh%D1 zEPIZ3(7M5B>!?$xOwAKp=6=D3gEpHUN!CS;+{-l0jN6w`W6OV$fiRhc`Kc?G^Iy34 zNDK)bFJ9u+D!H>!WH?mMazFsT973Uhn9eC46h`d( z&TUO74|rm%#mLcW-IcOU;C`x|t$1~`+7}#8hCc<_Ao>D!3j(Lf)ytdM5uxmRbxZ^$ z1Nk+Vig+u{%yss7*=)N#$>3e4V`yVWre*y_|DrVOPCUevcMg2@*x7kGexNdrkJTB< zXu=Vps4dXk035mLpr&36FCHfHEn%sxHA30;}A)r_Z!{KFICq-FV- zjqvw(RzI<7PZ+S}sO6lc4mf#+o7RQ3)y=LZ zE^zf3ULAmWP+5RbuOPp1#8zwH)A|w33hoZmQ|R&FJdg8e=&RCN$mq~fL3&nX5E2`S zIo(}$QKGJq#5ne@ulC*89TPLrG6x8yVSU894V~kOC~jaL@GtU?k}eD69V*FhR*1_| zQ&WRbP-mF)FU(+oUmt|-RoIQti!;KpMOV~F?Sxb+J3E`s>NAuoogEz_RyVH1ZZKN> zH}_?T7~Qa}m6`-@b5h%Kssth(aFZd&Jj7`Zk2Si(LlT^Rg09~FMpt)4FU5V<^WUVx z_@BYl`Y4zAwF-Z9L=V8h=j+UgKM$P>dJul$a*}1v=cZc5Ll>?sVaEj~{&OND9A=Ka zuqi(}z3sHJ@;|9oaWO3ogCJv^*3~TislU3R(pseDq^FN8UsyNBm{1+k@gZ;pHnuFe z6XF{174m(tcWe4R`QR>*Psql0^owSE`K&ranUR7-vSmkN>&*(+i|b#Y@PiZ-Qkm`I z8r5^pu|xxWh?jQG&MIzV+}UgLPf$D2vH#NymnGlrrl~96zpM%}2(bF!YFCpXF*|44+tMhOLIdJ^S}L7@AFf?AiknHf>!&+;ldkXV?Q6aeo6 zr4#%u*V`iE(*t2A@B}hzjtkL&+C?%D37M34DRzru=toY_xpU_XNY@JU;GU>lXp~%2VpyLS#H&}*S~Pgnx?GH*+k2VG`>uy<3Y}6^k$3z8&QR_c z9Nd-Is=V;S^%dY@QeC6tnw2`1mt z@6Rp1w?Jl%mMX*xcbBI6e*70fH4=7!gZ!54Y5Qg_)kB7{gbhVKwsrTAFX(4TkQKNh zZ?#%C^P5CT_1jCrtChwfO{o}pT7>97uFN@(fjuisy#<5SzVgF!jCK-^Cr=hsO=l}q zuL}7(=`m!74|UkK z*M}p;`@II7uEw!8EXsB!JC6K)^M30e($Bp?{zI3kwrJ>zh>7_ta@jw5FD@_|#vvaD z{7Nc0C3ERR>!3*NHYM%rlU+PXvL3Wp*XscFU57jZnycfp({0WHykjo)V|(eV8>Egp zUW^x{?T&f6y71oh6x}qAo!sT@9pwi312F%sVS727sb$%PAysmOz~{MRLZ1W7AB2tJ z#S01GnZxrjdH0kpaH37I`b{CL2!ZlNCIKa3kHf|$xR-=?7?vZle$Pcni|e3F2)-^Q zs~M?26}mW1Xqn-Y$k0FgZMoLY1N#_cCFJ4th{oa!mfgJNHwHMvGf@w^lv^05M~CZZ zE<04;?&)EG&iw45mV_pIv9;%pj!m}Zc@?drw$HnsU1^RcuXkKzIEwnKrw4Bcr0|CJ z=OQW&P9Aq0ro>|*_}i-Rnv2eZ0EVG#!Zu%G-RjRt7UWL}7Xro<06*f=9Mkd5pQTcF zAU^5gfL|7TN3R|Nijdws*0(pg!Mo~@e2(Hxi zQPsV`vM6q!AiZpz=~5SB*_@ElO!su)Be_O*{jy1;(l)!LWWnaUG@RmO{8;T@Yl|cc zY}X-aXlM}bDMV9S|Kvz~a-1MjaI*Wo`Wp#~yD9&dCt=&I;e$^n-css--EhO*SA2Wt zl%t~~z-Vtpq0J4mpNc&Wbmz+Yr2dN!bTq!0@+I`-;}5^e{kM>@hsOy~Qz@9on^ya< zK?KJ4N`b^dJB|VTI<)V(24WmL)C0RGGbOuWS%co}x|ARE?vKh>kKNN_2ufYwJ^BGF z&GoNu$S*&9nBLI%nw8wL+*F8VY}?U6(-|+yQoG4#o(?ef5(zy(tkWk(Ez4wx|DVv$ zx(r<|-D|hxQB{*{__N`u4@rR1$$L<+MdNu`t6!Q2xz#xmt*;#GmaTq7x-cASVx?ox zbo8;^;c4?Cl&b9Pn#Vd{my3_%#<O&B~@P7Ly!_eYuM-zyjBO{ z#e}&MT&!9NWDlm{-hMxGqUtqD#I*S*UV(yQOU zINm+c%^|Hui-t{S7DA#G2zw)jc@SG~CS5>&)4qQ%76T;GevUpPF8HnWzXJ>*{EAzG#N;2;2_ zrKKIaDK}itq6wg+fA1?Jnwo9jT_H(o6j(x30tMT;kMI1UTTDl+4F*nL);C~o?l3W* z96p-EH@JNDXnjte`H{6F-^%AE#hb^zxeaq@oHw}5FMt_zyGoSKH>weL^_1W9YOm#7 zva$=`2P<-o2)z-h>O&|A-nniYMQ!8=id3!7tKlWjWbKt`HX|xDgmCLV)p8QS6gbPu z59MFiFuwWV2l|tR7A2yx&P-x1|8Z%qnTZV!2lKH_V1}XinwHB92@a^5xi*>EuUKeY zY#LVfhARIvgFx~M(R;SY5t==g$oVi2_w)11{7I@Ex;_ky`kyNgws>fu571HLkXSBI zBoM)&7Q7MmT~#CCjx*eu-}X>uZguNzI@NxsYQ&mB%8A~rVS(h-;zoXfp|XYI%Gjc_ zEfodrT>!F>`rShaB1`9GR-~EHEsbnX_5C9aRpEU)0m%^@8O7tAq+8W8DW1(Y_$#08 zVy{R~M$FG>wPPW(HdpuFzU7n0R8++&=SXbWtZ-)|ti8*hEYot5Be8eh= zQ=T4jbl`BHr!N4DCZ-#ZI{ILC>>H>UHMrgzjt3m*-CTxMEhi14$Yu!KWd9==;EH7T*O) z-#df_^N?oJ8`$&iKomIibEuY3iT-Rh|3eFXCyap^G;$*=iG2MJvx!&)LK!O+b{e{` zq3cAh;49ae8vNN)hv`FBVQ<6?K$y%SGDCKu&sIOS;`|eOx7XG-!@Qo!CLE%g9u#tQ zOf1JLx%T(N?6=k>3UqaJ)slqe8mJPr1qb6P2gSITV&V;84JyauAG}~8f?5;~w47ht}@u~6K1g9hL zkBEj@3G`jrGL_nPr7{?P!!idBu2hwMUKfjVs}O6* z32U{A&@_zVQ%8tjl?{D3{B3ew;yV`8li^(YiVmn+ZkV`^Y&Q^{!*12T*}_<*qrP0$ zf9&_Tx;k|&{Aja0uF>o-3ux`Id??7W2XbzORfKSZ$gO{nTRSMhmlmd0qMk68WjXRU&D(jK1d;>)t^1?S#1>8cMWPoPI_m z*zi(Ae0K-knkZe6NM-Aj!v#%DIYf>S)(i*fpn3~VeT_*ePKIxEMYNxQ#NR(F6|WY>dE{kZK|k36FLU{ z(aSs+Mw#0b{jcP)9doXPj&Ijr=x)+VOX)G8zvs}$6iYk-kf7va)T zsGb>WyzNB-Lr{74sqW{`%jB7cJlLU~e2er00vvLk_JOXZVs6^}FEc?q6hNJ@Zdzr**r`l6emujPInl=`4&67r)F?*(?epfYP^L8-l31f}U`pm~ zUfHg?YwP_(*B=4S# zS@+?P3af35K14i-|Kv|`!h!W8IZyb{p7NnABd+&8WJ zWILPgzqy$Qc<6!VRU~D?4?E*~%(o-M$i2cRjjA#Wyvzwbc3@7q+ zse3&7t~`SEjs{Rp37J*i1Kh$>@HpwE(TPXSVn*SM#KrGaZwQ77e`2; zL@Wo~gyI@;so;qI-5#)XwY3KzOp3^4oUwp4hIL<@VOx{ZW#xhS^yz=gJo&)ZViHEkcmSL7vY`k zN&Ue!(olZ3FpS0CObpx@6&`vVkF0Kk19GKPQ!MMO^2%Ro(xZVKBEL9F6w08#?z zKgi$T3I~`W&FP%I2r1GiEYoa0^=5vPNA9<@zCHg-7)<9qiP}3IZX%*7fzbn^t~wYf zh-jn=8E8JCZVC$v(}YQHjhzhYFnnv`z7o-Iyun0_5uK7>>p>m!eJ4CGz8Q782;tsk zdwZO}HU1=8AM*=f3?c)Cn95Gx~Ob_kfh1X~}j zoSMbaIy%5pHFC?j(mg z%<2^N+d$5&Agk#*?iQ{mk&Z|BfQKZI7zDpd{ZpSM0c2;r9dSh4Bb-`<85Og}h^6s| zeWwtwYiz2dtkuBTAA&U z$22~Mw?v2&I$JPM@XruF#xQ$ZTLpv-m~KT1FJ0Y+VoWM&t|%>yoRA|9c#N|lVX$`S zS?y2<{TdPNH#ODO%(ZGJ$Q&Tp3|1Sy=flJdgH;^Dk6QaOU4bZ*t_y;PF0ICCT)gDJ zcxWWN+&iZv;vx28LLdLp?B)*0Do_~l7v`@wgsT)@Q%{-H^v5`^jgf7~MHg{DkN{u} z($TY?R{uHnPm6yuxKdI(UxK>f@4|R{{e9u8dxR;35S0w#@N&n?*RExa#5fKBB7~s= zhu90n9k6xGzl0Y6dD1q)89o-WW2-#|FB0hV$2=hu_h@9aZdrOLgs}$kUO*BO=$PI}EfahSCO;xb1WN~UoepoZ@Uo^#`#4=! zyGx!3WMn<&g0#IZjneKMEn+_5`yqvb%B|wZi_Dy1cph4kjl*FQNXL3NLwatneek57 z)mDmKN{#Ilss52_$?H`$|A~^6wdwG<6NF@1bUcSmt(`3<**hG6eg>|zI{4fnjCqXP zb?Q{bnMo6ucfX0(3+CL4o>@oOD&nr zXx;ddh!Dw^lGfm+<)ewiRR?Y|oM4mk7G0Cb?!isX`9k)wuP;$_R`IroxHx5hkj$=} zZjF&vr8&x5cNOT)*#9zMEZ_Ravdjb0Nh76Dl&6w4(ZJ|4+<3YPmIPG?DLaZ8ogt3> zV2=o!XQD%uio?SPR!rtzb5!WOyL?wp5ebo@f#X#ECo@LY1r0`Hw#tLaAP`?|%SsAa zT@TFd9S6?)RLcw34+p1nB7X+G10^zw8$rpV^hjVPYzG^vvBZ01#i;<|?k1u?CzCez!dCL}C zPjQWsmJ!P|TV{d>)Am2ZEmo+JS&|-HKZ5oAE_WSVC|dl(b}}DdBH{@g&T%=o30_b2 zPkGR~LQsjR<34<_h=iao`m}ZNC~e&H&0XOpk$nj*)Z6BN!$P)0Pj7B*`tPkmA@5GE ztrYk%nCrFZ5lcQrUlM9^L7 zlN}ezH6f12C`SuV8Wgsfqn+6%L~AhfS+yd-OF|@eT<)~}JDyn8wIa7Y=?$mOi_Kqu z^_ti|h!y~gf@GF#h{u7y(aqYpaKI`#RdfdGmqIQ^%oXJ4w>WtnsjmhOCpH^spP{Quz7Q)B@$U+2d-CLjH z1UGG?^hWsw{mTVNy2lKMTqX3NEs*9T5_ciJ^AgKhTN#iNKi$6|plcJ6w}Xw_z-W?D zg`4($aT|h4tE=T_+^u4bgVr;RNq2Qfc_uMWEzO*cB`-?3Xa7;`C=gzHk!9R4bxo{4 z>5_Ui=T@8}NCW#l#?rvf&fP$mYSRYkE(=<8Nx;BB=*m$}Ae0+HwhSR>UErJS9xLWr z%CyChnfntC@iZ6>U}Di~%cFHqIn6EhFv*uhr+tgsa11o9<404qIl`U;?wF}Qzi8En z;?arNqs*rZmrjzTUiE9zm#r@L%CP-^$bkx+MFhl;i7k7)yScf!n_AbpWl$LN9qmS#eiMfDhpQgwr?l?&b}tP#g>Vr zFTUSiYtM_W!`Y!$=OOe){8H{4u2}MKzwP#x@&*G;LT=mvlV1L8aX3%&)MghMY_Zw^i`T_S$(!-vV+Y0zR~%eEy1 zn1^*g-IEFzjVybA&^2gZOoZaB42jDOI|w{pTR;)Rmk5gg!LKfbE+plNIFG5{OFu); zO%iKWdUWGeKgf}T$IWtZ1V&SnAe$b`bU9#JA|oBqin7G?gZc3cH#aw}SOW=RTGv{q zmx#629j8;{QlfAGq|tiXE%D=^)(eXJ2yTP%V9q^>A2FYp#+BN7j+I|xy$^d75^m3U ze~rj{UBGG8v>;0ejiJ!5oY5XsIe6N@N#m5Z_8yqwbn+(~<}CBvL;p%*1VYR?AK~yM zhQ?yq7jO1ak{21ULIR4{hw1k^{GDfYbwjZ@55EiAOO((1b4BMDMO*C4YA0PTp>KYA zIVVT7P5ZJ#S?b*}rS&5UxD87MIz~oUCKvmY^tI_=+zrCr0yv4nji5FwJ74Y)^db^z z6$3pY1hcX#ZrE46%x}Wd$SmXf{S9I70@esLp4(yhKowImo+M=mhf6uR5?MUmJy$2? zo`?xZgjn>zwhB)7Ha3l+jB@KiOuwh9fB%mYu{mink>O0(b2a6=mE`T%^UVmBb<0 z`RXzsSqc>JAarzekSXluRH4OPnuzRIUp*40fb|K9w~rEOu*MsX+oJTpoGyHY%YTOm zT_l9XGcy6)DlQ8rsxQ>-L>wySWD68JLI)1G4_1G+zv3Ed(#zLcrWMcpD#V`$Sf`CY z@BMpuh=|$6?PJj)Eg#-0t1*r@5muVlgh1u6g@6Ftg?z8$?7JZ0(Q4-GcenD<+D(aD zl~z;~$5%hZ_HEMF$2YxfmxOsC2rmJmWUp+qvbv${^HF>U z6m5k0GWh=eBDbeMbcLBiPejr3%?Bg#s&?oNEQE6eXNfr!Yi9fro1N&0$V=OEAFYtR0)@wlDFyU;iwVa zu!h}v+ZG~^I??SUGc{qN_b163#}ss43CT=Yk1*y;*0fPg8e7hq-F-~3xq;ON*{Z7) zC+NNOscA>f*?EO;U4{yI{J`E!vrrlXXU1LztFbzPUc(oCGqG})w~`X22yA0%>L};KxC}Jb8|d>cGJupN7*$M-FH0K>UXB+2x|G z9JB2YLe)cnFW_|^*X{RcLtHi{6WkzWLqq8#&C{CHjykTv=^@i)B&u5<+|`?c1pOOU zR2j5-!^Rlv=`E5hg_<(os0PkiOGKGj96RHC6LiU3lfYBh%1(5Pqp7dYar_l!_nb(3 z?r7kxDfkdK@Jag#CvyBM7$Z>0mGEN2Cy%s5C%NKGh)7s%P5h(9nz9BBLpL%9v(@hF z{U#I-pb~Txcso3Bz)X5$QBmXBD1)p!2A?oxT+yprsNl+c_AFxHyXwFJE1N|_+a!S_ zb+)Y2X9mUFloHnD8mpzZhmA}mMHSeESe;oL!}UNNdtCe;ow2YmY~YFt^6$HKrjjzd zrT%DqaM!5esqcS^Y&58Rh?y7X!0v)$2O%W{I|l6=)9*@YcCLD6w$F+6c`mzGRkZ0I zT3eVmC`sGKz9s8ez9_3{!;=>!ooH~?Dr!(H3n8D_Bw#usvW>7Xo$0;ICoFs#W!Snc zNhQWFQ=V~LSDViHVRfhnXY2s50Yba8k8fh14E-ZCBh*7w`*SZECG_h?=DvCDxNc%y zF3Q&VZOGhCmsr?@{eq_Jk87D{MDo@R!gd!`okFbB}wUz zA_or$<%!&G*{N~FTfSG_+Cx*x!?2Djk6va56|XN%gv$V`2vv$XDAY zs;{MA9E&1uqEJGERes5ujB#bOLbmiW&W#{h0NOpOOx`S&@FnEr8(zCT(LY=q6WuPg z+@#Q-GuF7JPmv+bG1k}=ujy>oJu2T{Bd+UN(B;6OT2Rwq^Q(2;u)?%j-RfG%k?2Pk z^~}b8wK^Ic{&8fchH3D~-K(1B6=EWPQ?3<7YeqCZs5bd*Ei`B&u%NE_ywx#&!CsbH zw3mjWYiEdu$X|q0$!1>N%dMe5*&-qFO*Q*iYmY<0XZwnAXKmg{rISK42aMSZnz-VM zH6`zkin$tPefaXUgzpPvctpZ7e73>IJY%_`6I-Nxajh>_l#RO;AOaDO*h7BHXHoj& zh$&@>l~s0mJEQLl*Ld4D(Ky`~%XS}jb=5@+sXMP)=?tik8eC96CDfjMQ!Y;Y;kf9$ znNaao57Og?mP0@6tQ?*Tww+_29Ght@JmAcf(4A?HXS=P}~?n z8h4H%4;`{h+|`w3_^b1fK{gD_5_c6or^jTy`d|>*{K?K*?DM+<##H+)|2>rYmbA(X zz4UEI+4KIYm`qX*n$${W%Xj-bEG4i0GI=06*&xA}*nQlPB2&~{pQg(0zWlgJNN82-#L})a6KfB1?Pg18DR}X$=QltLs^{PXjtZ_ih;V(}ZdgcwK zw5*dze_LsqO@8|p2D4dwBK_<-tk|`*PoVDh>Jb4e#fnO@2G4gqAJ?;P*5~?kFKH|c z?hTbR_0^2ia`Ji(9u4+l*vgK58W`BA5_O@ZS8RFjf9bwB%Yi5S@Fb12(>hP;^YQc! zm0xCZG#}JQlhn$`kJOLlQ8w5lRWRj$4qSCxa2~5(U}6bfIA5_U*6%K(ou&SN+`Uy) zTmKh57@PuyAPK=;gBEKk?(V@|iaQi{XesWcrNuSD-Q6oV#hp^1Kyimj`~CfA&HKE} zn)^Uja&yDIA3bNEbM_W4A0+b>{^rMIE!-()V9ie9dBYdaay|G#H7t{}B9#fvF!{@! z^?4wdMKLbRa-g+JtU%)#q_SJ!TYXgz-JI30 zYc2xE2AnaqMRXKDN9NlhD(@Sea(43nR4YZytnOy3v(#^$e36 zyhSQLiG?8DBxLd=($qLcRzt5L34F;&)hUP8k?zQIk2CDT=Cm`}!qr!LNJ9u|r`Nk1 zZSyTkeaqxVU|2Uk8|jxBU0l+87N4^_Y5l+(oUK;I56b+)XVk@84E<3cKYF$cy=AqNQKfSv_R8HAa-8Zb3G-YP8?+#YRQMC;hi4O}M8%Q(TgcFv zrLS+M={S-RBMfOI5&)1I9^q*eE&QrJzk-k+bNl(M(R0rmJYzK$-)w0{B3Y#GfMh*J zYU6ztcsf6sm1xPce7n`6vaVj(UID%%{yhl8ULt_Wk$ z)HN%FIl_odTD8!)F1~?SwGL*;Of;)AHov>UpfU?Fmu+l$RlC7EXd}R~Eu6Dl#=~;> zSE%W)w&EaXBnL9%6WcW8-urJ|4H8pC+UIAwMo8=TugoA)=IV7D)yLx1y9H#L7BU`T zX=lf}>!eR04-*hM{aW`IuSHgT2fLCA{r$=2e|oI{lb_rKOQ-4ot&IO0+C_?ZAY-&h z(y@pgKV+H+l9Jx-xz_7-s~QM00-Uf2gCR+B*aTgcqUC9pkrlrAN6&5l-J{CW|1{|S zy$Qqu(wHH=xvRff21q_-B)Ep8Q=&ymaM^(T5|;3Py>3HNBOz1y{|YaB%M!Ahfwx&S zmH6C!|M#p1_}b=3f8oDJZ%Xod*hXTI|5JZJBGi9@g#n8yq|1MMk1UM)E1ZC=%tj*c zxQRolL|odIf3MyAPul0d--t6(5M;Uf7fqGWOmj!-!vL~(kas|fEj3vEPm!`Y_5UI6 zY4wqZ5QC(y{jVzM;1Sw}&u75@x&f)>q*N^WsxD>k^4`$KcQ(nq0yOR60??@EF@zp=PR zzt{lR(6VAI9v~`;6ia%fo3^65!jVwNa(&jyVMojEbPTREJiezR;I@)~ z=l)ot|NpGiPvTpx{cn9G^Uc{ifet_9c+;?;*gCsW0=tV|pB7{_jKNA5Cei zP}%=;;%Phz`2U>v|N8|nEZB0{(87nI*u)R#qVWkkvC;nyyCI8ESixs1jc9Bc>k>+6 zu*>e|LAlkD%WGL4#s3e*)W z|Bn2d$TojK{GcH&fTx&$Q5z+|+7q-N+I{+KUSsW^W(0q; z6v_EtXPgt;yij=J=uD(zKywHoWCd79>hI>-%SRH=c8MHA25+%PVsqf_N_tu8SOzcm z{M5X*cVuD9`>>1PcV_C6Xi)}tAc?j%xoZxhzg?;c!>ew$1Y)p~Fndc{&GahR%Vx|> zIuC}301El5wH)){JYi%__I7ZwaxL#4ZcKD|Rr`DC?OdQ9*lxfu9E0}dndtxS?98c$ zEfPmx6MGs<5H-wPT3vkh{LI2+72uDFH9`54r=a60-{K-q6M`y_P%jT7497ghu($@3 zh^wX+(9hnF&5y*G4u;-5pg0{;NQ`y!(6gd$!fR6YSVL3@NjqUgI(XdI2IeHgv9h+A zeQAmnzwGdNyF8Ji<4=*fu-%2<3CdoE97b`k{4|p$l#~QHHeOf+xpE7hU0Be@jDK(( zjJr_GoSzS$KR7tFsbSrfVR@PV)rBz%^(0X&v@`%WoHnwTxvE*gUD#eL4C_1sT)1}` zc$I}Cp2uU!Ufc_As`zAR;Gc!76Fa0^`q6sP*LZg*Ce8w+$~$~FeD#CR#N71pejvv)3s7w3R~~FR&hh3~H(--?MwF6p6ooG!UlFJ@c-(xQn@M08XSxb!}MH znoT7S;?I5Ep(kS8>5h6#zvU25m_hZRqDqRom0DQ1Uytk&{nuM)mKb1?>QM5qZc8`T z-p^y@Lo?9q9&sTjNp&$PHO>=yY}xA7Y5Y(1c47$z{8&?qi|i43yp*Q0_X8Oi(*V>n z{B8Fy+6cGv7y)13DZ#U?x688Wn4$0PtV{+n`=3P5otO+wWZ)W2Zk}xl0(5YyfssT~ zgcfQ>d}<%X30Mx=0H-fsreLuF7_F8+gUq~Sg}ppho%X1@E7Yrcz11rQq_dhV#=*d= zAEk9{-I7zqYyBPIjcC8Ri3Q55?%@N1PE@zISW%trYPU@mr;DrM>s>3jAEq!G2&WA% zv)OSY2cH!uQwz=B4yN7!h3{L&)#(PL`?$;^}F#Gc&oXzuGoqd6;)<)m{^`XFw!;PJ{sTU zL_-midCJGx*6L(4Vg@A)h~5&9uSKJlWhqwgA3g-PSGB8{W^M2?o?c39*KF2+T+cf$ zrkQsJ;){NJW_{`_C#D^^)k0CLwHfjI!tShurOA}YA?oXJo8qax?Ot~Ea&wOCh=FZH zE!ClJV=5YPdUwD3Lq9e^)kQCgcYCT5q=EJ_?d{fPhzvkxlhj7&kxKsWIx-#l`WAr8 z14Oxb!^su&NAd0^c^eX$%ZUvnr_C;(!pl`80n@$FIlfGHOy=v(N_Y;Kdbv<7N-7c@ zPA!9e--mH`z0Tq`QIN>sinkCa2?6Nh<$}opmf9#8+$bKT-s#$%F%&l1&BfBl`+Z)0iuLWczvCN=i>GbwE;7z%z5|kGDXo?87v@n(-k6B7 zU{<>`_!riNrTh>_s2g7YZn%=v`p84~#C`+VqI90vKXpwKnz_Yip!K9=fGvb+h-=Yy z0I%WeP73HS&Ba!QT~d*XhHPr=Sqaun4b#q#_|A>ybdY5_B9-};VEVAH`XuFZ?O@v9jjojU-~vT=iEsNb9r5R_e2&Blc|BIBp`^9R^E_R;uwxK@xM0Ds zwA(lMOy=rDj2=yJZksOo6f9BI|5hS2q+&7d3ylfNQ@jPVA9}2)<`xz@07t@WT)v6s z@UQArT8t6Zv&A=HP|23gGl{}z8T0Zm-cU?1=WH*3RI|;2Fy+aX*C!vlw*i43>n&E$ z2yzoj!za^N0pVY00&s-NO%pR{Wqy4rD+hoFO=SA$!({;x2|RB*?I7<(9{BMy`JT#u z&3PIXRhnqKp;SmEmabt=kBbKdf`BrFDYG(-5MnZhhYd63Q7a{B;?%$_!htlJ=Btv7 z*DkTQ%w+HLKxj#f{JamJN|fA4fZD=Qg31H))0W~TI_;Y|nfdKEUqo#&$_qX6b#e;^ z#teCf3Hw0-$EOJ4?6X}(J-Y$%_pq9DbWMj8FV=^xHUQoLwZXCbCR-s{Px9kat$Uw0 z0wbr2;mra*8Djhtrn@D`y7~!BI9M zZY;yMAL4KbWk7>qn)J^r|17bI%2#r$Tl2~@QNXNQO!vN%^f{4F@^*lzF!JNwYz(Oo z$?CM63IfBJtnVe_utH~2P=Hd4=~T*vT9PLuu>Vfpi$$3yY3TwjQ(P4%mB2NI=^p1J-Bkvbi;+1EvG)Au^Di-tru+%uHs2u5c zh24wb95|+~vB~FKA`+6$-XVacF`v74NbBInp;$7&<>QgUX3Uu5#n|z+F6VLyyh@vx z&5z3x-2pwh*-YU0eL7>}WmQC>DFNdcgD9rs@(VVx$}6Tp3~NFBlN3*Q(NXgBsX|64 z>-Xj$m;XT_!Aj=NQU>8j&>-pWrjx%g|G>2&u9BFBO&n+#fD5(6;UV8fK z62JN`#|Z1R*;SiH3bF-|z}1LfV~60CM{$#ALJC+*Q60t|q{VT1 zbyJQMHb8oUM{oy=jviYzI-C=b@Am%e!BSJjW5!A+4~&g!UY#U=IZnI8q;Vg=Bp}eh zTnp~Q@ac)B4Q+_|M5vFZ-vC5cC?f$sgN1f79os2X)Np4>RnPyxflOGJk_hiRP}YMHd4^Z2(KsLaGUM31lIx zBzG%Jbyd@^6AkYJv^Ns8Iv-(qp}A}9+DkPUJcF=Jdj}hlLetafoDrUTk_N9&yKvhi z9Wpt0H&k(qsnw#9w2mtUOfJ6H@!#lz+37YM22hR9HIEtRo3C34`pg5*!2p4nZar2> zV~t{6r6Gc}KsO~ zj7@H){bry}RjrtOFG3$|rurQ3QPM|M>TvR#XeU6o$C=)+E+;*#YceNiP|Ks|*Np{+n}u$!1S^V6 zzutmQIhi24;z!%Es^pqbLP*$A!B%)Z6$rvaI@gKP9>CRE0!APF7UQvQFnXxxm!86( z$<-LJ@{B)nB@iX=2_Y6w7;j~IT7#P{|B9(aAdp=Hf{uN+t5U2qLl&7Jyk%`l2B2YN z1GKqQ{qCk^97Z8;8STK1)~oka*JGDLXKZ6vRDw3b9w|+fdCU?)Skwb5bGGsW_QuQ! z5rGR}*G;$sDnwwQtbXJkJdUjkoO`ZM%=55Ob91z$f9DsIrmRI21xkiZO)7zD2+-6jFgFTHbnA zxRfN7!kFy8?Cb}=Dy9Oobr=z?#j?$+jl>SO4Bi*j|aR71XC;0Y@I2*j}r$3YuU z3US)UdGj3-Em+)i+g$()k>)C(=bpMm1UFOPalf?O5H(DFILiY&7WIw zPt*!kf7OwV1SKxl+yqEmYW`Xo|65+wm0~eL!j4}&ny$F0Cg&Oy6YHoQSHJD)0y*j* z(Ikz|7*hvz232{Gcb=wh7xUGFRItAx<8kR-2+oh8CpJMDfmPKKagU~f`mj3>(SeN> zHy4;>(?eKqck9pVBt~nk{V|M~oX^cf&k((3_4!Ko{QaYV@`ce)+d1=R!F8RJ+GjH8 zgF24_CJ)Ug$FG*fw6@>hl=X5oSBVf9^kf?axow5A-#%y1ODh^Px_-UuY4C7Wek#7e z@9^KG4s7!d1-(U|Vs2QACesFjPJYE2w>dJTd=-eP`nG|q&me(McY}kWk~JNN_DU6F zQoDYSEUb5I!t-_|WR`?dxPn+(lNy82ZmSKWEUQ;Y%O<+GQ>(5@jxyuM@r3rzeRzrk zouqnR=!b8ZUw*2}+d9@cn$loF+ss_jH;PLJmOs&k-`lDWEh(x(SY?uEXKa;>xj6tD z4P3(>seHWX1G(Qek-4Gfx_WInqfZ{+P^{vVH|Im~Kj5KjUJ7p=`C(5K2cxToWvT8o z)mgph)`gB#+D#1@Fi31)snp;QxtwBBEhBD#SbN?w(DVPgyTN5(ccKZgm zTjN;y4TE_zfQ+jf&uA`B#Hlbz0ciJ27e}Qx_W9YLRQ{qyhwOUB{?X^++ZO6Kx8=FT zcef|wWf|;4Uc<1J0{Go+`ObFu(cWdzV>uWdhIayH#(hsxus{iRW-TR{1{TpUE6+}*%EAkljOo2-*2+tfu^s~AlX(-J&+UE3? zrLJw(>MDdQYGdD`nQ4tw?VuZqZy0{=BNjf?+BT8xC>4udUF~u+qhA7jB`SmBxTcF% z7kZp7#oj%8$0OWJ`F%M+ol+nq1GfdxkC-|5ZXEaC;rmnb^({4yEIxuu*Ixr}))UsK z5)35M9fn~fEEqA$NjwmPuTBoVo|pQe2^q{tjZIp&oyLqZt%CUKE34yx)Q=3$y$)8B zLhkG{XTfIiETQhuS@}a2rAWw?1z*7 zNQL5)6mC6drt<0sMKBr95F?~|jcM5r1S_4Q?JLt~*h?9S6qgI}vuTf}9zxsLtoXkW?6 znsYsJNngtVrP$mXKU_Ji&nTc6jrdNf|8uIMnIo5lyHVHJ?Pc74Eq!?I`Y3vgc$?k- zb6rH(Lls-)cwk*7W&P{e@>9`TZ}2Dx-FRE z{z*Z*=q>#D2`L%mGk(Z!dhlJnSjDuD^tUJt1=j8H(2!of87Q@nlD=UW4V#cQw3l3{ zrozV;km^bIUDTYL;{z|5MmIWhSvvWYfv%oKdV+n-b?MEU9k2tQ*QOz!@q>@dek-Au zij+0T5}o;(3!yN>F_Mg4dki^Nc*p_4AG2>_WD7PdX&EE{GRtTH4i8Ue2IEE=o)E#0 zJKr&d7XZ9K$GjAWX4q+6Wht3Qhz#l$p0zKePrWkSq>DPrR+rA-gDO88`;`a9G`d9E z+o-5+iFNj|IPrGxE?}UBF`h_y1J7WyIX3f?4ETA}=-#}J^Ns23bY~_nCKw|`g{CzT zxYlo_y86Dsc`1=ITM5n-^?BZki(tE!9Wl9BL{XxLTN=lv+sxb)8^Eo58ziVT4CBP+ z*ny+Rg7lBUM5`lap8whHwoL@p8y~_sRZ+grg%HTVulfq0f4o)!s0?-~fER=^aL6R~ z^{}NOSHD33XFn8w0V{RUP_7~Ya(wj%<%6RzTB$5{giY*@#Y7rgv)_P&2of5=#KH}& z`Yt&GK-{=*tOy>T3Jji0!PXwSt+-rSJWRAE9MfP0&2f>7-*yAB7sio%my2kr9$^SEt@+M; zpczx94)D5Lt6#5`HCK+7JnwSD%<9+S+t8n{NP7W_R{mjCYhkyY{N+H1yEaCAYcOn9 z?^&-pC@oKZs<4bcg0~+Uu;DMFSwovsTt!x3E^7nSUg3f=(T&bh z4|xH+fkOw7&hs2d=aG9ZEl;W;9c#B8AQqcvV0`>D*1pu|=^gvRKV_EuHhD1rwq*7S zxGw1WG%Z+vmKPo`fF$KH_%bK&2h$nwg-5pnb8|K3bE6Pa3! z^SYgbQoQ@S+}Y_7QB~X?)@2v>QuRo5h8n%E>fszKAB3-%I#D$KBKux2f?eDwXyI?f zmyrDumxaS1@_PnzX%yls`F_{JEu75f+*Pw3^1jg0fz_-#BQH2|_I#B56hprzl3}E^ z+BohKBk_;o7gSn~R z(u3>mi-QW^mHZdaJtmy$w*n(Bn}zY`!{k&q6{287>ZdC3+ckD0bV&OJj#tP-RyCqg za={2FI}0-#h*Dt{)Zoqm7axml#&FWa45;oEJfLJ&0-Z~!;~V)*BmWFr(W)MFZVcR1 zTHs7Phs5U8t-61+ZNKxob>N|4j==z4@oiS$eR=4RlWi7iSjQA>xL8K+iT|o-PFO)S^)ea9BX}5~O4GqTMhWVm4gh0u$&G zhfxSQk{PWcZZK0??NS->SfBEytZNiw!^5h0h@AFly5XE1`ZLS%o$DgS>JEM734$D- zCZiNpTXdol5eh`}4>5&|@-T@33^^B&a+AZ>xcB?~+b^rq!2`OPf=#_eF}V2~qnwUw zaD)cATy!)+e^MHh3|A%$;D?KM#-J~P=3Eub#{Bu#Fmtxj^7(J*%!Vx5=#GQ4ImknB-LDeXrE|RciYJ3(<|J0YYj|BZM7*f-CKrw&%&=^# zE?zQGJTPO~KU9QTJ^{~k*bNgf9D|=Y$7*YF=7v$I@11Hmh6*gyjOLJ{ar`xw^4~fB zb3u7R`(cW@dHHsZqZVBT+BK`?WgiGM;{lM6b>}>XL9b*s%b3(M9UyjY=ZKF;eA(Oz z`JW=DbMc{*&EifUXItY$PQB7iE)Lo7!u)~9bwvN`Bj!Vcr6^BIQxpwzLnCN$L&L_J zA79cEc32cH*QULNB4G2CgOo*{;X*{&pd@3FdFR?_hp{A}1_RGD0Hg6?=hB?Uh+x-2 zvDmf8aMhDF+t6V%rFfL>mDnEvvAEhnz%}->B>AcJ0INwOv-pgiE^G&>()d0DT}Djkv&FkridC)pb-@EK zgTNBkDs%QB+1qDaJ2emYDW=1&Q427E0?IbR?ie=44W-AQ%J|FE%)$g7{ zT8|}5?U7C{jzWJ<#Faqo>ZRc|HEYf&W*VlvVi${b6*}bcQ*hv&RO`w`%-J^=9NTS6 zLW3#NFK7jmR?&}C1p-ko88BY&X6x?m6({$0rrh2geLop9Y@jwXxK$PZQEpXClT#jy z4L0}(O{((aRRfxFm#*5*25k1m&IXM9-ze5j%n?ux>5%z=9#!!99^RW2srQiNrYY;agxh<=#8^RK`u==j;EO%wqa@*IVBABoW1 z-%oiek2a@oVeBrKD8|p2QOct}bkVe~oC`Y5pTJ}i%cB~vnw3q8G8-x>E3cL|&zXaE z4MrT_Z0&d@Y2hi8SjJ49*ZO~gMe^Hd-^PI4v@|DRVsETzY*LO(1MuNCO*Zii-bR!f zMYn1LA%h711b&lhk@i!0Mbnav7i>Yl6*>ks?4x&=Gb&4ey=KWrMskFi85DwVnbV8^ z^I6>><};Qh#^b-?23FIJbGpDQRZ{4p zefp6jdD4-^b`}DfJJ_wN*8n7DY*U3RPSYc z=j8Wndb(0Dehqn>@pOUglTr8?WHOPa%pVb$Wo^v!VTbj;+1GH(ZVDR_Wxa=X&WNUS z4zeTO@*(~=@=(k}Wv$jrTA4_%$~Kq?C0E@d1&o?37Q1Org5idfNu7a*YJ&pS!V>Op zDS|-tmUV6vXd8)R`(buUjCnhFS;uKS9p+R5Ci!b8CvRZ#egg1vn zvr|}6e!J29j$uhM>q05!i7U0G`2F)~wYpeT&n>mw`oxR`H4obalOGO z8)7bpin;pRJ6Yq(gQszQd^KQZnx5aHxSTtm(CXFQMXOX8F62G68BK;Rx+`wV0KJqS za{_R}r5`an)(L63lO%HGlFyz&t0+%F_9NL}RtnO?V%%rMt3iqHXx=we`7S+RUS3Nc z2bnH~*gwLPi?7pZsupwx%R4o9ok!6s94rhPoaHL0p$Mxx<9e{06M{h1O=%9o>#@;(cp6O zB{da)rJpgfn=X;c!<-N1M+XLJEf8_1q~^J@@}^O<7~kjQ^1ji!<*S zajp2Q@fg2CiIFa)`vxEv7xoj2H*oa;(t3}Hx8fo6I|7ksuW^eKPmiBgKKVgd+%%A4 zAIbVO3a-YW)4WZUx)^J#2Lgf^?^~bsmN|sM{6#+GsqJ0s9OVd{xYvgNbl53cfTWhy zmfb2N+UmFN_tcd}t`WL38G$ray$b*4Brzr$2i(X$Dh)9Rfh7D%9Lni+rSV0D`eTh} z@KqD#7O`PTn6PRvv`eocGEOfau``Ah3}awoR{oe6?F_!V zm~g%t;&VLCXIlJ|QWTJ1S2Tbj`x2p;KyZjhG7I@JwdeXiZ-NL0V(Y0nhzDg9En$NFptg9t%n-uh zVp#MI-}+fr_2LGS9CE6p7+>i`9JC3ajT3cTYEY9ezqmS4s&JUX*mSOwDmpvsKCACd z0Zc=|--&&7TPY#`9&%k7kN9LZM2oc^_5eIIXk{*B_&^peTO4#V2QP|hGXwJ%8) zChjjM`#1Ta8ey+^S?$oXR5+no()txr*3&$U1vF~@IZV5H7w&*&TKOf}>MqJRC`kky z9|hiillHs2>lxyul=YplxGmt+yBpTY!V#n{?`j9bN+G#E(3_BB@n5&|plS@Xsd*e? zz+m$qRVDy^)N#e&Kr61lzNGbCFlq?-boT(?NA$2F6d7TacR5gic+lh)11(7;7KhAK z+@V&4*Ho*wPX?p(Boy=6Or|CRr+ZkJnkXR&q0Fi5e`292DenRly~pk7G>Mpd z-dc{Q+!xd6aVn6PrIcnI)P>ic?dx#*vLAhbIm6BUK(Ge37=tO?*p4cth>ovr1lu`R z$i3JskX|&sTBkwu>YnH8e_)z#{9N@La($7Yas3}ZSTucuJtBslnCxI( zG<_Hzy%ZzrehKAZ2(#J8dmHE79D$&Bk4lfEX0&aVHMJ3a6}2UY;XiI9SZcv?Cest( zNR20M*PH5_mA13Zp1Ce5_$PB9=RJT5Eqv2RB;Up=(GgI2#HxuUk zwtR?|?u+AGYft&Lc_^ANT2Ymh!;gl+z>kOOPaOZZDiLh<7c{V9G_Y&=3ZCdblGRu(!EHi!*8f6s#2Cn_8{7Y_<6idAT&!0V zF}v~ZFt{EaMFzu?^J5%6PpF}LN9vuoJ*S0wjr`MR(h-C-smDbdQUIu7wpr022@5%w zlN)J27&UhUeePqT+h*#RxrvXfEa*$Zddc&5w2#vfzuLB{Se<^o@oH$!uyU8ae4br% z$UBR!!Fz}sd@kB70o8Cns2PZQ#=a4bVkBZRSmh59mnWre{;_biX;0150%(zqwfMNW z*;_vPqd<~mwGKH+y#^8;<7jA+#5t2`5r()7gF$4Z{w;QV*|oX`7CN66cBXo~z}8D6 zHE!S5XIRUz%dv50dtc4=ka7#~X?(Ihz(}kXKEwy1BFoT+LGm1;)H$ zXb!UC&R&^4O2k-|caS%^f+H4cz|xQHPg`C13Ok;HHJ=r6(uJZ|rr?ypA+RVz($3PE zX<7f*0Ij7Q1SQdx5`PFj=)w3Wt_DLAndh>DJF9M|ipWF91PLm0DD1~?lMPEwD9

tgLj&t$3buEE%KP%VqVH1CL%nk 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 H41WGE2d!y} literal 0 HcmV?d00001 diff --git a/drexelundweiss/drexelundweiss.pro b/drexelundweiss/drexelundweiss.pro index f8fe629..0638497 100644 --- a/drexelundweiss/drexelundweiss.pro +++ b/drexelundweiss/drexelundweiss.pro @@ -1,17 +1,15 @@ include(../plugins.pri) -TARGET = $$qtLibraryTarget(nymea_deviceplugindrexelundweiss) - QT += \ serialport \ serialbus \ SOURCES += \ - deviceplugindrexelundweiss.cpp \ + integrationplugindrexelundweiss.cpp \ modbusrtumaster.cpp \ HEADERS += \ - deviceplugindrexelundweiss.h \ + integrationplugindrexelundweiss.h \ modbusrtumaster.h \ modbusdegisterdefinition.h diff --git a/drexelundweiss/deviceplugindrexelundweiss.cpp b/drexelundweiss/integrationplugindrexelundweiss.cpp similarity index 56% rename from drexelundweiss/deviceplugindrexelundweiss.cpp rename to drexelundweiss/integrationplugindrexelundweiss.cpp index efefece..94ed650 100644 --- a/drexelundweiss/deviceplugindrexelundweiss.cpp +++ b/drexelundweiss/integrationplugindrexelundweiss.cpp @@ -1,55 +1,58 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license * * GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. * -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . * -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq * -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#include "deviceplugindrexelundweiss.h" +#include "integrationplugindrexelundweiss.h" #include "plugininfo.h" #include "modbusdegisterdefinition.h" -DevicePluginDrexelUndWeiss::DevicePluginDrexelUndWeiss() +IntegrationPluginDrexelUndWeiss::IntegrationPluginDrexelUndWeiss() { } -DevicePluginDrexelUndWeiss::~DevicePluginDrexelUndWeiss() +IntegrationPluginDrexelUndWeiss::~IntegrationPluginDrexelUndWeiss() { } -void DevicePluginDrexelUndWeiss::init() +void IntegrationPluginDrexelUndWeiss::init() { - connect(this, &DevicePluginDrexelUndWeiss::configValueChanged, this, &DevicePluginDrexelUndWeiss::onPluginConfigurationChanged); + connect(this, &IntegrationPluginDrexelUndWeiss::configValueChanged, this, &IntegrationPluginDrexelUndWeiss::onPluginConfigurationChanged); } -void DevicePluginDrexelUndWeiss::discoverDevices(DeviceDiscoveryInfo *info) +void IntegrationPluginDrexelUndWeiss::discoverThings(ThingDiscoveryInfo *info) { // Create the list of available serial interfaces - QList deviceDescriptors; + QList thingDescriptors; - if (info->deviceClassId() == modbusConnectionDeviceClassId) { + if (info->thingClassId() == modbusConnectionThingClassId) { Q_FOREACH(QSerialPortInfo port, QSerialPortInfo::availablePorts()) { if (m_usedSerialPorts.contains(port.systemLocation())){ //device already in use @@ -58,87 +61,87 @@ void DevicePluginDrexelUndWeiss::discoverDevices(DeviceDiscoveryInfo *info) //Serial port is not yet used, create now a new one qCDebug(dcDrexelUndWeiss()) << "Found serial port:" << port.portName(); QString description = port.manufacturer() + " " + port.description(); - DeviceDescriptor descriptor(info->deviceClassId(), port.portName(), description); + ThingDescriptor descriptor(info->thingClassId(), port.portName(), description); ParamList parameters; - parameters.append(Param(modbusConnectionDeviceSerialPortParamTypeId, port.systemLocation())); + parameters.append(Param(modbusConnectionThingSerialPortParamTypeId, port.systemLocation())); descriptor.setParams(parameters); - info->addDeviceDescriptor(descriptor); + info->addThingDescriptor(descriptor); } } - info->finish(Device::DeviceErrorNoError); + info->finish(Thing::ThingErrorNoError); return; } - if (info->deviceClassId() == x2luDeviceClassId) { - info->finish(Device::DeviceErrorNoError); + if (info->thingClassId() == x2luThingClassId) { + info->finish(Thing::ThingErrorNoError); return; } - if (info->deviceClassId() == x2wpDeviceClassId) { - info->finish(Device::DeviceErrorNoError); + if (info->thingClassId() == x2wpThingClassId) { + info->finish(Thing::ThingErrorNoError); return; } - info->finish(Device::DeviceErrorDeviceClassNotFound); + info->finish(Thing::ThingErrorDeviceClassNotFound); return; } -void DevicePluginDrexelUndWeiss::setupDevice(DeviceSetupInfo *info) +void IntegrationPluginDrexelUndWeiss::setupThing(ThingSetupInfo *info) { - Device *device = info->device(); + Thing *thing = info->thing(); - if (device->deviceClassId() == modbusConnectionDeviceClassId) { + if (thing->thingClassId() == modbusConnectionThingClassId) { - QString serialPort = device->paramValue(modbusConnectionDeviceSerialPortParamTypeId).toString(); - int baudRate = device->paramValue(modbusConnectionDeviceBaudRateParamTypeId).toInt(); + QString serialPort = thing->paramValue(modbusConnectionThingSerialPortParamTypeId).toString(); + int baudRate = thing->paramValue(modbusConnectionThingBaudRateParamTypeId).toInt(); ModbusRTUMaster *modbus = new ModbusRTUMaster(serialPort, baudRate, QSerialPort::Parity::NoParity, 8, 1, this); - connect(modbus, &ModbusRTUMaster::connectionStateChanged, this, &DevicePluginDrexelUndWeiss::onConnectionStateChanged); - connect(modbus, &ModbusRTUMaster::receivedCoil, this, &DevicePluginDrexelUndWeiss::onReceivedCoil); - connect(modbus, &ModbusRTUMaster::receivedDiscreteInput, this, &DevicePluginDrexelUndWeiss::onReceivedDiscreteInput); - connect(modbus, &ModbusRTUMaster::receivedHoldingRegister, this, &DevicePluginDrexelUndWeiss::onReceivedHoldingRegister); - connect(modbus, &ModbusRTUMaster::receivedInputRegister, this, &DevicePluginDrexelUndWeiss::onReceivedInputRegister); + connect(modbus, &ModbusRTUMaster::connectionStateChanged, this, &IntegrationPluginDrexelUndWeiss::onConnectionStateChanged); + connect(modbus, &ModbusRTUMaster::receivedCoil, this, &IntegrationPluginDrexelUndWeiss::onReceivedCoil); + connect(modbus, &ModbusRTUMaster::receivedDiscreteInput, this, &IntegrationPluginDrexelUndWeiss::onReceivedDiscreteInput); + connect(modbus, &ModbusRTUMaster::receivedHoldingRegister, this, &IntegrationPluginDrexelUndWeiss::onReceivedHoldingRegister); + connect(modbus, &ModbusRTUMaster::receivedInputRegister, this, &IntegrationPluginDrexelUndWeiss::onReceivedInputRegister); - m_modbusRTUMasters.insert(device, modbus); + m_modbusRTUMasters.insert(thing, modbus); m_usedSerialPorts.append(serialPort); - info->finish(Device::DeviceErrorNoError); + info->finish(Thing::ThingErrorNoError); return; } - if (device->deviceClassId() == x2luDeviceClassId) { - info->finish(Device::DeviceErrorNoError); + if (thing->thingClassId() == x2luThingClassId) { + info->finish(Thing::ThingErrorNoError); return; } - if (device->deviceClassId() == x2wpDeviceClassId) { - info->finish(Device::DeviceErrorNoError); + if (thing->thingClassId() == x2wpThingClassId) { + info->finish(Thing::ThingErrorNoError); return; } - info->finish(Device::DeviceErrorDeviceClassNotFound); + info->finish(Thing::ThingErrorDeviceClassNotFound); return; } -void DevicePluginDrexelUndWeiss::postSetupDevice(Device *device) +void IntegrationPluginDrexelUndWeiss::postSetupDevice(Thing *thing) { if (!m_refreshTimer) { // Refresh timer for TCP read int refreshTime = configValue(drexelUndWeissPluginUpdateIntervalParamTypeId).toInt(); m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(refreshTime); - connect(m_refreshTimer, &PluginTimer::timeout, this, &DevicePluginDrexelUndWeiss::onRefreshTimer); + connect(m_refreshTimer, &PluginTimer::timeout, this, &IntegrationPluginDrexelUndWeiss::onRefreshTimer); } - if (device->deviceClassId() == modbusConnectionDeviceClassId) { + if (thing->thingClassId() == modbusConnectionThingClassId) { // read Register 5000 and emit auto-device ModbusRTUMaster *modbus = m_modbusRTUMasters.value(device); if (!modbus){ qCWarning(dcDrexelUndWeiss()) << "No modbus master available"; } - device->setStateValue(modbusConnectionConnectedStateTypeId, true); + thing->setStateValue(modbusConnectionConnectedStateTypeId, true); } - if ((device->deviceClassId() == x2luDeviceClassId) || (device->deviceClassId() == x2wpDeviceClassId)) { - Device *parentDevice = myDevices().findById(device->parentId()); + if ((thing->thingClassId() == x2luThingClassId) || (thing->thingClassId() == x2wpThingClassId)) { + Device *parentDevice = myThings().findById(thing->parentId()); if (!parentDevice) { qWarning(dcDrexelUndWeiss()) << "Could not find the parent device"; return; @@ -152,40 +155,40 @@ void DevicePluginDrexelUndWeiss::postSetupDevice(Device *device) } } -void DevicePluginDrexelUndWeiss::executeAction(DeviceActionInfo *info) +void IntegrationPluginDrexelUndWeiss::executeAction(DeviceActionInfo *info) { - Device *device = info->device(); + Thing *thing = info->device(); Action action = info->action(); - if (device->deviceClassId() == modbusConnectionDeviceClassId) { + if (thing->thingClassId() == modbusConnectionThingClassId) { ModbusRTUMaster *modbus = m_modbusRTUMasters.value(device); if (!modbus){ qCWarning(dcDrexelUndWeiss()) << "No modbus interface available"; - info->finish(Device::DeviceErrorHardwareFailure); + info->finish(Thing::ThingErrorHardwareFailure); return; } if (action.actionTypeId() == modbusConnectionDiscoverDevicesActionTypeId) { int slave = action.param(modbusConnectionDiscoverDevicesActionSlaveAddressParamTypeId).value().toInt(); discoverModbusSlaves(modbus, slave); - info->finish(Device::DeviceErrorNoError); + info->finish(Thing::ThingErrorNoError); return; } else { - info->finish(Device::DeviceErrorActionTypeNotFound); + info->finish(Thing::ThingErrorActionTypeNotFound); } - } else if (device->deviceClassId() == x2luDeviceClassId) { - Device *parentDevice = myDevices().findById(device->parentId()); + } else if (thing->thingClassId() == x2luThingClassId) { + Device *parentDevice = myThings().findById(thing->parentId()); if (!parentDevice) { qWarning(dcDrexelUndWeiss()) << "Could not find the parent device"; - info->finish(Device::DeviceErrorHardwareFailure); + info->finish(Thing::ThingErrorHardwareFailure); return; } ModbusRTUMaster *modbus = m_modbusRTUMasters.value(parentDevice); - int slave = device->paramValue(x2luDeviceSlaveAddressParamTypeId).toInt(); + int slave = thing->paramValue(x2luThingSlaveAddressParamTypeId).toInt(); if (!modbus){ qCWarning(dcDrexelUndWeiss()) << "No modbus interface available"; - info->finish(Device::DeviceErrorHardwareFailure); + info->finish(Thing::ThingErrorHardwareFailure); return; } @@ -209,20 +212,20 @@ void DevicePluginDrexelUndWeiss::executeAction(DeviceActionInfo *info) m_pendingActions.insert(modbus->writeHoldingRegister(slave, ModbusRegisterX2::Betriebsart, data), info); return; } else { - info->finish(Device::DeviceErrorActionTypeNotFound); + info->finish(Thing::ThingErrorActionTypeNotFound); } - } else if (device->deviceClassId() == x2wpDeviceClassId) { - Device *parentDevice = myDevices().findById(device->parentId()); + } else if (thing->thingClassId() == x2wpThingClassId) { + Device *parentDevice = myThings().findById(thing->parentId()); if (!parentDevice) { qWarning(dcDrexelUndWeiss()) << "Could not find modbus interface"; - info->finish(Device::DeviceErrorHardwareFailure); + info->finish(Thing::ThingErrorHardwareFailure); return; } ModbusRTUMaster *modbus = m_modbusRTUMasters.value(parentDevice); - int slave = device->paramValue(x2wpDeviceSlaveAddressParamTypeId).toInt(); + int slave = thing->paramValue(x2wpThingSlaveAddressParamTypeId).toInt(); if (!modbus){ qCWarning(dcDrexelUndWeiss()) << "No modbus master available"; - info->finish(Device::DeviceErrorHardwareFailure); + info->finish(Thing::ThingErrorHardwareFailure); return; } @@ -237,17 +240,17 @@ void DevicePluginDrexelUndWeiss::executeAction(DeviceActionInfo *info) m_pendingActions.insert(modbus->writeHoldingRegister(slave, ModbusRegisterX2::BrauchwasserSolltermperatur, data), info); return; } else { - info->finish(Device::DeviceErrorActionTypeNotFound); + info->finish(Thing::ThingErrorActionTypeNotFound); } } else { - info->finish(Device::DeviceErrorDeviceClassNotFound); + info->finish(Thing::ThingErrorDeviceClassNotFound); } } -void DevicePluginDrexelUndWeiss::deviceRemoved(Device *device) +void IntegrationPluginDrexelUndWeiss::deviceRemoved(Thing *thing) { - if (device->deviceClassId() == modbusConnectionDeviceClassId) { + if (thing->thingClassId() == modbusConnectionThingClassId) { ModbusRTUMaster *modbus = m_modbusRTUMasters.take(device); if (!modbus){ @@ -259,39 +262,39 @@ void DevicePluginDrexelUndWeiss::deviceRemoved(Device *device) } } -void DevicePluginDrexelUndWeiss::onRefreshTimer() +void IntegrationPluginDrexelUndWeiss::onRefreshTimer() { - foreach (Device *device, myDevices()) { + foreach (Thing *thing, myThings()) { - if (device->deviceClassId() == modbusConnectionDeviceClassId) { + if (thing->thingClassId() == modbusConnectionThingClassId) { ModbusRTUMaster *modbus = m_modbusRTUMasters.value(device); if (!modbus) { qCWarning(dcDrexelUndWeiss()) << "No modbus master available"; return; } - } else if (device->deviceClassId() == x2luDeviceClassId || device->deviceClassId() == x2wpDeviceClassId){ + } else if (thing->thingClassId() == x2luThingClassId || thing->thingClassId() == x2wpThingClassId){ updateStates(device); } } } -void DevicePluginDrexelUndWeiss::updateStates(Device *device) +void IntegrationPluginDrexelUndWeiss::updateStates(Thing *thing) { - if (device->deviceClassId() == x2luDeviceClassId) { - Device *parent = myDevices().findById(device->parentId()); + if (thing->thingClassId() == x2luThingClassId) { + Device *parent = myThings().findById(thing->parentId()); ModbusRTUMaster *modbus = m_modbusRTUMasters.value(parent); - int slave = device->paramValue(x2luDeviceSlaveAddressParamTypeId).toInt(); + int slave = thing->paramValue(x2luThingSlaveAddressParamTypeId).toInt(); modbus->readHoldingRegister(slave, ModbusRegisterX2::AktiveLuefterstufe); modbus->readHoldingRegister(slave, ModbusRegisterX2::Betriebsart); // Ventilation mode modbus->readHoldingRegister(slave, ModbusRegisterX2::CO2); } - if (device->deviceClassId() == x2wpDeviceClassId) { - Device *parent = myDevices().findById(device->parentId()); + if (thing->thingClassId() == x2wpThingClassId) { + Device *parent = myThings().findById(thing->parentId()); ModbusRTUMaster *modbus = m_modbusRTUMasters.value(parent); - int slave = device->paramValue(x2wpDeviceSlaveAddressParamTypeId).toInt(); + int slave = thing->paramValue(x2wpThingSlaveAddressParamTypeId).toInt(); modbus->readHoldingRegister(slave, ModbusRegisterX2::Waermepumpe); modbus->readHoldingRegister(slave, ModbusRegisterX2::RaumSoll); @@ -312,7 +315,7 @@ void DevicePluginDrexelUndWeiss::updateStates(Device *device) } -void DevicePluginDrexelUndWeiss::onPluginConfigurationChanged(const ParamTypeId ¶mTypeId, const QVariant &value) +void IntegrationPluginDrexelUndWeiss::onPluginConfigurationChanged(const ParamTypeId ¶mTypeId, const QVariant &value) { // Check refresh schedule if (paramTypeId == drexelUndWeissPluginUpdateIntervalParamTypeId) { @@ -324,226 +327,226 @@ void DevicePluginDrexelUndWeiss::onPluginConfigurationChanged(const ParamTypeId } } -void DevicePluginDrexelUndWeiss::onConnectionStateChanged(bool status) +void IntegrationPluginDrexelUndWeiss::onConnectionStateChanged(bool status) { Q_UNUSED(status) } -void DevicePluginDrexelUndWeiss::onReceivedCoil(int slaveAddress, int modbusRegister, bool value) +void IntegrationPluginDrexelUndWeiss::onReceivedCoil(int slaveAddress, int modbusRegister, bool value) { Q_UNUSED(slaveAddress) Q_UNUSED(modbusRegister) Q_UNUSED(value) } -void DevicePluginDrexelUndWeiss::onReceivedDiscreteInput(int slaveAddress, int modbusRegister, bool value) +void IntegrationPluginDrexelUndWeiss::onReceivedDiscreteInput(int slaveAddress, int modbusRegister, bool value) { Q_UNUSED(slaveAddress) Q_UNUSED(modbusRegister) Q_UNUSED(value) } -void DevicePluginDrexelUndWeiss::onReceivedHoldingRegister(int slaveAddress, int modbusRegister, int value) +void IntegrationPluginDrexelUndWeiss::onReceivedHoldingRegister(int slaveAddress, int modbusRegister, int value) { ModbusRTUMaster *modbus = static_cast(sender()); if (m_modbusRTUMasters.values().contains(modbus) ){ Device *parentDevice = m_modbusRTUMasters.key(static_cast(modbus)); - foreach(Device *device, myDevices().filterByParentDeviceId(parentDevice->id())) { - if (device->deviceClassId() == x2luDeviceClassId && device->paramValue(x2luDeviceSlaveAddressParamTypeId) == slaveAddress) { + foreach(Thing *thing, myThings().filterByParentDeviceId(parentthing->id())) { + if (thing->thingClassId() == x2luThingClassId && thing->paramValue(x2luThingSlaveAddressParamTypeId) == slaveAddress) { switch (modbusRegister) { case ModbusRegisterX2::Waermepumpe: - device->setStateValue(x2wpPowerStateTypeId, value); + thing->setStateValue(x2wpPowerStateTypeId, value); break; case ModbusRegisterX2::RaumSoll: - device->setStateValue(x2wpTargetTemperatureStateTypeId, value/1000.00); + thing->setStateValue(x2wpTargetTemperatureStateTypeId, value/1000.00); break; case ModbusRegisterX2::Raum: - device->setStateValue(x2wpTemperatureStateTypeId, value/1000.00); + thing->setStateValue(x2wpTemperatureStateTypeId, value/1000.00); break; case ModbusRegisterX2::TemperaturWarmwasserspeicherUnten: - device->setStateValue(x2wpWaterTemperatureStateTypeId, value/1000.00); + thing->setStateValue(x2wpWaterTemperatureStateTypeId, value/1000.00); break; case ModbusRegisterX2::BrauchwasserSolltermperatur: - device->setStateValue(x2wpTargetWaterTemperatureStateTypeId, value/1000.00); + thing->setStateValue(x2wpTargetWaterTemperatureStateTypeId, value/1000.00); break; case ModbusRegisterX2::Auszenluft: - device->setStateValue(x2wpOutsideAirTemperatureStateTypeId, value/1000.00); + thing->setStateValue(x2wpOutsideAirTemperatureStateTypeId, value/1000.00); break; case ModbusRegisterX2::Summenstoerung: if (value != 0) { //get actual error } else { - device->setStateValue(x2wpErrorStateTypeId, "No Error"); + thing->setStateValue(x2wpErrorStateTypeId, "No Error"); } break; case ModbusRegisterX2::StoerungAbluftventilator: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Exhaust fan"); + thing->setStateValue(x2wpErrorStateTypeId, "Exhaust fan"); break; case ModbusRegisterX2::StoerungBoilerfuehlerElektroheizstab: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Boiler sensor electric heating element"); + thing->setStateValue(x2wpErrorStateTypeId, "Boiler sensor electric heating element"); break; case ModbusRegisterX2::StoerungBoilerfuehlerSolar: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Boiler sensor solar"); + thing->setStateValue(x2wpErrorStateTypeId, "Boiler sensor solar"); break; case ModbusRegisterX2::StoerungBoilerfuehlerWaermepumpe: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Boiler sensor heat pump"); + thing->setStateValue(x2wpErrorStateTypeId, "Boiler sensor heat pump"); break; case ModbusRegisterX2::StoerungBoileruebertemperatur: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Boiler overtemperature"); + thing->setStateValue(x2wpErrorStateTypeId, "Boiler overtemperature"); break; case ModbusRegisterX2::StoerungCO2Sensor: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "CO2-Sensor"); + thing->setStateValue(x2wpErrorStateTypeId, "CO2-Sensor"); break; case ModbusRegisterX2::StoerungDruckverlustAbluftZuGrosz: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Pressure loss exhaust air too big"); + thing->setStateValue(x2wpErrorStateTypeId, "Pressure loss exhaust air too big"); break; case ModbusRegisterX2::StoerungDruckverlustZuluftZuGrosz: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Pressure loss supply air too large"); + thing->setStateValue(x2wpErrorStateTypeId, "Pressure loss supply air too large"); break; case ModbusRegisterX2::StoerungDurchflussmengeHeizgkreis: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Flow rate of heating circuit"); + thing->setStateValue(x2wpErrorStateTypeId, "Flow rate of heating circuit"); break; case ModbusRegisterX2::StoerungDurchflussmengeSolekreis: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Flow rate brine circuit"); + thing->setStateValue(x2wpErrorStateTypeId, "Flow rate brine circuit"); break; case ModbusRegisterX2::StoerungTeilnehmerNichtErreichbar: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Participant not available"); + thing->setStateValue(x2wpErrorStateTypeId, "Participant not available"); break; case ModbusRegisterX2::StoerungTemperaturfuehlerAuszenluft: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Temperature sensor outside air"); + thing->setStateValue(x2wpErrorStateTypeId, "Temperature sensor outside air"); break; case ModbusRegisterX2::StoerungTemperaturfuehlerHeizkreisVorlauf: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Temperature sensor heating circuit flow"); + thing->setStateValue(x2wpErrorStateTypeId, "Temperature sensor heating circuit flow"); break; case ModbusRegisterX2::StoerungTemperaturfuehlerRaum: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Temperature sensor room"); + thing->setStateValue(x2wpErrorStateTypeId, "Temperature sensor room"); break; case ModbusRegisterX2::StoerungTemperaturfuehlerSolarkollektor: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Temperature sensor solar collector"); + thing->setStateValue(x2wpErrorStateTypeId, "Temperature sensor solar collector"); break; case ModbusRegisterX2::StoerungTemperaturfuehlerSole: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Temperature sensor brine"); + thing->setStateValue(x2wpErrorStateTypeId, "Temperature sensor brine"); break; case ModbusRegisterX2::StoerungTemperaturfuehlerSoleAuszenluft: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Temperature sensor brine outside air"); + thing->setStateValue(x2wpErrorStateTypeId, "Temperature sensor brine outside air"); break; case ModbusRegisterX2::StoerungWaermepumpeHochdruck: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Heat pump high pressure"); + thing->setStateValue(x2wpErrorStateTypeId, "Heat pump high pressure"); break; case ModbusRegisterX2::StoerungWaermepumpeNiederdruck: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Heat pump low pressure"); + thing->setStateValue(x2wpErrorStateTypeId, "Heat pump low pressure"); break; case ModbusRegisterX2::StoerungWertNichtZulaessig: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Value not allowed"); + thing->setStateValue(x2wpErrorStateTypeId, "Value not allowed"); break; case ModbusRegisterX2::StoerungZuluftventilator: if (value != 0) - device->setStateValue(x2wpErrorStateTypeId, "Supply air fan"); + thing->setStateValue(x2wpErrorStateTypeId, "Supply air fan"); break; case ModbusRegisterX2::LeistungKompressor: - device->setStateValue(x2wpPowerCompressorStateTypeId, value/1000.00); + thing->setStateValue(x2wpPowerCompressorStateTypeId, value/1000.00); break; case ModbusRegisterX2::LeistungWarmwasser: - device->setStateValue(x2wpPowerWaterHeatingStateTypeId, value/1000.00); + thing->setStateValue(x2wpPowerWaterHeatingStateTypeId, value/1000.00); break; case ModbusRegisterX2::LeistungRaumheizung: - device->setStateValue(x2wpPowerRoomHeatingStateTypeId, value/1000.00); + thing->setStateValue(x2wpPowerRoomHeatingStateTypeId, value/1000.00); break; case ModbusRegisterX2::LeistungLuftvorwaermung: - device->setStateValue(x2wpPowerAirPreheatingStateTypeId, value/1000.00); + thing->setStateValue(x2wpPowerAirPreheatingStateTypeId, value/1000.00); break; case ModbusRegisterX2::EnergieKompressor: - device->setStateValue(x2wpEnergyCompressorStateTypeId, value/1000.00); + thing->setStateValue(x2wpEnergyCompressorStateTypeId, value/1000.00); break; case ModbusRegisterX2::EnergieWarmwasser: - device->setStateValue(x2wpEnergyWaterHeatingStateTypeId, value/1000.00); + thing->setStateValue(x2wpEnergyWaterHeatingStateTypeId, value/1000.00); break; case ModbusRegisterX2::EnergieRaumheizung: - device->setStateValue(x2wpEnergyRoomHeatingStateTypeId, value/1000.00); + thing->setStateValue(x2wpEnergyRoomHeatingStateTypeId, value/1000.00); break; case ModbusRegisterX2::EnergieLuftvorerwarrmung: - device->setStateValue(x2wpEnergyAirPreheatingStateTypeId, value/1000.00); + thing->setStateValue(x2wpEnergyAirPreheatingStateTypeId, value/1000.00); break; default: break; } - } else if (device->deviceClassId() == x2wpDeviceClassId && device->paramValue(x2wpDeviceSlaveAddressParamTypeId) == slaveAddress) { + } else if (thing->thingClassId() == x2wpThingClassId && thing->paramValue(x2wpThingSlaveAddressParamTypeId) == slaveAddress) { switch (modbusRegister) { case ModbusRegisterX2::Betriebsart: if (value == VentialtionMode::ManuellStufe0) { - device->setStateValue(x2luVentilationModeStateTypeId, "Manual level 0"); + thing->setStateValue(x2luVentilationModeStateTypeId, "Manual level 0"); } else if (value == VentialtionMode::ManuellStufe1) { - device->setStateValue(x2luVentilationModeStateTypeId, "Manual level 1"); + thing->setStateValue(x2luVentilationModeStateTypeId, "Manual level 1"); } else if (value == VentialtionMode::ManuellStufe2) { - device->setStateValue(x2luVentilationModeStateTypeId, "Manual level 2"); + thing->setStateValue(x2luVentilationModeStateTypeId, "Manual level 2"); } else if (value == VentialtionMode::ManuellStufe3) { - device->setStateValue(x2luVentilationModeStateTypeId, "Manual level 3"); + thing->setStateValue(x2luVentilationModeStateTypeId, "Manual level 3"); } else if (value == VentialtionMode::Automatikbetrieb) { - device->setStateValue(x2luVentilationModeStateTypeId, "Automatic"); + thing->setStateValue(x2luVentilationModeStateTypeId, "Automatic"); } else if (value == VentialtionMode::Party) { - device->setStateValue(x2luVentilationModeStateTypeId, "Party"); + thing->setStateValue(x2luVentilationModeStateTypeId, "Party"); } break; case ModbusRegisterX2::AktiveLuefterstufe: - device->setStateValue(x2luActiveVentilationLevelStateTypeId, value); + thing->setStateValue(x2luActiveVentilationLevelStateTypeId, value); break; case ModbusRegisterX2::CO2: - device->setStateValue(x2luCo2StateTypeId, value); + thing->setStateValue(x2luCo2StateTypeId, value); break; } } @@ -553,30 +556,30 @@ void DevicePluginDrexelUndWeiss::onReceivedHoldingRegister(int slaveAddress, int switch (value) { case DeviceType::X2_WP: { qDebug(dcDrexelUndWeiss()) << "Discovered X2 heat pump"; - QList deviceDescriptors; - DeviceDescriptor descriptor(x2wpDeviceClassId, "X2 WP", "Drexel und Weiss", parentDevice->id()); + QList thingDescriptors; + ThingDescriptor descriptor(x2wpThingClassId, "X2 WP", "Drexel und Weiss", parentthing->id()); ParamList params; //modbus->readHoldingRegister(slaveAddress, ModbusRegisterX2::SoftwareVersion); - //params.append(Param(x2wpDeviceSofwareVersionParamTypeId, data)); - params.append(Param(x2wpDeviceSlaveAddressParamTypeId, slaveAddress)); + //params.append(Param(x2wpThingSofwareVersionParamTypeId, data)); + params.append(Param(x2wpThingSlaveAddressParamTypeId, slaveAddress)); descriptor.setParams(params); - deviceDescriptors.append(descriptor); - emit autoDevicesAppeared(deviceDescriptors); + thingDescriptors.append(descriptor); + emit autoDevicesAppeared(thingDescriptors); break; } case DeviceType::X2_LU: { qDebug(dcDrexelUndWeiss()) << "Discovered X2 ventilation unit"; - QList deviceDescriptors; - DeviceDescriptor descriptor(x2luDeviceClassId, "X2 LU", "Drexel und Weiss", parentDevice->id()); + QList thingDescriptors; + ThingDescriptor descriptor(x2luThingClassId, "X2 LU", "Drexel und Weiss", parentthing->id()); ParamList params; //modbus->readHoldingRegister(slaveAddress, ModbusRegisterX2::SoftwareVersion); - //params.append(Param(x2luDeviceSofwareVersionParamTypeId, data)); - params.append(Param(x2luDeviceSlaveAddressParamTypeId, slaveAddress)); + //params.append(Param(x2luThingSofwareVersionParamTypeId, data)); + params.append(Param(x2luThingSlaveAddressParamTypeId, slaveAddress)); descriptor.setParams(params); - deviceDescriptors.append(descriptor); - emit autoDevicesAppeared(deviceDescriptors); + thingDescriptors.append(descriptor); + emit autoDevicesAppeared(thingDescriptors); break; } case DeviceType::AerosilentBianco: @@ -590,38 +593,38 @@ void DevicePluginDrexelUndWeiss::onReceivedHoldingRegister(int slaveAddress, int } } -void DevicePluginDrexelUndWeiss::onReceivedInputRegister(int slaveAddress, int modbusRegister, int value) +void IntegrationPluginDrexelUndWeiss::onReceivedInputRegister(int slaveAddress, int modbusRegister, int value) { Q_UNUSED(slaveAddress) Q_UNUSED(modbusRegister) Q_UNUSED(value) } -void DevicePluginDrexelUndWeiss::onWriteRequestFinished(QUuid requestId, bool success) +void IntegrationPluginDrexelUndWeiss::onWriteRequestFinished(QUuid requestId, bool success) { DeviceActionInfo *info = m_pendingActions.take(requestId); if (!info) return; if (success) { - info->finish(Device::DeviceErrorNoError); + info->finish(Thing::ThingErrorNoError); } else { - info->finish(Device::DeviceErrorHardwareFailure); + info->finish(Thing::ThingErrorHardwareFailure); } } -void DevicePluginDrexelUndWeiss::discoverModbusSlaves(ModbusRTUMaster *modbus, int slaveAddress) +void IntegrationPluginDrexelUndWeiss::discoverModbusSlaves(ModbusRTUMaster *modbus, int slaveAddress) { - foreach (Device *device, myDevices()) { - if (device->deviceClassId() == x2luDeviceClassId) { - if (device->paramValue(x2luDeviceSlaveAddressParamTypeId).toInt() == slaveAddress) { + foreach (Thing *thing, myThings()) { + if (thing->thingClassId() == x2luThingClassId) { + if (thing->paramValue(x2luThingSlaveAddressParamTypeId).toInt() == slaveAddress) { qWarning(dcDrexelUndWeiss()) << "Device with slave address" << slaveAddress << "already added"; return; } } - if (device->deviceClassId() == x2wpDeviceClassId) { - if (device->paramValue(x2wpDeviceSlaveAddressParamTypeId).toInt() == slaveAddress) { - qWarning(dcDrexelUndWeiss()) << "Device with slave address" << slaveAddress << "already added"; + if (thing->thingClassId() == x2wpThingClassId) { + if (thing->paramValue(x2wpThingSlaveAddressParamTypeId).toInt() == slaveAddress) { + qWarning(dcDrexelUndWeiss()) << "Thing with slave address" << slaveAddress << "already added"; return; } } diff --git a/drexelundweiss/integrationplugindrexelundweiss.h b/drexelundweiss/integrationplugindrexelundweiss.h new file mode 100644 index 0000000..84133b4 --- /dev/null +++ b/drexelundweiss/integrationplugindrexelundweiss.h @@ -0,0 +1,81 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef INTEGRATIONPLUGINDREXELUNDWEISS_H +#define INTEGRATIONPLUGINDREXELUNDWEISS_H + +#include "integrations/integrationplugin.h" +#include "plugintimer.h" + +#include +#include "modbusrtumaster.h" +#include + +class DevicePluginDrexelUndWeiss : public IntegrationPlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationplugindrexelundweiss.json") + Q_INTERFACES(IntegrationPlugin) + +public: + explicit DevicePluginDrexelUndWeiss(); + ~DevicePluginDrexelUndWeiss() override; + void init() override; + + void discoverThings(ThingDiscoveryInfo *info) override; + void setupThing(ThingSetupInfo *info) override; + void postSetupThing(Thing *thing) override; + void thingRemoved(Thing *thing) override; + void executeAction(ThingActionInfo *info) override; + +private: + QList m_usedSerialPorts; + QHash m_modbusRTUMasters; + PluginTimer *m_refreshTimer = nullptr; + QHash m_pendingActions; + + void updateStates(Device *device); + void discoverModbusSlaves(ModbusRTUMaster *modbus, int slaveAddress); + +private slots: + void onRefreshTimer(); + void onPluginConfigurationChanged(const ParamTypeId ¶mTypeId, const QVariant &value); + + void onConnectionStateChanged(bool status); + void onReceivedCoil(int slaveAddress, int modbusRegister, bool value); + void onReceivedDiscreteInput(int slaveAddress, int modbusRegister, bool value); + void onReceivedHoldingRegister(int slaveAddress, int modbusRegister, int value); + void onReceivedInputRegister(int slaveAddress, int modbusRegister, int value); + + void onWriteRequestFinished(QUuid requestId, bool success); +}; + +#endif // INTEGRATIONPLUGINDREXELUNDWEISS_H diff --git a/drexelundweiss/deviceplugindrexelundweiss.json b/drexelundweiss/integrationplugindrexelundweiss.json similarity index 99% rename from drexelundweiss/deviceplugindrexelundweiss.json rename to drexelundweiss/integrationplugindrexelundweiss.json index 462ed36..57fb5d0 100644 --- a/drexelundweiss/deviceplugindrexelundweiss.json +++ b/drexelundweiss/integrationplugindrexelundweiss.json @@ -17,7 +17,7 @@ "name": "DrexelUndWeiss", "displayName": "Drexel und Weiss", "id": "9f476e8b-7e95-448e-b03b-874747e8fb1f", - "deviceClasses": [ + "thingClasses": [ { "name": "modbusConnection", "displayName": "Modbus Connection", diff --git a/drexelundweiss/meta.json b/drexelundweiss/meta.json new file mode 100644 index 0000000..994ede4 --- /dev/null +++ b/drexelundweiss/meta.json @@ -0,0 +1,13 @@ +{ + "title": "Drexel und Weiss", + "tagline": "Connect to Drexel und Weiss HVAC systems.", + "icon": "drexelundweiss.png", + "stability": "community", + "offline": true, + "technologies": [ + "network" + ], + "categories": [ + "heating" + ] +} diff --git a/drexelundweiss/modbusdegisterdefinition.h b/drexelundweiss/modbusdegisterdefinition.h index 2440882..534583b 100644 --- a/drexelundweiss/modbusdegisterdefinition.h +++ b/drexelundweiss/modbusdegisterdefinition.h @@ -1,29 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license * * GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. * -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . * -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq * -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef MODBUSDEGISTERDEFINITION #define MODBUSDEGISTERDEFINITION diff --git a/drexelundweiss/modbusrtumaster.cpp b/drexelundweiss/modbusrtumaster.cpp index 5b14dcd..ccf2e97 100644 --- a/drexelundweiss/modbusrtumaster.cpp +++ b/drexelundweiss/modbusrtumaster.cpp @@ -24,6 +24,7 @@ * or see our FAQ/Licensing Information on https://nymea.io/license/faq * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #include "modbusrtumaster.h" #include "extern-plugininfo.h" diff --git a/drexelundweiss/modbusrtumaster.h b/drexelundweiss/modbusrtumaster.h index e281f73..b7abc28 100644 --- a/drexelundweiss/modbusrtumaster.h +++ b/drexelundweiss/modbusrtumaster.h @@ -1,29 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license * * GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. * -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . * -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq * -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef MODBUSRTUMASTER_H #define MODBUSRTUMASTER_H diff --git a/mypv/devicepluginmypv.h b/mypv/devicepluginmypv.h deleted file mode 100644 index 48a14f2..0000000 --- a/mypv/devicepluginmypv.h +++ /dev/null @@ -1,87 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . -* -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef DEVICEPLUGINMYPV_H -#define DEVICEPLUGINMYPV_H - -#include "devices/deviceplugin.h" -#include "plugintimer.h" -#include "modbustcpmaster.h" - -#include - -class DevicePluginMyPv: public DevicePlugin -{ - Q_OBJECT - - Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginmypv.json") - Q_INTERFACES(DevicePlugin) - - -public: - explicit DevicePluginMyPv(); - - void discoverDevices(DeviceDiscoveryInfo *info) override; - void setupDevice(DeviceSetupInfo *info) override; - void postSetupDevice(Device *device) override; - void deviceRemoved(Device *device) override; - void executeAction(DeviceActionInfo *info) override; - -private: - - enum ElwaModbusRegisters { - Power= 1000, - WaterTemperature = 1001, - TargetWaterTemperature = 1002, - Status = 1003, - ManuelStart = 1012 - }; - - enum ElwaStatus { - Heating = 2, - Standby = 3, - Boosted = 4, - HeatFinished = 5, - Setup = 9, - ErrorOvertempFuseBlown = 201, - ErrorOvertempMeasured = 202, - ErrorOvertempElectronics = 203, - ErrorHardwareFault = 204, - ErrorTempSensor = 205 - }; - - PluginTimer *m_refreshTimer = nullptr; - QHash m_modbusTcpMasters; - - void update(Device *device); - -private slots: - void onRefreshTimer(); -}; - -#endif // DEVICEPLUGINMYPV_H - diff --git a/mypv/devicepluginmypv.cpp b/mypv/integrationpluginmypv.cpp similarity index 56% rename from mypv/devicepluginmypv.cpp rename to mypv/integrationpluginmypv.cpp index dd1d734..e04be9d 100644 --- a/mypv/devicepluginmypv.cpp +++ b/mypv/integrationpluginmypv.cpp @@ -1,43 +1,46 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license * * GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. * -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . * -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq * -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "plugininfo.h" -#include "devicepluginmypv.h" +#include "integrationpluginmypv.h" #include #include -DevicePluginMyPv::DevicePluginMyPv() +IntegrationPluginMyPv::IntegrationPluginMyPv() { } -void DevicePluginMyPv::discoverDevices(DeviceDiscoveryInfo *info) +void IntegrationPluginMyPv::discoverThings(ThingDiscoveryInfo *info) { QUdpSocket *searchSocket = new QUdpSocket(this); @@ -59,7 +62,7 @@ void DevicePluginMyPv::discoverDevices(DeviceDiscoveryInfo *info) } QTimer::singleShot(2000, this, [this, searchSocket, info](){ - QList descriptorList; + QList descriptorList; while(searchSocket->hasPendingDatagrams()) { char buffer[1024]; QHostAddress senderAddress; @@ -93,8 +96,8 @@ void DevicePluginMyPv::discoverDevices(DeviceDiscoveryInfo *info) QByteArray serialNumber = data.mid(8, 16); bool existing = false; - foreach (Device *existingDev, myDevices()) { - if (existingDev->deviceClassId() == info->deviceClassId() && existingDev->paramValue(elwaDeviceIpAddressParamTypeId).toString() == address.toString()) { + foreach (Thing *existingThing, myThings()) { + if (existingThing->thingClassId() == info->thingClassId() && existingThing->paramValue(elwaThingIpAddressParamTypeId).toString() == address.toString()) { existing = true; } } @@ -102,24 +105,24 @@ void DevicePluginMyPv::discoverDevices(DeviceDiscoveryInfo *info) qCDebug(dcMypv()) << "Already have device" << senderAddress << "in configured devices. Skipping..."; continue; } - DeviceDescriptor deviceDescriptors(info->deviceClassId(), "AC ELWA-E", address.toString()); + ThingDescriptor thingDescriptors(info->thingClassId(), "AC ELWA-E", address.toString()); ParamList params; - params << Param(elwaDeviceIpAddressParamTypeId, address.toString()); - params << Param(elwaDeviceSerialNumberParamTypeId, serialNumber); - deviceDescriptors.setParams(params); - descriptorList << deviceDescriptors; + params << Param(elwaThingIpAddressParamTypeId, address.toString()); + params << Param(elwaThingSerialNumberParamTypeId, serialNumber); + thingDescriptors.setParams(params); + descriptorList << thingDescriptors; } - info->addDeviceDescriptors(descriptorList);; + info->addThingDescriptors(descriptorList);; searchSocket->deleteLater(); }); } -void DevicePluginMyPv::setupDevice(DeviceSetupInfo *info) +void IntegrationPluginMyPv::setupThing(ThingSetupInfo *info) { - Device *device = info->device(); + Thing *thing = info->thing(); - if(device->deviceClassId() == elwaDeviceClassId) { - QHostAddress address = QHostAddress(device->paramValue(elwaDeviceIpAddressParamTypeId).toString()); + if(thing->thingClassId() == elwaThingClassId) { + QHostAddress address = QHostAddress(thing->paramValue(elwaThingIpAddressParamTypeId).toString()); ModbusTCPMaster *modbusTcpMaster = new ModbusTCPMaster(address, 502, this); if (!modbusTcpMaster->createInterface()) { @@ -127,134 +130,134 @@ void DevicePluginMyPv::setupDevice(DeviceSetupInfo *info) return; } - m_modbusTcpMasters.insert(device, modbusTcpMaster); + m_modbusTcpMasters.insert(thing, modbusTcpMaster); } else { - Q_ASSERT_X(false, "setupDevice", QString("Unhandled deviceClassId: %1").arg(device->deviceClassId().toString()).toUtf8()); + Q_ASSERT_X(false, "setupDevice", QString("Unhandled deviceClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); } } -void DevicePluginMyPv::postSetupDevice(Device *device) +void IntegrationPluginMyPv::postSetupThing(Thing *thing) { - if (device->deviceClassId() == elwaDeviceClassId) { - update(device); + if (thing->thingClassId() == elwaThingClassId) { + update(thing); } } -void DevicePluginMyPv::deviceRemoved(Device *device) +void IntegrationPluginMyPv::thingRemoved(Thing *thing) { - if (device->deviceClassId() == elwaDeviceClassId) { - ModbusTCPMaster *modbusTCPMaster = m_modbusTcpMasters.take(device); + if (thing->thingClassId() == elwaThingClassId) { + ModbusTCPMaster *modbusTCPMaster = m_modbusTcpMasters.take(thing); modbusTCPMaster->deleteLater(); } } -void DevicePluginMyPv::executeAction(DeviceActionInfo *info) +void IntegrationPluginMyPv::executeAction(ThingActionInfo *info) { - Device *device = info->device(); + Thing *thing = info->thing(); Action action = info->action(); - if (device->deviceClassId() == elwaDeviceClassId) { + if (thing->thingClassId() == elwaThingClassId) { - ModbusTCPMaster *modbusTCPMaster = m_modbusTcpMasters.value(device); + ModbusTCPMaster *modbusTCPMaster = m_modbusTcpMasters.value(thing); if (action.actionTypeId() == elwaHeatingPowerActionTypeId) { int heatingPower = action.param(elwaHeatingPowerActionHeatingPowerParamTypeId).value().toInt(); if(!modbusTCPMaster->setRegister(0xff, ElwaModbusRegisters::Power, heatingPower)){ - return info->finish(Device::DeviceErrorHardwareFailure); + return info->finish(Thing::ThingErrorHardwareFailure); } return; } else if (action.actionTypeId() == elwaPowerActionTypeId) { bool power = action.param(elwaHeatingPowerActionHeatingPowerParamTypeId).value().toBool(); if(power) { if(!modbusTCPMaster->setRegister(0xff, ElwaModbusRegisters::ManuelStart, 1)){ - return info->finish(Device::DeviceErrorHardwareFailure); + return info->finish(Thing::ThingErrorHardwareFailure); } } } else { Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8()); } } else { - Q_ASSERT_X(false, "executeAction", QString("Unhandled deviceClassId: %1").arg(device->deviceClassId().toString()).toUtf8()); + Q_ASSERT_X(false, "executeAction", QString("Unhandled deviceClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); } } -void DevicePluginMyPv::onRefreshTimer(){ +void IntegrationPluginMyPv::onRefreshTimer(){ - foreach (Device *device, myDevices()) { - update(device); + foreach (Thing *thing, myThings()) { + update(thing); } } -void DevicePluginMyPv::update(Device *device) { - if (device->deviceClassId() == elwaDeviceClassId) +void IntegrationPluginMyPv::update(Thing *thing) { + if (thing->thingClassId() == elwaThingClassId) { - ModbusTCPMaster *modbusTCPMaster = m_modbusTcpMasters.value(device); + ModbusTCPMaster *modbusTCPMaster = m_modbusTcpMasters.value(thing); int data; if (modbusTCPMaster->getRegister(0xff, ElwaModbusRegisters::Status, &data)) { switch (data) { case Heating: { - device->setStateValue(elwaStatusStateTypeId, "Heating"); - device->setStateValue(elwaPowerStateTypeId, true); + thing->setStateValue(elwaStatusStateTypeId, "Heating"); + thing->setStateValue(elwaPowerStateTypeId, true); break; } case Standby:{ - device->setStateValue(elwaStatusStateTypeId, "Standby"); - device->setStateValue(elwaPowerStateTypeId, false); + thing->setStateValue(elwaStatusStateTypeId, "Standby"); + thing->setStateValue(elwaPowerStateTypeId, false); break; } case Boosted:{ - device->setStateValue(elwaStatusStateTypeId, "Boosted"); - device->setStateValue(elwaPowerStateTypeId, true); + thing->setStateValue(elwaStatusStateTypeId, "Boosted"); + thing->setStateValue(elwaPowerStateTypeId, true); break; } case HeatFinished:{ - device->setStateValue(elwaStatusStateTypeId, "Heat finished"); - device->setStateValue(elwaPowerStateTypeId, false); + thing->setStateValue(elwaStatusStateTypeId, "Heat finished"); + thing->setStateValue(elwaPowerStateTypeId, false); break; } case Setup:{ - device->setStateValue(elwaStatusStateTypeId, "Setup"); - device->setStateValue(elwaPowerStateTypeId, false); + thing->setStateValue(elwaStatusStateTypeId, "Setup"); + thing->setStateValue(elwaPowerStateTypeId, false); break; } case ErrorOvertempFuseBlown:{ - device->setStateValue(elwaStatusStateTypeId, "Error Overtemp Fuse blown"); + thing->setStateValue(elwaStatusStateTypeId, "Error Overtemp Fuse blown"); break; } case ErrorOvertempMeasured:{ - device->setStateValue(elwaStatusStateTypeId, "Error Overtemp measured"); + thing->setStateValue(elwaStatusStateTypeId, "Error Overtemp measured"); break; } case ErrorOvertempElectronics:{ - device->setStateValue(elwaStatusStateTypeId, "Error Overtemp Electronics"); + thing->setStateValue(elwaStatusStateTypeId, "Error Overtemp Electronics"); break; } case ErrorHardwareFault:{ - device->setStateValue(elwaStatusStateTypeId, "Error Hardware Fault"); + thing->setStateValue(elwaStatusStateTypeId, "Error Hardware Fault"); break; } case ErrorTempSensor:{ - device->setStateValue(elwaStatusStateTypeId, "Error Temp Sensor"); + thing->setStateValue(elwaStatusStateTypeId, "Error Temp Sensor"); break; } default: - device->setStateValue(elwaStatusStateTypeId, "Unknown"); + thing->setStateValue(elwaStatusStateTypeId, "Unknown"); } - device->setStateValue(elwaConnectedStateTypeId, true); + thing->setStateValue(elwaConnectedStateTypeId, true); } else { - device->setStateValue(elwaConnectedStateTypeId, false); + thing->setStateValue(elwaConnectedStateTypeId, false); } if (modbusTCPMaster->getRegister(0xff, ElwaModbusRegisters::WaterTemperature, &data)) { - device->setStateValue(elwaTemperatureStateTypeId, data/10.00); + thing->setStateValue(elwaTemperatureStateTypeId, data/10.00); } if (modbusTCPMaster->getRegister(0xff, ElwaModbusRegisters::TargetWaterTemperature, &data)) { - device->setStateValue(elwaTargetWaterTemperatureStateTypeId, data/10.00); + thing->setStateValue(elwaTargetWaterTemperatureStateTypeId, data/10.00); } if (modbusTCPMaster->getRegister(0xff, ElwaModbusRegisters::Power, &data)) { - device->setStateValue(elwaHeatingPowerStateTypeId, data); + thing->setStateValue(elwaHeatingPowerStateTypeId, data); } } } diff --git a/mypv/integrationpluginmypv.h b/mypv/integrationpluginmypv.h new file mode 100644 index 0000000..83d923a --- /dev/null +++ b/mypv/integrationpluginmypv.h @@ -0,0 +1,90 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef INTEGRATIONPLUGINMYPV_H +#define INTEGRATIONPLUGINMYPV_H + +#include "integrations/integrationplugin.h" +#include "plugintimer.h" +#include "modbustcpmaster.h" + +#include + +class IntegrationPluginMyPv: public IntegrationPlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginmypv.json") + Q_INTERFACES(IntegrationPlugin) + + +public: + explicit IntegrationPluginMyPv(); + + void discoverThings(ThingDiscoveryInfo *info) override; + void setupThing(ThingSetupInfo *info) override; + void postSetupThing(Thing *thing) override; + void thingRemoved(Thing *thing) override; + void executeAction(ThingActionInfo *info) override; + +private: + + enum ElwaModbusRegisters { + Power= 1000, + WaterTemperature = 1001, + TargetWaterTemperature = 1002, + Status = 1003, + ManuelStart = 1012 + }; + + enum ElwaStatus { + Heating = 2, + Standby = 3, + Boosted = 4, + HeatFinished = 5, + Setup = 9, + ErrorOvertempFuseBlown = 201, + ErrorOvertempMeasured = 202, + ErrorOvertempElectronics = 203, + ErrorHardwareFault = 204, + ErrorTempSensor = 205 + }; + + PluginTimer *m_refreshTimer = nullptr; + QHash m_modbusTcpMasters; + + void update(Thing *thing); + +private slots: + void onRefreshTimer(); +}; + +#endif // INTEGRATIONPLUGINMYPV_H + diff --git a/mypv/devicepluginmypv.json b/mypv/integrationpluginmypv.json similarity index 99% rename from mypv/devicepluginmypv.json rename to mypv/integrationpluginmypv.json index 4327b07..c13fddd 100644 --- a/mypv/devicepluginmypv.json +++ b/mypv/integrationpluginmypv.json @@ -7,7 +7,7 @@ "name": "myPV", "displayName": "my-PV", "id": "1f17597f-e0d0-459b-858d-ec9cbcd10b2c", - "deviceClasses": [ + "thingClasses": [ { "name": "elwa", "displayName": "AC Elwa E", diff --git a/mypv/meta.json b/mypv/meta.json new file mode 100644 index 0000000..43fa0ad --- /dev/null +++ b/mypv/meta.json @@ -0,0 +1,13 @@ +{ + "title": "my-PV", + "tagline": "Control my-PV network enabled heating rods.", + "icon": "mypv.png", + "stability": "community", + "offline": true, + "technologies": [ + "network" + ], + "categories": [ + "heating" + ] +} diff --git a/mypv/modbustcpmaster.cpp b/mypv/modbustcpmaster.cpp index 4590e9b..e895d77 100644 --- a/mypv/modbustcpmaster.cpp +++ b/mypv/modbustcpmaster.cpp @@ -1,29 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license * * GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. * -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . * -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq * -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "modbustcpmaster.h" #include "extern-plugininfo.h" diff --git a/mypv/modbustcpmaster.h b/mypv/modbustcpmaster.h index b214282..73907c0 100644 --- a/mypv/modbustcpmaster.h +++ b/mypv/modbustcpmaster.h @@ -1,29 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license * * GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. * -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . * -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq * -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef MODBUSTCPMASTER_H #define MODBUSTCPMASTER_H diff --git a/mypv/mypv.png b/mypv/mypv.png new file mode 100644 index 0000000000000000000000000000000000000000..a7b664d08f08b19065d2f1f4428aefb36b30ba16 GIT binary patch literal 4702 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 b|J+D$bUFTDc0osO7*Xgz475<1_Tm2lh2$nO literal 0 HcmV?d00001 diff --git a/mypv/mypv.pro b/mypv/mypv.pro index 51ab21d..9754d69 100644 --- a/mypv/mypv.pro +++ b/mypv/mypv.pro @@ -1,19 +1,15 @@ include(../plugins.pri) -TARGET = $$qtLibraryTarget(nymea_devicepluginmypv) - QT += \ network \ serialbus \ -LIBS += -lmodbus - SOURCES += \ - devicepluginmypv.cpp \ + integrationpluginmypv.cpp \ modbustcpmaster.cpp \ HEADERS += \ - devicepluginmypv.h \ + integrationpluginmypv.h \ modbustcpmaster.h \ diff --git a/wallbe/devicepluginwallbe.cpp b/wallbe/devicepluginwallbe.cpp deleted file mode 100644 index 361e7de..0000000 --- a/wallbe/devicepluginwallbe.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . -* -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "devicepluginwallbe.h" -#include "plugininfo.h" - -#include "devices/devicemanager.h" -#include "devices/device.h" -#include "types/param.h" - -#include -#include -#include -#include - - -DevicePluginWallbe::DevicePluginWallbe() -{ -} - - -void DevicePluginWallbe::setupDevice(DeviceSetupInfo *info) -{ - Device *device = info->device(); - qCDebug(dcWallbe) << "Setting up a new device:" << device->params(); - - QHostAddress address(device->paramValue(wallbeEcoDeviceIpParamTypeId).toString()); - - if (address.isNull()){ - qCWarning(dcWallbe) << "IP address is null"; - info->finish(Device::DeviceErrorSetupFailed, tr("IP address parameter not valid")); - return; - } - - WallBe *wallbe = new WallBe(address, 502, this); - m_connections.insert(device, wallbe); - - info->finish(Device::DeviceErrorNoError); -} - - -void DevicePluginWallbe::discoverDevices(DeviceDiscoveryInfo *info) -{ - if (info->deviceClassId() == wallbeEcoDeviceClassId){ - - Discover *discover = new Discover(QStringList("-xO" "-p 502")); - connect(discover, &Discover::devicesDiscovered, this, [info, this](QList hosts){ - foreach (Host host, hosts) { - DeviceDescriptor descriptor; - foreach(Device *device, myDevices().filterByParam(wallbeEcoDeviceMacParamTypeId, host.macAddress())) { - descriptor.setDeviceId(device->id()); - break; - } - descriptor.setTitle(host.hostName()); - ParamList params; - params.append(Param(wallbeEcoDeviceIpParamTypeId, host.address())); - params.append(Param(wallbeEcoDeviceMacParamTypeId, host.macAddress())); - descriptor.setParams(params); - info->addDeviceDescriptor(descriptor); - } - info->finish(Device::DeviceErrorNoError); - }); - return; - } - Q_ASSERT_X(false, "discoverDevices", QString("Unhandled deviceClassId: %1").arg(info->deviceClassId().toString()).toUtf8()); -} - - -void DevicePluginWallbe::postSetupDevice(Device *device) -{ - if (!m_pluginTimer) { - m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(120); - connect(m_pluginTimer, &PluginTimer::timeout, this, [this] { - foreach(Device *device, m_connections.keys()) { - update(device); - } - }); - } - if (device->deviceClassId() == wallbeEcoDeviceClassId){ - update(device); - return; - } - Q_ASSERT_X(false, "postSetupDevice", QString("Unhandled deviceClassId: %1").arg(device->deviceClassId().toString()).toUtf8()); -} - - -void DevicePluginWallbe::executeAction(DeviceActionInfo *info) -{ - Device *device = info->device(); - Action action = info->action(); - - WallBe *wallbe = m_connections.value(device); - if (!wallbe) { - qCWarning(dcWallbe()) << "Wallbe object not available"; - info->finish(Device::DeviceErrorHardwareFailure); - return; - } - - if (device->deviceClassId() == wallbeEcoDeviceClassId){ - - // check if this is the "set power" action - if (action.actionTypeId() == wallbeEcoPowerActionTypeId) { - - // get the param value of the charging action - bool charging = action.param(wallbeEcoPowerActionPowerParamTypeId).value().toBool(); - qCDebug(dcWallbe) << "start Charging button" << device->name() << "set power to" << charging; - wallbe->setChargingStatus(charging); - // Set the "power" state - device->setStateValue(wallbeEcoPowerStateTypeId, charging); - info->finish(Device::DeviceErrorNoError); - return; - - } else if(action.actionTypeId() == wallbeEcoChargeCurrentActionTypeId){ - - uint16_t current = action.param(wallbeEcoChargeCurrentEventChargeCurrentParamTypeId).value().toUInt(); - qCDebug(dcWallbe) << "Charging power set to" << current; - wallbe->setChargingCurrent(current); - device->setStateValue(wallbeEcoChargeCurrentStateTypeId, current); - info->finish(Device::DeviceErrorNoError); - return; - } - Q_ASSERT_X(false, "executeAction", QString("Unhandled action: %1").arg(action.actionTypeId().toString()).toUtf8()); - } - Q_ASSERT_X(false, "executeAction", QString("Unhandled deviceClassId: %1").arg(device->deviceClassId().toString()).toUtf8()); -} - - -void DevicePluginWallbe::deviceRemoved(Device *device) -{ - m_address.removeOne(QHostAddress(device->paramValue(wallbeEcoDeviceIpParamTypeId).toString())); - WallBe *wallbe = m_connections.take(device); - if (wallbe) { - qCDebug(dcWallbe) << "Remove device" << device->name(); - wallbe->deleteLater(); - } - - if (myDevices().isEmpty()) { - hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer); - m_pluginTimer = nullptr; - } -} - -void DevicePluginWallbe::update(Device *device) -{ - WallBe * wallbe = m_connections.value(device); - if(!wallbe->isAvailable()) - return; - - device->setStateValue(wallbeEcoConnectedStateTypeId, true); - - //EV state - 16 bit ASCII (8bit) - switch (wallbe->getEvStatus()) { - case 65: - device->setStateValue(wallbeEcoEvStatusStateTypeId, "A - No car plugged in"); - break; - case 66: - device->setStateValue(wallbeEcoEvStatusStateTypeId, "B - Supply equipment not yet ready"); - break; - case 67: - device->setStateValue(wallbeEcoEvStatusStateTypeId, "C - Ready to charge"); - break; - case 68: - device->setStateValue(wallbeEcoEvStatusStateTypeId, "D - Ready to charge, ventilation needed"); - break; - case 69: - device->setStateValue(wallbeEcoEvStatusStateTypeId, "E - Short circuit detected"); - break; - case 70: - device->setStateValue(wallbeEcoEvStatusStateTypeId, "F - Supply equipment not available"); - break; - default: - device->setStateValue(wallbeEcoEvStatusStateTypeId, "F - Supply equipment not available"); - } - - qCDebug(dcWallbe) << "EV State:" << device->stateValue(wallbeEcoEvStatusStateTypeId).toString(); - - // Extract Input Register 102 - load time - 32bit integer - device->setStateValue(wallbeEcoChargeTimeStateTypeId, wallbe->getChargingTime()); - - // Read the charge current state - device->setStateValue(wallbeEcoChargeCurrentStateTypeId, wallbe->getChargingCurrent()); - device->setStateValue(wallbeEcoPowerStateTypeId, wallbe->getChargingStatus()); -} diff --git a/wallbe/devicepluginwallbe.h b/wallbe/devicepluginwallbe.h deleted file mode 100644 index 051cd3a..0000000 --- a/wallbe/devicepluginwallbe.h +++ /dev/null @@ -1,64 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . -* -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef DEVICEPLUGINWALLBE_H -#define DEVICEPLUGINWALLBE_H - -#include "devices/deviceplugin.h" -#include "wallbe.h" -#include "host.h" -#include "discover.h" -#include "plugintimer.h" - -#include -#include - -class DevicePluginWallbe : public DevicePlugin -{ - Q_OBJECT - - Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginwallbe.json") - Q_INTERFACES(DevicePlugin) - -public: - explicit DevicePluginWallbe(); - - void discoverDevices(DeviceDiscoveryInfo *info) override; - void setupDevice(DeviceSetupInfo *info) override; - void postSetupDevice(Device *device) override; - void executeAction(DeviceActionInfo *info) override; - void deviceRemoved(Device *device) override; - -private: - QHash m_connections; - QList m_address; - PluginTimer *m_pluginTimer = nullptr; - - void update(Device *device); -}; - -#endif // DEVICEPLUGINWALLBE_H diff --git a/wallbe/discover.cpp b/wallbe/discover.cpp index 5af54e4..41d4034 100644 --- a/wallbe/discover.cpp +++ b/wallbe/discover.cpp @@ -1,29 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license * * GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. * -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . * -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq * -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include #include diff --git a/wallbe/discover.h b/wallbe/discover.h index 4c75916..6e07191 100644 --- a/wallbe/discover.h +++ b/wallbe/discover.h @@ -1,29 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license * * GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. * -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . * -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq * -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef DISCOVER_H #define DISCOVER_H diff --git a/wallbe/host.cpp b/wallbe/host.cpp index bf6c388..6eb0468 100644 --- a/wallbe/host.cpp +++ b/wallbe/host.cpp @@ -1,29 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license * * GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. * -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . * -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq * -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "host.h" diff --git a/wallbe/host.h b/wallbe/host.h index 71463c4..41e5ac0 100644 --- a/wallbe/host.h +++ b/wallbe/host.h @@ -1,29 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license * * GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. * -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . * -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq * -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef HOST_H #define HOST_H diff --git a/wallbe/integrationpluginwallbe.cpp b/wallbe/integrationpluginwallbe.cpp new file mode 100644 index 0000000..417a241 --- /dev/null +++ b/wallbe/integrationpluginwallbe.cpp @@ -0,0 +1,209 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "integrationpluginwallbe.h" +#include "plugininfo.h" + +#include "types/param.h" + +#include +#include +#include +#include + + +IntegrationPluginWallbe::IntegrationPluginWallbe() +{ +} + + +void IntegrationPluginWallbe::setupThing(ThingSetupInfo *info) +{ + Thing *thing = info->thing(); + qCDebug(dcWallbe) << "Setting up a new device:" << thing->params(); + + QHostAddress address(thing->paramValue(wallbeEcoThingIpParamTypeId).toString()); + + if (address.isNull()){ + qCWarning(dcWallbe) << "IP address is null"; + info->finish(Thing::ThingErrorSetupFailed, tr("IP address parameter not valid")); + return; + } + + WallBe *wallbe = new WallBe(address, 502, this); + m_connections.insert(thing, wallbe); + + info->finish(Thing::ThingErrorNoError); +} + + +void IntegrationPluginWallbe::discoverThings(ThingDiscoveryInfo *info) +{ + if (info->thingClassId() == wallbeEcoThingClassId){ + + Discover *discover = new Discover(QStringList("-xO" "-p 502")); + connect(discover, &Discover::devicesDiscovered, this, [info, this](QList hosts){ + foreach (Host host, hosts) { + ThingDescriptor descriptor; + foreach(Thing *thing, myThings().filterByParam(wallbeEcoThingMacParamTypeId, host.macAddress())) { + descriptor.setThingId(thing->id()); + break; + } + descriptor.setTitle(host.hostName()); + ParamList params; + params.append(Param(wallbeEcoThingIpParamTypeId, host.address())); + params.append(Param(wallbeEcoThingMacParamTypeId, host.macAddress())); + descriptor.setParams(params); + info->addThingDescriptor(descriptor); + } + info->finish(Thing::ThingErrorNoError); + }); + return; + } + Q_ASSERT_X(false, "discoverThings", QString("Unhandled thingClassId: %1").arg(info->thingClassId().toString()).toUtf8()); +} + + +void IntegrationPluginWallbe::postSetupThing(Thing *thing) +{ + if (!m_pluginTimer) { + m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(120); + connect(m_pluginTimer, &PluginTimer::timeout, this, [this] { + foreach(Thing *thing, m_connections.keys()) { + update(thing); + } + }); + } + + if (thing->thingClassId() == wallbeEcoThingClassId){ + update(thing); + } else { + Q_ASSERT_X(false, "postSetupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); + } +} + + +void IntegrationPluginWallbe::executeAction(ThingActionInfo *info) +{ + Thing *thing = info->thing(); + Action action = info->action(); + + WallBe *wallbe = m_connections.value(thing); + if (!wallbe) { + qCWarning(dcWallbe()) << "Wallbe object not available"; + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + if (thing->thingClassId() == wallbeEcoThingClassId){ + + // check if this is the "set power" action + if (action.actionTypeId() == wallbeEcoPowerActionTypeId) { + + // get the param value of the charging action + bool charging = action.param(wallbeEcoPowerActionPowerParamTypeId).value().toBool(); + qCDebug(dcWallbe) << "start Charging button" << thing->name() << "set power to" << charging; + wallbe->setChargingStatus(charging); + // Set the "power" state + thing->setStateValue(wallbeEcoPowerStateTypeId, charging); + info->finish(Thing::ThingErrorNoError); + return; + + } else if(action.actionTypeId() == wallbeEcoChargeCurrentActionTypeId){ + + uint16_t current = action.param(wallbeEcoChargeCurrentEventChargeCurrentParamTypeId).value().toUInt(); + qCDebug(dcWallbe) << "Charging power set to" << current; + wallbe->setChargingCurrent(current); + thing->setStateValue(wallbeEcoChargeCurrentStateTypeId, current); + info->finish(Thing::ThingErrorNoError); + return; + } + Q_ASSERT_X(false, "executeAction", QString("Unhandled action: %1").arg(action.actionTypeId().toString()).toUtf8()); + } + Q_ASSERT_X(false, "executeAction", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); +} + + +void IntegrationPluginWallbe::thingRemoved(Thing *thing) +{ + m_address.removeOne(QHostAddress(thing->paramValue(wallbeEcoThingIpParamTypeId).toString())); + WallBe *wallbe = m_connections.take(thing); + if (wallbe) { + qCDebug(dcWallbe) << "Remove device" << thing->name(); + wallbe->deleteLater(); + } + + if (myThings().isEmpty()) { + hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer); + m_pluginTimer = nullptr; + } +} + +void IntegrationPluginWallbe::update(Thing *thing) +{ + WallBe * wallbe = m_connections.value(thing); + if(!wallbe->isAvailable()) + return; + + thing->setStateValue(wallbeEcoConnectedStateTypeId, true); + + //EV state - 16 bit ASCII (8bit) + switch (wallbe->getEvStatus()) { + case 65: + thing->setStateValue(wallbeEcoEvStatusStateTypeId, "A - No car plugged in"); + break; + case 66: + thing->setStateValue(wallbeEcoEvStatusStateTypeId, "B - Supply equipment not yet ready"); + break; + case 67: + thing->setStateValue(wallbeEcoEvStatusStateTypeId, "C - Ready to charge"); + break; + case 68: + thing->setStateValue(wallbeEcoEvStatusStateTypeId, "D - Ready to charge, ventilation needed"); + break; + case 69: + thing->setStateValue(wallbeEcoEvStatusStateTypeId, "E - Short circuit detected"); + break; + case 70: + thing->setStateValue(wallbeEcoEvStatusStateTypeId, "F - Supply equipment not available"); + break; + default: + thing->setStateValue(wallbeEcoEvStatusStateTypeId, "F - Supply equipment not available"); + } + + qCDebug(dcWallbe) << "EV State:" << thing->stateValue(wallbeEcoEvStatusStateTypeId).toString(); + + // Extract Input Register 102 - load time - 32bit integer + thing->setStateValue(wallbeEcoChargeTimeStateTypeId, wallbe->getChargingTime()); + + // Read the charge current state + thing->setStateValue(wallbeEcoChargeCurrentStateTypeId, wallbe->getChargingCurrent()); + thing->setStateValue(wallbeEcoPowerStateTypeId, wallbe->getChargingStatus()); +} diff --git a/wallbe/integrationpluginwallbe.h b/wallbe/integrationpluginwallbe.h new file mode 100644 index 0000000..9ebc8c5 --- /dev/null +++ b/wallbe/integrationpluginwallbe.h @@ -0,0 +1,67 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef INTEGRATIONPLUGINWALLBE_H +#define INTEGRATIONPLUGINWALLBE_H + +#include "integrations/integrationplugin.h" +#include "wallbe.h" +#include "host.h" +#include "discover.h" +#include "plugintimer.h" + +#include +#include + +class IntegrationPluginWallbe : public IntegrationPlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginwallbe.json") + Q_INTERFACES(IntegrationPlugin) + +public: + explicit IntegrationPluginWallbe(); + + void discoverThings(ThingDiscoveryInfo *info) override; + void setupThing(ThingSetupInfo *info) override; + void postSetupThing(Thing *thing) override; + void executeAction(ThingActionInfo *info) override; + void thingRemoved(Thing *thing) override; + +private: + QHash m_connections; + QList m_address; + PluginTimer *m_pluginTimer = nullptr; + + void update(Thing *thing); +}; + +#endif // INTEGRATIONPLUGINWALLBE_H diff --git a/wallbe/devicepluginwallbe.json b/wallbe/integrationpluginwallbe.json similarity index 99% rename from wallbe/devicepluginwallbe.json rename to wallbe/integrationpluginwallbe.json index 0e6aca3..3c3335c 100644 --- a/wallbe/devicepluginwallbe.json +++ b/wallbe/integrationpluginwallbe.json @@ -7,7 +7,7 @@ "displayName": "Petring", "name": "petring", "id": "831b4b87-0a6c-4d51-b055-967bb6e5fab5", - "deviceClasses": [ + "thingClasses": [ { "id": "e66c84f6-b398-47e9-8aeb-33840e7b4492", "displayName": "Wallbe eco 2.0", diff --git a/wallbe/meta.json b/wallbe/meta.json new file mode 100644 index 0000000..cacab09 --- /dev/null +++ b/wallbe/meta.json @@ -0,0 +1,13 @@ +{ + "title": "Wall-Be", + "tagline": "Integrate Wall-Be EV-Charger into nymea-", + "icon": "walllbe.png", + "stability": "community", + "offline": true, + "technologies": [ + "network" + ], + "categories": [ + "tool" + ] +} diff --git a/wallbe/wallbe.h b/wallbe/wallbe.h index 3bb8d7f..5abf454 100644 --- a/wallbe/wallbe.h +++ b/wallbe/wallbe.h @@ -1,29 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. -* This project including source code and documentation is protected by copyright law, and -* remains the property of nymea GmbH. All rights, including reproduction, publication, -* editing and translation, are reserved. The use of this project is subject to the terms of a -* license agreement to be concluded with nymea GmbH in accordance with the terms -* of use of nymea GmbH, available under https://nymea.io/license +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license * * GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the terms of the GNU -* Lesser General Public License as published by the Free Software Foundation; version 3. -* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU Lesser General Public License for more details. +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. * -* You should have received a copy of the GNU Lesser General Public License along with this project. -* If not, see . +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . * -* For any further details and any questions please contact us under contact@nymea.io -* or see our FAQ/Licensing Information on https://nymea.io/license/faq +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq * -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef WALLBE_H #define WALLBE_H diff --git a/wallbe/wallbe.png b/wallbe/wallbe.png new file mode 100644 index 0000000000000000000000000000000000000000..79eaae8c6b6491579d0ddd5b50b25224967cdfed GIT binary patch literal 10942 zcmeHtRaYEMwDtrG?(PnO!5xA-L4!NNEigF2-JJk$&;Y^R-7SRR4#5T)+=A=Z`L55! z`2pu*x@Yz3RkgcnKeG3#XmwS23{+B75D0{!s37wZxc~h3Lq-CwReIIA!0nB*tE$$U z$pWuT5QxQFQASeB+vFtYOA`5_|3*)Z@2T(EBNE};Hfm%EdSvzY2p0$p-x?)HEWz~! ztQsjX7K)zbDKXnWShPJAl|H6xCX6PfD-uPxxQC z-;Pm<&hm)LpYfq7K+hV�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 literal 0 HcmV?d00001 diff --git a/wallbe/wallbe.pro b/wallbe/wallbe.pro index 7af11bf..a140a4b 100644 --- a/wallbe/wallbe.pro +++ b/wallbe/wallbe.pro @@ -1,19 +1,17 @@ include(../plugins.pri) -TARGET = $$qtLibraryTarget(nymea_devicepluginwallbe) - QT += \ network \ serialbus \ SOURCES += \ - devicepluginwallbe.cpp \ + integrationpluginwallbe.cpp \ wallbe.cpp \ host.cpp \ discover.cpp HEADERS += \ - devicepluginwallbe.h \ + integrationpluginwallbe.h \ wallbe.h \ host.h \ discover.h