From 6df5e712f7ab7e4275c7eca09e8494641d5485c2 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Tue, 28 Nov 2017 08:13:30 +0100 Subject: [PATCH] Tellduslive update with support for auto config and Local api (#10435) * Add support for local api connection found in TellStick Znet Lite/Pro. Added auto discovery support for all TelldusLive devices, changed authentication method. Breaking change! Upgraded tellduslive dependency Update CODEOWNERS. * Close any open configurator when configuration done * Add support for Telldus Local API via config (#2) * Updated dependency (addresses issue raised by @rasmusbe in https://github.com/home-assistant/home-assistant/pull/10435#issuecomment-344719714) * Fix requested changes --- CODEOWNERS | 2 + homeassistant/components/discovery.py | 2 + .../www_static/images/logo_tellduslive.png | Bin 0 -> 7796 bytes homeassistant/components/tellduslive.py | 202 ++++++++++++++---- requirements_all.txt | 2 +- 5 files changed, 169 insertions(+), 39 deletions(-) create mode 100644 homeassistant/components/frontend/www_static/images/logo_tellduslive.png diff --git a/CODEOWNERS b/CODEOWNERS index 069edd6cce2..6fa130432f4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -74,6 +74,8 @@ homeassistant/components/tahoma.py @philklei homeassistant/components/*/tahoma.py @philklei homeassistant/components/tesla.py @zabuldon homeassistant/components/*/tesla.py @zabuldon +homeassistant/components/tellduslive.py @molobrakos @fredrike +homeassistant/components/*/tellduslive.py @molobrakos @fredrike homeassistant/components/*/tradfri.py @ggravlingen homeassistant/components/*/xiaomi_aqara.py @danielhiversen @syssi homeassistant/components/*/xiaomi_miio.py @rytilahti @syssi diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index 6861c5bdc70..5d362f21cef 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -35,6 +35,7 @@ SERVICE_AXIS = 'axis' SERVICE_APPLE_TV = 'apple_tv' SERVICE_WINK = 'wink' SERVICE_XIAOMI_GW = 'xiaomi_gw' +SERVICE_TELLDUSLIVE = 'tellstick' SERVICE_HANDLERS = { SERVICE_HASS_IOS_APP: ('ios', None), @@ -46,6 +47,7 @@ SERVICE_HANDLERS = { SERVICE_APPLE_TV: ('apple_tv', None), SERVICE_WINK: ('wink', None), SERVICE_XIAOMI_GW: ('xiaomi_aqara', None), + SERVICE_TELLDUSLIVE: ('tellduslive', None), 'philips_hue': ('light', 'hue'), 'google_cast': ('media_player', 'cast'), 'panasonic_viera': ('media_player', 'panasonic_viera'), diff --git a/homeassistant/components/frontend/www_static/images/logo_tellduslive.png b/homeassistant/components/frontend/www_static/images/logo_tellduslive.png new file mode 100644 index 0000000000000000000000000000000000000000..7ea78f8ef3aad4d3cd982835c797693a68264c00 GIT binary patch literal 7796 zcmeAS@N?(olHy`uVBq!ia0y~yU^D|^4mJh`hSz)ctz=+eP)PO&@?~JCQe$9fXklRZ z#lXPO@PdJ%)PRBERRRNp)eHs(@q#(K0&N%=7&r?&B8wRqxP?KOkzv*x2?hoR_7YED zSM~>N>>Mf@Or9zC85kJYlDyqr7{K7C^X_^E2HC5gE{-7;x8BZWpAqu(>hbroVvN#Z zS0a*X-WaNFR8^A_?(K9--j}iF{`8C~cW2&g6%9Dhm2{Y?t4T_<_fulH&PGATqj@4a z5e%(!H_d&!cfaNPxvY-m@7|r+yZg=F&%bTYSDLlCAGllnzxdqmd7tgu7{Qhg2Erhs zvt_Np;>6?`F4w$2onM-`PoLpI;3CD#9c*W1pS+$Cabu2pVZKhvztDe`?KdKRZq3=} z&hS9darMq1qpcHm?&rAqSSe$z&?@VwMTwtTJDsmMT1$O;U2~WrVM2G*x=fd%UPYyZ z`UyV@m>qwY)o3wQ6uqfQ_Ap^DWNvWm>X<6XKRdcRAy()A?kjFruDN!meoudMGR)rfg~#*d z%w!gq1$EjV*SUmqE!w7c>*@DOEh&@l{p_2ce1Gzri($4-uyJXQ<{^`?MFzZ%w>4bu z+?%}q_>s@P!Rh%749spS{w{Bu6s@9v@8h^_tK`<}=bGCbF*$?N(5(B?do~7UrL0Mw zq8@Ili@vSSxDhic?d8qr5YHG%{@KcpW3(6?>XS_Gw5^doBO^G^!=B?-+eM|Bx0ydZ ze=B&JPvWA@zW)plmM!q~l;CFDvf`~)Q*ryglW)=kqs3#D7ICZct(Lbwl%>wdP~fw$ z!}F?yrtRTp=T+lG3t!481l~5VoBrml|HPbGm5c`zCoWi-+>(;@ylY3zR!)}O!y2N6 zy91KAk8NO3_3fq^4ldi9ip?2ba2INR zJ#}7Y(eKNAqJouyN0uZ{=(x4(LjC;oC-2J=9^N?8%EsUj^d%!iCxb`$p@sLO(~fEx z(&nGr&i?n1kv@4fF26&Xp{Bd*&ymU~pWjPb&t|KxV&A+XS3vOOjdQ||a>pdZ7;+{o z^?Yerwc=mZhK?gOg575Q9k+I^m^eeL{(G*5H={#5pTtI)`Lep7u+w8dQRFx2$#Q_wguP24VLp3256=}p@br*s$i zcRY=n=Jn?RV@-5b+nP(kx6AzBEY972-8owGY!}1{nyFmv&55DcHH{Nns~TJ zA&)`s+?6M+({2R0=-%hOohR?&vrk<7h|{rxS(S`?rY&XVO+FiOdRz7R75DbMTL&_n z|8ue>TXNzA z)39TQSqvmJrc5u)4e&a@p>oNQL%u1u-+o=u(RSAJQ>pf>N=7royV8r@PIy<&+cP0v zIiPQm=cBxn_x3GM_u($8*vG(T=FP3H$N5C{PGsD%m(3@(-G04r^WEuBnjam{nw2OL zFpG(Y=i|kos(CqEi)5C?A9BpN{kHvw$D@`>69hYy^BDfdR=x5$E1@>?*3IU-+itT5 zYRrrdNMiY$$MDzTLciB=@*kWjen2y*Oo~-8c}4217rD3d-n*Zui9Ygn#ni>V#!lbZ zCZsL<)|>l%s@=x7-<)e?d1t4KxAZO)Jhp)$NZa;w@87TCxwrrB;LqK?S8l4%^kYrW zt{EDcH{9!dHp{J~XQHU@jWT^_7q;At#pkMBZgbo;u_2~ zZEJ6z`Mqw+(&KA>tj-jB5-_W2zN_#2<*a9QpEUBHJS^|ZP$Qr1^5CDon3I$Gvzxd5uPvTcd9Yfff0~aj|Lwd*$A9=d5;=LR zEnrqtddHI)$F8R*-1fcUw$f$a5wQ+wo`08&Q{H};QCn2jXR|@OqLFiU#)KWdn|=ib z{xs0p+w+~BO>%zv>)HO^?EQ=E;zN+&C|ZkpIu z`P0q6bhh1fk@_evxFh*`i|^#6h8teG>ZY`Adu!(_ckbz)1ux59O5b;RRwu9Y>0w2l zP6ki+reH?1nJZX*Wp8b(=Gu1moXgsRM;f6;fBm1l?wk4V?}};sCs(^Z*p{d)#`A`? zQO`F2cF^`B8I8u=?aqPPdtW|ZRe99uYUKW~tu=qAMOB|)`6;{6=ik099yThfFXw&y z(;#?orueV_*DjkpvUHnPGizQ??)Jl%jLSQss;_IVTKl7TtK!?I@vApQ*R5Qla{5JD zT<6hGdt?LasvLG}eg5S5zjId0wznWn#(~}avI|$qajxu}m2_43thU(7Mc(D-zZjd# z#=qLyoqfnn?Df9`sgA}yrjy%W3zf)o=Iow3Z@KS{u&et@*1pPF7wY-)RY+-fo-m_~ z-v78`-O)R1X7o+hO7uQ=_ke8RWFZB+TM|nASKdEitd@UolD=r!<_&G-|5on06&;da zVs>>^*YER++1@UTJ8sKP+WJ=JtZ}bt{o}hWCHb~bF9*u0q>W03&@Zp<`=>CG@BX4K_2-vj!@#18jS(OX(k34nMJhyqz<+|S7?cC<` zD`&6ls{a?dHQX&^SIO2ZtGn3$i6`E+Ncyy}+R-G3r{AcJb=%VPq-(|8Kc$XsyIr|V zOy1Zh)ZWyw)GSPM`4%_(kfY&mT6g5i+~Rs|byvkNlIwn=@4Q5jboMPPGH<`Td3PQ6 z9t%IEj1`-1*DbbseQK(9N$JX4;mbVPW*q%q_V~itho4oxX`SMh5OmvkK*;pxlHXsL zvOoL~zfrbZJ7?5-t&t!>q&`W>ei2bs&gdy%tNUCO>9#&OJs_VF;xaw6WcW8L@(2~AHMxvmzKLreBFmdZ%^Eso+p+sJR)Ns6lyAuk5qL zvR7i~eKO~qGRw}Mx+eErWJ+#rw^QZ6;zfzcEi4uPY;;wYrk}XsQ@5f1;I`Xm9lzZT z`8)gjy8R+AvT_5iepWC4K10$>_2s=)rrXYJU9&dxC-NwGZ#IuQd`szirB_%KMYbAFtp)Z+_PF%WmE}o!jpw7`oJD z)W5MadFoj9*haUWJM^o|zE>-B-_E|!b6acYj+cR}qwCjNy*_nkrrQd=DLS77Hf_6- z>}rzo&q&CmcTr)+@dfYvqj#P8w8D!0cAoh8%W7|PWPg=!HTm)}V1HofzyGY?#5t4A zg1@f6|1#=Y*yr_{DLI)fd$jvqgeIGFoJi1feA>PxdkU-eJ4xrb;t5&5o>(rmvJMiG zid`obU)zv-ZQ1!(R$7bKaYSby{5_4KxN}00$jRSD0*}L+ck+lPUjA#<kw16K3i z=fwS6^x1B6|NM=4j@5dCyxtvmtg^H9V;6R<|H1e)ql@{okh?-o^UogN^Nr6WoEIy8 zPP4f>$9-$r(-#@VVM^Pstls+l-%+z{?b(G}rrx!jFP|GG`hC8y<*ZXr9dFdQy+7%C zV^2Y&fRVea@Hw_K%~m)6R$eSy^>Xp@x#vsjzHQQ)v%&ZL%qi#i^zR(ny3fp(*U|Ep zxp!fu`@AVP;;V(k4AY+S7GAFAelq#Rj=cC)ntJW?QY!R?WeRi`pV+!uHZ7O;8pEF0 zYs-vv=N4KnUHif~wB+TwIDPl8Pp|8AzTV9%9m89xt~a^-NsGsYJG&z8O+CN)Vo%R< zgX;#bj!RcHUwu1ozRHWsr{x2#`p3W7p>nnPlV|&r()D*#ckeQCzI8Qb`Kk2zFTNI; z=xvmKs>riqL+F#}LuKvdu4kuQDu@oX$ed!TcD1_so!_d|qeeEr9&V{IE44^3O8m~!*X9bOt{Z>Sq1h<%+ zt~+yzb-!8dvgv=+#1ff5^lxYtS}S_1blcuV35$JJJ=$knRr;i(|8|h6UiGD$Kf_;6 zKl#0>EIw?@ny5!LWm~kCb*;Z8p2+-R>xM}4{^(V&eol5dp;cY*P{NDY01w7nB z?OwZFy%{q1-_LOO)vM-I*L^Aq`S<6R`0`x_trs%tBO5m`R>a&WQ8Ou=vMRo2_geO- zztZK=m)XkHCR$}@|H|06`D&&2{1@AV%&WxC&)hHiaZO>}xo?NArsOc}6WR7oRjYTG z`|}qtDAeTU<~(^0 zX@wkH&Ixw>+4|8_KY0W_ZGQf(YGT0WGpc=Jn@sk6KL5mgqf*7x&zq{x8vHhDh_`wh zuzucV#hiu>U$@lm{rB}|Ca2TsF4t8b9=*-2z2cSkW96$Antt}Pwf>#SI`Q`7`3=it z`wDEVv);+E)_;qAoLaN*}$Pldre-=DGg)zV@@TRs}va?tlIB{Dr6Q zmISVwv-zq`wdNtV2kMEJBd2fU@p}E>Z?k)dO})#f=5oRASL(-V)4fBrLhNe;g)Vwl z95mf$fBvV#AL;V)^~^3e82%JiF7VzJUANF`)#FTEcg>yu)W2=JXD*kw_IB}>NUfQ> z*ZuitdOrT;sdIt1ml|`0Zf4gP5HNGNQFZ#(srNtjzRFqWyEpdHzRPcQ_b!*8x=*@&%I?tq zXR9LoHZ@jgmWJvLm^`Rz5rf@Tc&IitU?Sz&kC>+bcORc}R_riNM;Twh!Jvu%lY z+?@^F@3Zm)MfvA6Z(#i4cjMap_PZ0^ZXaKK^0wWU1iu?$Ti4X+{+)esyS#^8q}l58HBYHw%6)F3|Kd9LS6YqX+$;=?n6+P=;+BRkXyG@bbU+Mles|@&jCPmzB zm6}ly>x1w_@8>!`zjvta54^H@(czH8)y1-By6oo1SUml|x;AyzXT=}xPCX%vKZ0%) zF^93&buF`B_3LEew{H)Gw*~5Ii&+^0MT}ZC)*}e{VBiRCQ<7=8Z;wjXpk{^JKTH`nwfTi{-7hv&}D) zIK=kAI`OpZBsa-bzH0KXOSfk{-ScKkx!jGk7Nwk{!QZx}dCt`>?o@q0+4%;8jaE*S z!aBPHr}hP%TP7!+JpZM|-Jnp}@T1|X^Q|=ISFYJ=c<<(~y-T;f)#8wD5O4K8vi#f8 z>?yBadDSLJ}vbN0K~*=0Mf z-ko10`77~#YQ{@n`_1PzT$1ZMmfLpkA5RPK0q;X;Ta$XWuxNc1 z>r@LZ{2%!(b;X~kuff7W=U9GUiBG%R@oML_=`!52=XHH}aFp>78%MNJ;$`;VRexhn zsV;>xwWnKLpAS@2y*aj`BsnjBc1-EbD9ijUdj(!( zG7Ijzc4N~QZ&dO`>O5-ImM`+- z{xxas$=Ds&s#{n#eV=<&^dIHWl9S&j@$H))Q#|4Eu{%fXE@tnUwH93Yoa}yk>2B!Z z+uzmq`dskanYvvzyI$_9?XxSJUX`||zUX-pcK7wK>sGg9<77-$c2)a-fAv!e^ z?YC{*Bw>5%%iC++Qn|-&MDL$|?%K0|wrgi!`&u6oyfswryN<;zw#=5SKMw+r-<)N7 zwczVNPzfcrt@L?#a=W*i$QGk5UsYc`=T;HEz4H6!t=?s!Yc;J}YL?&m`{@3(18Ta? z%jFi_(pK(kJ^QQpSij2+h8q2xyN?zgU-RYR-hGU@`q3*-y|-D(uNtlJFzt21k&{`f z8;xsY(pldX^qjJaul~ulK2Ay@r(uI-=b_HULThC!oqrq5DvdbxTWDW#X26e5t$lxf zdu}$(zLoH^yU%s)>ZOh?zeV)tuLlK;<*9pB+E>?XtyR|LR!+~lyfkax{zG9lr_*E> zTS;rlaz{tMoWAPCCfhwRZAzd@Jf9`x>f@i8`Yv(#QIWe2{WGvP_r9t-Yvr8<5AU*v z)|@f$3S?+m`#fjun``mb_GjL|+M>q!Mvb>?{XPBC^E>2b_e6(2b$(vCm&f%6L*nc0 z_qLzhZSgKLdb{d<%D8lH+s&w}kCR;) z=5DX3sJJS3h>hd+0bi}0)vu=CN$pi%YMOc>V(qnif0xHy|Gw4Du0S>V@>`Xvk~8e7 z8lcGE@2AysJ1W%PII`3%WJ%uXzrS*_FCAQ`v!?EqQn*yn+GTfG%~j_+y53+A*w$Nh z_fy-duHWZ2EED-P$M)#7m5(%k-FoS$$n7gnMDG=Zxv9lGl1)zW4dj_x;vo&i7jd4zV?CSh{QRms5K~9{>J4 z>G#Ic(iOYv@8{fCSiPKQnfzL-tgaYA)6 zUE4)VjdiwX{CwrRUeDF-21BBB$aJpcw&;5~veG`PFE*}>o4)+Tx4!n~KKCtVWjvg} zeXoFx@l|PAlJjiis=}MWt16EkaH_h%h!ef#qR*HNj8b~OJbemq$ihcUOzds5md8X(|;WKOU&e| z?DD-kD_(52n%@P=Y_}JbbMDOHzq%=DW6|VkQ@eH96Iyr~5~Y6>>=e9y>e{T=S8i4R zf3$s{X$!A_+4(PfV)yLLD%^4N)$JE-iSvCWL0O=3)&1N**H&fz)mYay)QZT zr)P6WE95lqy}vf+*u}8@Go|0knx2o%yC2rHfl(ocdFsxoK7SWx{rXbr{_Xx*ZSmXv z*@`&~9Jdd=5BM=}RsH9ATaK-?dUYz~r|kaoEkdAlcD6b_PdW7L=kQhMmc0`3y1g&YPB=|+&(Sq`yrNIQapO%Tk42{vpH1cew&;|9haWZV$KbuTrmf%9-@^)8e|Nn-bV9|cHYG{=Y_W2u4L@2>xwW*d8c-eo1NL&#Qlh_ ze!_{v=B?rrUi5dgo#j_fEN&zNX~YaA}cmDdv# zT6Hs0^CCZ;wDOZXR21iSXlce|{xj)|9E;02<4kU_JW@+_(#gqDGSJ}_cRB? foF>Q(4UEtCYX|er3=uzM3gUaZ`njxgN@xNA&f%&S literal 0 HcmV?d00001 diff --git a/homeassistant/components/tellduslive.py b/homeassistant/components/tellduslive.py index a0e1efbd75c..fa8916aca11 100644 --- a/homeassistant/components/tellduslive.py +++ b/homeassistant/components/tellduslive.py @@ -8,35 +8,41 @@ from datetime import datetime, timedelta import logging from homeassistant.const import ( - ATTR_BATTERY_LEVEL, DEVICE_DEFAULT_NAME, EVENT_HOMEASSISTANT_START) + ATTR_BATTERY_LEVEL, DEVICE_DEFAULT_NAME, + CONF_TOKEN, CONF_HOST, + EVENT_HOMEASSISTANT_START) from homeassistant.helpers import discovery +from homeassistant.components.discovery import SERVICE_TELLDUSLIVE import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import track_point_in_utc_time from homeassistant.util.dt import utcnow +from homeassistant.util.json import load_json, save_json import voluptuous as vol +APPLICATION_NAME = 'Home Assistant' + DOMAIN = 'tellduslive' -REQUIREMENTS = ['tellduslive==0.3.4'] +REQUIREMENTS = ['tellduslive==0.10.3'] _LOGGER = logging.getLogger(__name__) -CONF_PUBLIC_KEY = 'public_key' -CONF_PRIVATE_KEY = 'private_key' -CONF_TOKEN = 'token' +TELLLDUS_CONFIG_FILE = 'tellduslive.conf' +KEY_CONFIG = 'tellduslive_config' + CONF_TOKEN_SECRET = 'token_secret' CONF_UPDATE_INTERVAL = 'update_interval' +PUBLIC_KEY = 'THUPUNECH5YEQA3RE6UYUPRUZ2DUGUGA' +NOT_SO_PRIVATE_KEY = 'PHES7U2RADREWAFEBUSTUBAWRASWUTUS' + MIN_UPDATE_INTERVAL = timedelta(seconds=5) DEFAULT_UPDATE_INTERVAL = timedelta(minutes=1) CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Required(CONF_PUBLIC_KEY): cv.string, - vol.Required(CONF_PRIVATE_KEY): cv.string, - vol.Required(CONF_TOKEN): cv.string, - vol.Required(CONF_TOKEN_SECRET): cv.string, + vol.Optional(CONF_HOST): cv.string, vol.Optional(CONF_UPDATE_INTERVAL, default=DEFAULT_UPDATE_INTERVAL): ( vol.All(cv.time_period, vol.Clamp(min=MIN_UPDATE_INTERVAL))) }), @@ -45,21 +51,156 @@ CONFIG_SCHEMA = vol.Schema({ ATTR_LAST_UPDATED = 'time_last_updated' +CONFIG_INSTRUCTIONS = """ +To link your TelldusLive account: -def setup(hass, config): +1. Click the link below + +2. Login to Telldus Live + +3. Authorize {app_name}. + +4. Click the Confirm button. + +[Link TelldusLive account]({auth_url}) +""" + + +def setup(hass, config, session=None): """Set up the Telldus Live component.""" - client = TelldusLiveClient(hass, config) + from tellduslive import Session, supports_local_api + config_filename = hass.config.path(TELLLDUS_CONFIG_FILE) + conf = load_json(config_filename) - if not client.validate_session(): + def request_configuration(host=None): + """Request TelldusLive authorization.""" + configurator = hass.components.configurator + hass.data.setdefault(KEY_CONFIG, {}) + data_key = host or DOMAIN + + # Configuration already in progress + if hass.data[KEY_CONFIG].get(data_key): + return + + _LOGGER.info('Configuring TelldusLive %s', + 'local client: {}'.format(host) if host else + 'cloud service') + + session = Session(public_key=PUBLIC_KEY, + private_key=NOT_SO_PRIVATE_KEY, + host=host, + application=APPLICATION_NAME) + + auth_url = session.authorize_url + if not auth_url: + _LOGGER.warning('Failed to retrieve authorization URL') + return + + _LOGGER.debug('Got authorization URL %s', auth_url) + + def configuration_callback(callback_data): + """Handle the submitted configuration.""" + session.authorize() + res = setup(hass, config, session) + if not res: + configurator.notify_errors( + hass.data[KEY_CONFIG].get(data_key), + 'Unable to connect.') + return + + conf.update( + {host: {CONF_HOST: host, + CONF_TOKEN: session.access_token}} if host else + {DOMAIN: {CONF_TOKEN: session.access_token, + CONF_TOKEN_SECRET: session.access_token_secret}}) + save_json(config_filename, conf) + # Close all open configurators: for now, we only support one + # tellstick device, and configuration via either cloud service + # or via local API, not both at the same time + for instance in hass.data[KEY_CONFIG].values(): + configurator.request_done(instance) + + hass.data[KEY_CONFIG][data_key] = \ + configurator.request_config( + 'TelldusLive ({})'.format( + 'LocalAPI' if host + else 'Cloud service'), + configuration_callback, + description=CONFIG_INSTRUCTIONS.format( + app_name=APPLICATION_NAME, + auth_url=auth_url), + submit_caption='Confirm', + entity_picture='/static/images/logo_tellduslive.png', + ) + + def tellstick_discovered(service, info): + """Run when a Tellstick is discovered.""" + _LOGGER.info('Discovered tellstick device') + + if DOMAIN in hass.data: + _LOGGER.debug('Tellstick already configured') + return + + host, device = info[:2] + + if not supports_local_api(device): + _LOGGER.debug('Tellstick does not support local API') + # Configure the cloud service + hass.async_add_job(request_configuration) + return + + _LOGGER.debug('Tellstick does support local API') + + # Ignore any known devices + if conf and host in conf: + _LOGGER.debug('Discovered already known device: %s', host) + return + + # Offer configuration of both live and local API + request_configuration() + request_configuration(host) + + discovery.listen(hass, SERVICE_TELLDUSLIVE, tellstick_discovered) + + if session: + _LOGGER.debug('Continuing setup configured by configurator') + elif conf and CONF_HOST in next(iter(conf.values())): + # For now, only one local device is supported + _LOGGER.debug('Using Local API pre-configured by configurator') + session = Session(**next(iter(conf.values()))) + elif DOMAIN in conf: + _LOGGER.debug('Using TelldusLive cloud service ' + 'pre-configured by configurator') + session = Session(PUBLIC_KEY, NOT_SO_PRIVATE_KEY, + application=APPLICATION_NAME, **conf[DOMAIN]) + elif config.get(DOMAIN): + _LOGGER.info('Found entry in configuration.yaml. ' + 'Requesting TelldusLive cloud service configuration') + request_configuration() + + if CONF_HOST in config.get(DOMAIN, {}): + _LOGGER.info('Found TelldusLive host entry in configuration.yaml. ' + 'Requesting Telldus Local API configuration') + request_configuration(config.get(DOMAIN).get(CONF_HOST)) + + return True + else: + _LOGGER.info('Tellstick discovered, awaiting discovery callback') + return True + + if not session.is_authorized: _LOGGER.error( - "Authentication Error: Please make sure you have configured your " - "keys that can be acquired from " - "https://api.telldus.com/keys/index") + 'Authentication Error') return False + client = TelldusLiveClient(hass, config, session) + hass.data[DOMAIN] = client - hass.bus.listen(EVENT_HOMEASSISTANT_START, client.update) + if session: + client.update() + else: + hass.bus.listen(EVENT_HOMEASSISTANT_START, client.update) return True @@ -67,36 +208,21 @@ def setup(hass, config): class TelldusLiveClient(object): """Get the latest data and update the states.""" - def __init__(self, hass, config): + def __init__(self, hass, config, session): """Initialize the Tellus data object.""" - from tellduslive import Client - - public_key = config[DOMAIN].get(CONF_PUBLIC_KEY) - private_key = config[DOMAIN].get(CONF_PRIVATE_KEY) - token = config[DOMAIN].get(CONF_TOKEN) - token_secret = config[DOMAIN].get(CONF_TOKEN_SECRET) - self.entities = [] self._hass = hass self._config = config - self._interval = config[DOMAIN].get(CONF_UPDATE_INTERVAL) + self._interval = config.get(DOMAIN, {}).get( + CONF_UPDATE_INTERVAL, DEFAULT_UPDATE_INTERVAL) _LOGGER.debug('Update interval %s', self._interval) - - self._client = Client(public_key, - private_key, - token, - token_secret) - - def validate_session(self): - """Make a request to see if the session is valid.""" - response = self._client.request_user() - return response and 'email' in response + self._client = session def update(self, *args): """Periodically poll the servers for current state.""" - _LOGGER.debug("Updating") + _LOGGER.debug('Updating') try: self._sync() finally: @@ -106,7 +232,7 @@ class TelldusLiveClient(object): def _sync(self): """Update local list of devices.""" if not self._client.update(): - _LOGGER.warning("Failed request") + _LOGGER.warning('Failed request') def identify_device(device): """Find out what type of HA component to create.""" @@ -161,7 +287,7 @@ class TelldusLiveEntity(Entity): self._client = hass.data[DOMAIN] self._client.entities.append(self) self._name = self.device.name - _LOGGER.debug("Created device %s", self) + _LOGGER.debug('Created device %s', self) def changed(self): """Return the property of the device might have changed.""" diff --git a/requirements_all.txt b/requirements_all.txt index dcb48ea597e..76f84ea9ebe 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1064,7 +1064,7 @@ tellcore-net==0.1 tellcore-py==1.1.2 # homeassistant.components.tellduslive -tellduslive==0.3.4 +tellduslive==0.10.3 # homeassistant.components.sensor.temper temperusb==1.5.3