26_tahoma.pm 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680
  1. # $Id: 26_tahoma.pm 15245 2017-10-13 18:26:18Z mike3436 $
  2. ################################################################
  3. #
  4. # Copyright notice
  5. #
  6. # (c) 2015 mike3436 (mike3436@online.de)
  7. #
  8. # This script is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 2 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # The GNU General Public License can be found at
  14. # http://www.gnu.org/copyleft/gpl.html.
  15. # A copy is found in the textfile GPL.txt and important notices to the license
  16. # from the author is found in LICENSE.txt distributed with these scripts.
  17. #
  18. # This script is distributed in the hope that it will be useful,
  19. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. # GNU General Public License for more details.
  22. #
  23. # This copyright notice MUST APPEAR in all copies of the script!
  24. #
  25. ################################################################
  26. # $Id: 26_tahoma.pm
  27. #
  28. # 2014-08-01 V 0100 first Version using XML Interface
  29. # 2015-08-16 V 0200 communication to server changes from xml to json
  30. # 2015-08-16 V 0201 some standard requests after login which are not neccessary disabled (so the actual requests are not equal to flow of iphone app)
  31. # 2016-02-14 V 0202 bugs forcing some startup warning messages fixed
  32. # 2016-02-20 V 0203 perl exception while parsing json string captured
  33. # 2016-02-24 V 0204 commands open,close,my,stop and setClosure added
  34. # 2016-04-24 V 0205 commands taken from setup
  35. # 2016-06-16 V 0206 updateDevices called for devices created before setup has been read
  36. # 2016-11-15 V 0207 BLOCKING=0 can be used, all calls asynchron, attribut levelInvert inverts RollerShutter position
  37. # 2016-11-29 V 0208 HttpUtils used instead of LWP::UserAgent, BLOCKING=0 set as default, umlaut can be used in Tahoma names
  38. # 2016-12-15 V 0209 perl warnings during startup and login eliminated
  39. # 2017-01-08 V 0210 tahoma_cancelExecutions: cancel command added
  40. # 2017-01-10 V 0211 tahoma_getStates: read all states based on table {setup}{devices}[n]{definition}{states}
  41. # 2017-01-24 V 0212 tahoma_getStates: read all states recovered
  42. # 2017-01-24 V 0212 start scene with launchActionGroup so cancel is working on scenes now
  43. # 2017-01-24 V 0212 Attribut interval used to disable or enable refreshAllstates
  44. # 2017-01-24 V 0212 Setup changes recognized for reading places
  45. # 2017-03-23 V 0213 username and password stored encrypted
  46. # 2017-05-07 V 0214 encryption can be disabled by new attribut cryptLoginData
  47. # 2017-05-07 V 0214 correct parameters of setClosureAndLinearSpeed caused syntax error
  48. # 2017-07-01 V 0215 creation of fid and device names for first autocreate extended
  49. # 2017-07-08 V 0215 login delay increased automatically up to 160s if login failed
  50. # 2017-07-08 V 0215 default set commands on devices without commands deleted
  51. # 2017-10-08 V 0216 group definition added
  52. package main;
  53. use strict;
  54. use warnings;
  55. use utf8;
  56. use Encode qw(decode_utf8);
  57. use JSON;
  58. #use Data::Dumper;
  59. use Time::HiRes qw(time);
  60. use HttpUtils;
  61. sub tahoma_parseGetSetupPlaces($$);
  62. sub tahoma_UserAgent_NonblockingGet($);
  63. sub tahoma_encode_utf8($);
  64. my $hash_;
  65. sub tahoma_Initialize($)
  66. {
  67. my ($hash) = @_;
  68. $hash->{DefFn} = "tahoma_Define";
  69. $hash->{NOTIFYDEV} = "global";
  70. $hash->{NotifyFn} = "tahoma_Notify";
  71. $hash->{UndefFn} = "tahoma_Undefine";
  72. $hash->{SetFn} = "tahoma_Set";
  73. $hash->{GetFn} = "tahoma_Get";
  74. $hash->{AttrFn} = "tahoma_Attr";
  75. $hash->{AttrList} = "IODev ".
  76. "blocking ".
  77. "debug:1 ".
  78. "disable:1 ".
  79. "interval ".
  80. "logfile ".
  81. "url ".
  82. "placeClasses ".
  83. "levelInvert ".
  84. "cryptLoginData ".
  85. "userAgent ";
  86. $hash->{AttrList} .= $readingFnAttributes;
  87. }
  88. #####################################
  89. sub tahoma_fhemIdFromDevice($)
  90. {
  91. my @device = split "/", shift;
  92. $device[-1] =~ s/\W/_/g;
  93. return $device[-1] if (@device <= 4);
  94. $device[-2] =~ s/\W/_/g;
  95. return $device[-2].'_'.$device[-1] if (@device <= 5);;
  96. $device[-3] =~ s/\W/_/g;
  97. return $device[-3].'_'.$device[-2].'_'.$device[-1];
  98. }
  99. sub tahoma_fhemIdFromOid($)
  100. {
  101. my @oid = split "-", shift;
  102. $oid[0] =~ s/\W/_/g;
  103. return $oid[0];
  104. }
  105. my $groupId = 123001;
  106. sub tahoma_Define($$)
  107. {
  108. my ($hash, $def) = @_;
  109. my @a = split("[ \t][ \t]*", $def);
  110. my $ModuleVersion = "0216";
  111. my $subtype;
  112. my $name = $a[0];
  113. if( $a[2] eq "DEVICE" && @a == 4 ) {
  114. $subtype = "DEVICE";
  115. my $device = $a[3];
  116. my $fid = tahoma_fhemIdFromDevice($device);
  117. $hash->{device} = $device;
  118. $hash->{fid} = $fid;
  119. $hash->{INTERVAL} = 0;
  120. my $d = $modules{$hash->{TYPE}}{defptr}{"$fid"};
  121. return "device $device already defined as $d->{NAME}" if( defined($d) && $d->{NAME} ne $name );
  122. $modules{$hash->{TYPE}}{defptr}{"$fid"} = $hash;
  123. } elsif( $a[2] eq "PLACE" && @a == 4 ) {
  124. $subtype = "PLACE";
  125. my $oid = $a[@a-1];
  126. my $fid = tahoma_fhemIdFromOid($oid);
  127. $hash->{oid} = $oid;
  128. $hash->{fid} = $fid;
  129. $hash->{INTERVAL} = 0;
  130. my $d = $modules{$hash->{TYPE}}{defptr}{"$fid"};
  131. return "place oid $oid already defined as $d->{NAME}" if( defined($d) && $d->{NAME} ne $name );
  132. $modules{$hash->{TYPE}}{defptr}{"$fid"} = $hash;
  133. } elsif( $a[2] eq "GROUP" && @a == 4 ) {
  134. $subtype = "GROUP";
  135. my $oid = $a[@a-1];
  136. my $fid = 'group' . "$groupId";
  137. $groupId++;
  138. $hash->{oid} = $oid;
  139. $hash->{fid} = $fid;
  140. $hash->{INTERVAL} = 0;
  141. my $d = $modules{$hash->{TYPE}}{defptr}{"$fid"};
  142. return "group oid $oid already defined as $d->{NAME}" if( defined($d) && $d->{NAME} ne $name );
  143. $modules{$hash->{TYPE}}{defptr}{"$fid"} = $hash;
  144. } elsif( $a[2] eq "SCENE" && @a == 4 ) {
  145. $subtype = "SCENE";
  146. my $oid = $a[@a-1];
  147. my $fid = tahoma_fhemIdFromOid($oid);
  148. $hash->{oid} = $oid;
  149. $hash->{fid} = $fid;
  150. $hash->{INTERVAL} = 0;
  151. my $d = $modules{$hash->{TYPE}}{defptr}{"$fid"};
  152. return "scene oid $oid already defined as $d->{NAME}" if( defined($d) && $d->{NAME} ne $name );
  153. $modules{$hash->{TYPE}}{defptr}{"$fid"} = $hash;
  154. } elsif( $a[2] eq "ACCOUNT" && @a == 5 ) {
  155. $subtype = "ACCOUNT";
  156. my $username = $a[@a-2];
  157. my $password = $a[@a-1];
  158. $hash->{Clients} = ":tahoma:";
  159. $hash->{helper}{username} = $username;
  160. $hash->{helper}{password} = $password;
  161. $hash->{BLOCKING} = 0;
  162. $hash->{INTERVAL} = 0;
  163. $hash->{VERSION} = $ModuleVersion;
  164. } else {
  165. return "Usage: define <name> tahoma device\
  166. define <name> tahoma ACCOUNT username password\
  167. define <name> tahoma DEVICE id\
  168. define <name> tahoma SCENE oid username password\
  169. define <name> tahoma PLACE oid" if(@a < 4 || @a > 5);
  170. }
  171. $hash->{NAME} = $name;
  172. $hash->{SUBTYPE} = $subtype;
  173. $hash->{STATE} = "Initialized";
  174. if( $init_done ) {
  175. tahoma_connect($hash) if( $hash->{SUBTYPE} eq "ACCOUNT" );
  176. tahoma_initDevice($hash) if( $hash->{SUBTYPE} eq "DEVICE" );
  177. tahoma_initDevice($hash) if( $hash->{SUBTYPE} eq "PLACE" );
  178. tahoma_initDevice($hash) if( $hash->{SUBTYPE} eq "GROUP" );
  179. tahoma_initDevice($hash) if( $hash->{SUBTYPE} eq "SCENE" );
  180. }
  181. return undef;
  182. }
  183. sub tahoma_Notify($$)
  184. {
  185. my ($hash,$dev) = @_;
  186. return if($dev->{NAME} ne "global");
  187. return if(!grep(m/^INITIALIZED|REREADCFG$/, @{$dev->{CHANGED}}));
  188. if( $hash->{SUBTYPE} eq "ACCOUNT" )
  189. {
  190. my $name = $hash->{NAME};
  191. my $username = $hash->{helper}{username};
  192. my $password = $hash->{helper}{password};
  193. if ((defined $attr{$name}{cryptLoginData}) && (not $attr{$name}{cryptLoginData}))
  194. {
  195. $username = tahoma_decrypt($username);
  196. $password = tahoma_decrypt($password);
  197. }
  198. else
  199. {
  200. $username = tahoma_encrypt($username);
  201. $password = tahoma_encrypt($password);
  202. }
  203. $hash->{DEF} = "$hash->{SUBTYPE} $username $password";
  204. }
  205. tahoma_connect($hash) if( $hash->{SUBTYPE} eq "ACCOUNT" );
  206. tahoma_initDevice($hash) if( $hash->{SUBTYPE} eq "DEVICE" );
  207. tahoma_initDevice($hash) if( $hash->{SUBTYPE} eq "PLACE" );
  208. tahoma_initDevice($hash) if( $hash->{SUBTYPE} eq "GROUP" );
  209. tahoma_initDevice($hash) if( $hash->{SUBTYPE} eq "SCENE" );
  210. }
  211. sub tahoma_Undefine($$)
  212. {
  213. my ($hash, $arg) = @_;
  214. delete( $modules{$hash->{TYPE}}{defptr}{"$hash->{fid}"} ) if( $hash->{SUBTYPE} eq "DEVICE" );
  215. delete( $modules{$hash->{TYPE}}{defptr}{"$hash->{fid}"} ) if( $hash->{SUBTYPE} eq "PLACE" );
  216. delete( $modules{$hash->{TYPE}}{defptr}{"$hash->{fid}"} ) if( $hash->{SUBTYPE} eq "GROUP" );
  217. delete( $modules{$hash->{TYPE}}{defptr}{"$hash->{fid}"} ) if( $hash->{SUBTYPE} eq "SCENE" );
  218. return undef;
  219. }
  220. sub tahoma_login($)
  221. {
  222. my ($hash) = @_;
  223. my $name = $hash->{NAME};
  224. Log3 $name, 3, "$name: tahoma_login";
  225. $hash->{logged_in} = undef;
  226. $hash->{startup_run} = undef;
  227. $hash->{startup_done} = undef;
  228. $hash->{url} = "https://www.tahomalink.com/enduser-mobile-web/externalAPI/json/";
  229. $hash->{url} = $attr{$name}{url} if (defined $attr{$name}{url});
  230. $hash->{userAgent} = "TaHoma/7980 CFNetwork/758.5.3 Darwin/15.6.0";
  231. $hash->{userAgent} = $attr{$name}{userAgent} if (defined $attr{$name}{userAgent});
  232. $hash->{timeout} = 10;
  233. $hash->{HTTPCookies} = undef;
  234. $hash->{loginRetryTimer} = 5 if (!defined $hash->{loginRetryTimer});
  235. $hash->{loginRetryTimer} *= 2 if ($hash->{loginRetryTimer} < 160);
  236. Log3 $name, 2, "$name: login start";
  237. tahoma_UserAgent_NonblockingGet({
  238. timeout => 10,
  239. noshutdown => 1,
  240. hash => $hash,
  241. page => 'login',
  242. data => {'userId' => tahoma_decrypt($hash->{helper}{username}) , 'userPassword' => tahoma_decrypt($hash->{helper}{password})},
  243. callback => \&tahoma_dispatch,
  244. nonblocking => 1,
  245. });
  246. }
  247. my @startup_pages = ( 'getEndUser',
  248. 'getSetup',
  249. 'getActionGroups',
  250. #'/../../enduserAPI/setup/interactiveNotifications',
  251. #'/../../enduserAPI/setup/interactiveNotifications/history',
  252. #'getCalendarDayList',
  253. #'getCalendarRuleList',
  254. #'/../../enduserAPI/conditionGroups',
  255. #'getScheduledExecutions',
  256. #'getHistory',
  257. #'getSetupTriggers',
  258. #'getUserPreferences',
  259. #'getSetupOptions',
  260. #'getAvailableProtocolsType',
  261. #'getActiveProtocolsType',
  262. #'getSetupQuota?quotaId=smsCredit',
  263. #'getSetupDawnAndDuskTimes',
  264. '../../enduserAPI/setup/gateways',
  265. #'../../enduserAPI/setup/gateways',
  266. #'../../enduserAPI/setup/subscribe/notification/apple/com.somfy.iPhoneTaHoma',
  267. #'../../enduserAPI/setup/subscribe/notification/devices/tahoma',
  268. #'/../../enduserAPI/setup/subscribe/notification/apple/com.somfy.iPhoneTaHoma',
  269. #'../../enduserAPI/setup/subscribe/notification/devices/tahoma',
  270. 'getCurrentExecutions',
  271. 'refreshAllStates' );
  272. sub tahoma_startup($)
  273. {
  274. my ($hash) = @_;
  275. my $name = $hash->{NAME};
  276. Log3 $name, 4, "$name: tahoma_startup";
  277. return if (!$hash->{logged_in});
  278. return if ($hash->{startup_done});
  279. if (!defined($hash->{startup_run}))
  280. {
  281. $hash->{startup_run} = 0;
  282. }
  283. else
  284. {
  285. $hash->{startup_run}++;
  286. if ($hash->{startup_run} >= scalar @startup_pages)
  287. {
  288. $hash->{startup_done} = 1;
  289. return;
  290. }
  291. }
  292. my $page = $startup_pages[$hash->{startup_run}];
  293. my $subpage = "";
  294. $subpage = '?gatewayId='.$hash->{gatewayId} if (substr($page, -13) eq 'ProtocolsType');
  295. $subpage = '?quotaId=smsCredit' if ($page eq 'getSetupQuota');
  296. $subpage = '/'.$hash->{gatewayId}.'/version' if (substr($page, -8) eq 'gateways');
  297. tahoma_UserAgent_NonblockingGet({
  298. timeout => 10,
  299. noshutdown => 1,
  300. hash => $hash,
  301. page => $page,
  302. subpage => $subpage,
  303. callback => \&tahoma_dispatch,
  304. nonblocking => 1,
  305. });
  306. }
  307. sub tahoma_refreshAllStates($)
  308. {
  309. my ($hash) = @_;
  310. my $name = $hash->{NAME};
  311. Log3 $name, 4, "$name: tahoma_refreshAllStates";
  312. tahoma_UserAgent_NonblockingGet({
  313. timeout => 10,
  314. noshutdown => 1,
  315. hash => $hash,
  316. page => 'refreshAllStates',
  317. callback => \&tahoma_dispatch,
  318. nonblocking => 1,
  319. });
  320. }
  321. sub tahoma_getEvents($)
  322. {
  323. my ($hash) = @_;
  324. my $name = $hash->{NAME};
  325. Log3 $name, 4, "$name: tahoma_getEvents";
  326. return if(!$hash->{logged_in} && !$hash->{startup_done});
  327. tahoma_UserAgent_NonblockingGet({
  328. timeout => 10,
  329. noshutdown => 1,
  330. hash => $hash,
  331. page => 'getEvents',
  332. callback => \&tahoma_dispatch,
  333. nonblocking => 1,
  334. });
  335. }
  336. sub tahoma_readStatusTimer($)
  337. {
  338. my $timestart = time;
  339. my $timeinfo = "tahoma_readStatusTimer";
  340. my ($hash) = @_;
  341. my $name = $hash->{NAME};
  342. RemoveInternalTimer($hash);
  343. my ($seconds) = gettimeofday();
  344. $hash->{refreshStateTimer} = $seconds + 10 if ( (!defined($hash->{refreshStateTimer})) || (!$hash->{logged_in}) );
  345. if( $hash->{request_active} ) {
  346. Log3 $name, 3, "$name: request active";
  347. if( ($timestart - $hash->{request_time}) > 10)
  348. {
  349. Log3 $name, 2, "$name: timeout, close ua";
  350. $hash->{socket} = undef;
  351. $hash->{request_active} = 0;
  352. $hash->{logged_in} = 0;
  353. $hash->{startup_done} = 0;
  354. }
  355. }
  356. elsif( !$hash->{logged_in} ) {
  357. tahoma_login($hash) if (!(defined $hash->{loginRetryTimer}) || !(defined $hash->{request_time}) || (($timestart - $hash->{request_time}) >= $hash->{loginRetryTimer}));
  358. $timeinfo = "tahoma_login";
  359. }
  360. elsif( !$hash->{startup_done} ) {
  361. tahoma_startup($hash);
  362. $timeinfo = "tahoma_startup";
  363. if ( $hash->{startup_done} ) {
  364. tahoma_getStates($hash) ;
  365. $hash->{refreshStateTimer} = $seconds + $hash->{INTERVAL};
  366. $timeinfo = "tahoma_getStates";
  367. }
  368. }
  369. elsif( ($seconds < $hash->{refreshStateTimer}) || ($hash->{INTERVAL} <= 0) )
  370. {
  371. Log3 $name, 4, "$name: refreshing event";
  372. tahoma_getEvents($hash);
  373. $timeinfo = "tahoma_getEvents";
  374. }
  375. else
  376. {
  377. Log3 $name, 4, "$name: refreshing state";
  378. tahoma_refreshAllStates($hash);
  379. tahoma_getStates($hash);
  380. $hash->{refreshStateTimer} = $seconds + $hash->{INTERVAL};
  381. $timeinfo = "tahoma_refreshAllStates tahoma_getStates";
  382. }
  383. my $timedelta = time -$timestart;
  384. if ($timedelta > 0.5)
  385. {
  386. $timedelta *= 1000;
  387. Log3 $name, 3, "$name: $timeinfo took $timedelta ms"
  388. }
  389. InternalTimer(gettimeofday()+2, "tahoma_readStatusTimer", $hash, 0);
  390. }
  391. sub tahoma_connect($)
  392. {
  393. my ($hash) = @_;
  394. my $name = $hash->{NAME};
  395. Log3 $name, 3, "$name: tahoma_connect";
  396. RemoveInternalTimer($hash);
  397. tahoma_login($hash);
  398. my ($seconds) = gettimeofday();
  399. $hash->{refreshStateTimer} = $seconds + 10;
  400. tahoma_readStatusTimer($hash);
  401. }
  402. sub tahoma_initDevice($)
  403. {
  404. my ($hash) = @_;
  405. my $name = $hash->{NAME};
  406. my $subtype = $hash->{SUBTYPE};
  407. AssignIoPort($hash);
  408. if(defined($hash->{IODev}->{NAME})) {
  409. Log3 $name, 3, "$name: I/O device is " . $hash->{IODev}->{NAME};
  410. } else {
  411. Log3 $name, 1, "$name: no I/O device";
  412. }
  413. my $device;
  414. if( defined($hash->{device}) ) {
  415. $device = tahoma_getDeviceDetail( $hash, $hash->{device} );
  416. #Log3 $name, 4, Dumper($device);
  417. } elsif( defined($hash->{oid}) ) {
  418. $device = tahoma_getDeviceDetail( $hash, $hash->{oid} );
  419. #Log3 $name, 4, Dumper($device);
  420. }
  421. if( defined($device) && ($subtype eq 'DEVICE') ) {
  422. Log3 $name, 4, "$name: I/O device is label=".$device->{label};
  423. $hash->{inType} = $device->{type};
  424. $hash->{inLabel} = $device->{label};
  425. $hash->{inControllable} = $device->{controllableName};
  426. $hash->{inPlaceOID} = $device->{placeOID};
  427. $hash->{inClass} = $device->{uiClass};
  428. $device->{levelInvert} = $attr{$hash->{NAME}}{levelInvert} if (defined $attr{$hash->{NAME}}{levelInvert});
  429. }
  430. elsif( defined($device) && ($subtype eq 'PLACE') ) {
  431. Log3 $name, 4, "$name: I/O device is label=".$device->{label};
  432. $hash->{inType} = $device->{type};
  433. $hash->{inLabel} = $device->{label};
  434. $hash->{inOID} = $device->{oid};
  435. $hash->{inClass} = 'RollerShutter';
  436. $hash->{inClass} = $attr{$hash->{NAME}}{placeClasses} if (defined $attr{$hash->{NAME}}{placeClasses});
  437. }
  438. elsif( defined($device) && ($subtype eq 'SCENE') ) {
  439. Log3 $name, 4, "$name: I/O device is label=".$device->{label};
  440. $hash->{inLabel} = $device->{label};
  441. $hash->{inOID} = $device->{oid};
  442. }
  443. elsif($subtype eq 'GROUP' ) {
  444. $hash->{inType} = '';
  445. $hash->{inLabel} = '';
  446. $hash->{inLabel} = $attr{$hash->{NAME}}{alias} if (defined $attr{$hash->{NAME}}{alias});
  447. $hash->{inOID} = '';
  448. $hash->{inClass} = '';
  449. }
  450. else
  451. {
  452. my $device=$hash->{device};
  453. $device ||= 'undefined';
  454. $subtype ||= 'undefined';
  455. Log3 $name, 3, "$name: unknown device=$device, subtype=$subtype";
  456. }
  457. }
  458. sub tahoma_updateDevices($)
  459. {
  460. my ($hash) = @_;
  461. my $name = $hash->{NAME};
  462. Log3 $name, 3, "$name: tahoma_updateDevices";
  463. return undef if( !$hash->{helper}{devices} ) ;
  464. $hash = $hash->{IODev} if( defined($hash->{IODev}) );
  465. foreach my $module (keys %{$modules{$hash->{TYPE}}{defptr}}) {
  466. my $def = $modules{$hash->{TYPE}}{defptr}{"$module"};
  467. my $subtype = $def->{SUBTYPE};
  468. if (defined($def->{oid}) && !defined($def->{inType}))
  469. {
  470. Log3 $name, 3, "$name: updateDevices oid=$def->{oid}";
  471. my $device = tahoma_getDeviceDetail( $hash, $def->{oid} );
  472. if( defined($device) && ($subtype eq 'PLACE') ) {
  473. Log3 $name, 4, "$name: I/O device is label=".$device->{label};
  474. $def->{inType} = $device->{type};
  475. $def->{inLabel} = $device->{label};
  476. $def->{inOID} = $device->{oid};
  477. $def->{inClass} = 'RollerShutter';
  478. $def->{inClass} = $attr{$def->{NAME}}{placeClasses} if (defined $attr{$def->{NAME}}{placeClasses});
  479. }
  480. elsif( defined($device) && ($subtype eq 'SCENE') ) {
  481. Log3 $name, 4, "$name: I/O device is label=".$device->{label};
  482. $def->{inLabel} = $device->{label};
  483. $def->{inOID} = $device->{oid};
  484. }
  485. }
  486. elsif (defined($def->{device}) && !defined($def->{inType}))
  487. {
  488. Log3 $name, 3, "$name: updateDevices device=$def->{device}";
  489. my $device = tahoma_getDeviceDetail( $hash, $def->{device} );
  490. if( defined($device) && ($subtype eq 'DEVICE') ) {
  491. Log3 $name, 4, "$name: I/O device is label=".$device->{label};
  492. $def->{inType} = $device->{type};
  493. $def->{inLabel} = $device->{label};
  494. $def->{inControllable} = $device->{controllableName};
  495. $def->{inPlaceOID} = $device->{placeOID};
  496. $def->{inClass} = $device->{uiClass};
  497. $device->{levelInvert} = $attr{$def->{NAME}}{levelInvert} if (defined $attr{$def->{NAME}}{levelInvert});
  498. }
  499. }
  500. }
  501. return undef;
  502. }
  503. sub tahoma_getDevices($$)
  504. {
  505. my ($hash,$nonblocking) = @_;
  506. my $name = $hash->{NAME};
  507. Log3 $name, 4, "$name: tahoma_getDevices";
  508. tahoma_UserAgent_NonblockingGet({
  509. timeout => 10,
  510. noshutdown => 1,
  511. hash => $hash,
  512. page => 'getSetup',
  513. callback => \&tahoma_dispatch,
  514. nonblocking => $nonblocking,
  515. });
  516. return $hash->{helper}{devices};
  517. }
  518. sub tahoma_getDeviceDetail($$)
  519. {
  520. my ($hash,$id) = @_;
  521. my $name = $hash->{NAME};
  522. Log3 $name, 4, "$name: tahoma_getDeviceDetails $id";
  523. $hash = $hash->{IODev} if( defined($hash->{IODev}) );
  524. foreach my $device (@{$hash->{helper}{devices}}) {
  525. return $device if( defined($device->{deviceURL}) && ($device->{deviceURL} eq $id) );
  526. return $device if( defined($device->{oid}) && ($device->{oid} eq $id) );
  527. }
  528. Log3 $name, 4, "$name: getDeviceDetails $id not found";
  529. return undef;
  530. }
  531. sub tahoma_getStates($)
  532. {
  533. my ($hash) = @_;
  534. my $name = $hash->{NAME};
  535. Log3 $name, 4, "$name: tahoma_getStates";
  536. my $data = '[';
  537. foreach my $device (@{$hash->{helper}{devices}}) {
  538. if( defined($device->{deviceURL}) && defined($device->{states}) )
  539. {
  540. $data .= ',' if (substr($data, -1) eq '}');
  541. $data .= '{"deviceURL":"'.$device->{deviceURL}.'","states":[';
  542. foreach my $state (@{$device->{states}}) {
  543. $data .= ',' if (substr($data, -1) eq '}');
  544. $data .= '{"name":"' . $state->{name} . '"}';
  545. }
  546. $data .= ']}';
  547. }
  548. }
  549. $data .= ']';
  550. Log3 $name, 5, "$name: tahoma_getStates data=".$data;
  551. tahoma_UserAgent_NonblockingGet({
  552. timeout => 10,
  553. noshutdown => 1,
  554. hash => $hash,
  555. page => 'getStates',
  556. data => tahoma_encode_utf8($data),
  557. callback => \&tahoma_dispatch,
  558. nonblocking => 1,
  559. });
  560. }
  561. sub tahoma_getDeviceList($$$$);
  562. sub tahoma_getDeviceList($$$$)
  563. {
  564. my ($hash,$oid,$placeClasses,$deviceList) = @_;
  565. #print "tahoma_getDeviceList oid=$oid devices=".scalar @{$deviceList}."\n";
  566. my @classes = split(' ',$placeClasses);
  567. my $devices = $hash->{helper}{devices};
  568. foreach my $device (@{$devices}) {
  569. if ( defined($device->{deviceURL}) && defined($device->{placeOID}) && defined($device->{uiClass}) ) {
  570. if (( grep { $_ eq $device->{uiClass}} @classes ) && ($device->{placeOID} eq $oid)) {
  571. push ( @{$deviceList}, { device => $device->{deviceURL}, class => $device->{uiClass}, levelInvert => $device->{levelInvert} } ) ;
  572. #print "tahoma_getDeviceList url=$device->{deviceURL} devices=".scalar @{$deviceList}."\n";
  573. }
  574. } elsif ( defined($device->{oid}) && defined($device->{subPlaces}) ) {
  575. if ($device->{oid} eq $oid)
  576. {
  577. foreach my $place (@{$device->{subPlaces}}) {
  578. tahoma_getDeviceList($hash,$place->{oid},$placeClasses,$deviceList);
  579. }
  580. }
  581. }
  582. }
  583. }
  584. sub tahoma_getGroupList($$$)
  585. {
  586. my ($hash,$oid,$deviceList) = @_;
  587. #print "tahoma_getGroupList oid=$oid devices=".scalar @{$deviceList}."\n";
  588. my @groupDevices = split(',',$oid);
  589. foreach my $module (@groupDevices) {
  590. if (defined($defs{$module}) && defined($defs{$module}{device}) && defined($defs{$module}{inClass})) {
  591. push ( @{$deviceList}, { device => $defs{$module}{device}, class => $defs{$module}{inClass}, levelInvert => $attr{$module}{levelInvert} } ) ;
  592. }
  593. }
  594. }
  595. sub tahoma_checkCommand($$$$)
  596. {
  597. my ($hash,$device,$command,$value) = @_;
  598. my $name = $hash->{NAME};
  599. Log3 $name, 4, "$name: tahoma_checkCommand";
  600. if (($command eq 'setClosure') && (defined ($device->{levelInvert})))
  601. {
  602. $value = 100 - $value if ($device->{levelInvert} && ($value >= 0) && ($value <= 100));
  603. }
  604. if (($command eq 'setClosure') && ($value == 100) && (index($hash->{COMMANDS}," close:") > 0))
  605. {
  606. $command = 'close';
  607. $value = '';
  608. }
  609. if (($command eq 'setClosure') && ($value == 0) && (index($hash->{COMMANDS}," open:") > 0))
  610. {
  611. $command = 'open';
  612. $value = '';
  613. }
  614. return ($command,$value);
  615. }
  616. sub tahoma_applyRequest($$$)
  617. {
  618. my ($hash,$command,$value) = @_;
  619. my $name = $hash->{NAME};
  620. Log3 $name, 4, "$name: tahoma_applyRequest";
  621. if ( !defined($hash->{IODev}) || !(defined($hash->{device}) || defined($hash->{oid})) || !defined($hash->{inLabel}) || !defined($hash->{inClass}) ) {
  622. Log3 $name, 3, "$name: tahoma_applyRequest failed - define error";
  623. return;
  624. }
  625. my @devices = ();
  626. if ( defined($hash->{device}) ) {
  627. push ( @devices, { device => $hash->{device}, class => $hash->{inClass}, commands => $hash->{COMMANDS}, levelInvert => $attr{$hash->{NAME}}{levelInvert} } );
  628. } elsif ($hash->{SUBTYPE} eq 'GROUP') {
  629. tahoma_getGroupList($hash->{IODev},$hash->{oid},\@devices);
  630. } else {
  631. tahoma_getDeviceList($hash->{IODev},$hash->{oid},$hash->{inClass},\@devices);
  632. }
  633. Log3 $name, 4, "$name: tahoma_applyRequest devices=".scalar @devices;
  634. foreach my $dev (@devices) {
  635. Log3 $name, 4, "$name: tahoma_applyRequest devices=$dev->{device} class=$dev->{class}";
  636. }
  637. return if (scalar @devices < 1);
  638. my $data = '';
  639. $value = '' if (!defined($value));
  640. my $commandChecked = $command;
  641. my $valueChecked = $value;
  642. foreach my $device (@devices) {
  643. ($commandChecked, $valueChecked) = tahoma_checkCommand($hash,$device,$command,$value);
  644. if (defined($commandChecked) && defined($valueChecked))
  645. {
  646. $data .= ',' if substr($data, -1) eq '}';
  647. $data .= '{"deviceURL":"'.$device->{device}.'",';
  648. $data .= '"commands":[{"name":"'.$commandChecked.'","parameters":['.$valueChecked.']}]}';
  649. }
  650. }
  651. return if (length $data < 20);
  652. my $dataHead = '{"label":"' . $hash->{inLabel};
  653. if ($commandChecked eq 'setClosure') {
  654. $dataHead .= ' - Positionieren auf '.$valueChecked.' % - iPhone","actions":[';
  655. } elsif ($commandChecked eq 'close') {
  656. $dataHead .= ' - Schliessen - iPhone","actions":[';
  657. } elsif ($commandChecked eq 'open') {
  658. $dataHead .= ' - Oeffnen - iPhone","actions":[';
  659. } elsif ($commandChecked eq 'setClosureAndLinearSpeed') { #neu fuer setClosureAndLinearSpeed
  660. $dataHead .= ' - Positionieren auf '.(split(',',$valueChecked))[0].' % - iPhone","actions":['; #neu fuer setClosureAndLinearSpeed
  661. } else {
  662. $dataHead .= " - $commandChecked $valueChecked".' - iPhone","actions":[';
  663. }
  664. $data = $dataHead . $data . ']}';
  665. Log3 $name, 3, "$name: tahoma_applyRequest data=".$data;
  666. tahoma_UserAgent_NonblockingGet({
  667. timeout => 10,
  668. noshutdown => 1,
  669. hash => $hash,
  670. page => 'apply',
  671. data => tahoma_encode_utf8($data),
  672. callback => \&tahoma_dispatch,
  673. nonblocking => 1,
  674. });
  675. }
  676. sub tahoma_scheduleActionGroup($$)
  677. {
  678. my ($hash,$delay) = @_;
  679. my $name = $hash->{NAME};
  680. Log3 $name, 4, "$name: tahoma_scheduleActionGroup";
  681. if ( !defined($hash->{IODev}) || !defined($hash->{oid}) ) {
  682. Log3 $name, 3, "$name: tahoma_scheduleActionGroup failed - define error";
  683. return;
  684. }
  685. $delay = 0 if(!defined($delay));
  686. tahoma_UserAgent_NonblockingGet({
  687. timeout => 10,
  688. noshutdown => 1,
  689. hash => $hash,
  690. page => 'scheduleActionGroup',
  691. subpage => '?oid='.$hash->{oid}.'&delay='.$delay,
  692. callback => \&tahoma_dispatch,
  693. nonblocking => 1,
  694. });
  695. }
  696. sub tahoma_launchActionGroup($)
  697. {
  698. my ($hash) = @_;
  699. my $name = $hash->{NAME};
  700. Log3 $name, 4, "$name: tahoma_launchActionGroup";
  701. if ( !defined($hash->{IODev}) || !defined($hash->{oid}) ) {
  702. Log3 $name, 3, "$name: tahoma_launchActionGroup failed - define error";
  703. return;
  704. }
  705. tahoma_UserAgent_NonblockingGet({
  706. timeout => 10,
  707. noshutdown => 1,
  708. hash => $hash,
  709. page => 'launchActionGroup',
  710. subpage => '?oid='.$hash->{oid},
  711. callback => \&tahoma_dispatch,
  712. nonblocking => 1,
  713. });
  714. }
  715. sub tahoma_cancelExecutions($)
  716. {
  717. my ($hash) = @_;
  718. my $name = $hash->{NAME};
  719. Log3 $name, 4, "$name: tahoma_cancelExecutions";
  720. my $subpage = '';
  721. if (defined($hash->{IODev}))
  722. {
  723. if (defined($hash->{inExecId}) && (length $hash->{inExecId} > 20))
  724. {
  725. $subpage = '?execId='.$hash->{inExecId};
  726. }
  727. elsif (defined($hash->{inTriggerId}) && (length $hash->{inTriggerId} > 20))
  728. {
  729. $subpage = '?triggerId='.$hash->{inTriggerId};
  730. }
  731. else
  732. {
  733. Log3 $name, 3, "$name: tahoma_cancelExecutions failed - no valid execId or triggerId found";
  734. return;
  735. }
  736. }
  737. Log3 $name, 3, "$name: tahoma_cancelExecutions subpage=$subpage";
  738. tahoma_UserAgent_NonblockingGet({
  739. timeout => 10,
  740. noshutdown => 1,
  741. hash => $hash,
  742. page => 'cancelExecutions',
  743. subpage => $subpage,
  744. callback => \&tahoma_dispatch,
  745. nonblocking => 1,
  746. });
  747. }
  748. sub tahoma_dispatch($$$)
  749. {
  750. my ($param, $err, $data) = @_;
  751. my $hash = $param->{hash};
  752. my $name = $hash->{NAME};
  753. my $hashIn = $hash;
  754. $hash = $hash->{IODev} if (defined($hash->{IODev}));
  755. $hash->{request_active} = 0;
  756. if( $err ) {
  757. Log3 $name, 2, "$name: tahoma_dispatch http request failed: $err";
  758. $hash->{logged_in} = 0;
  759. return;
  760. }
  761. if( $data ) {
  762. tahoma_GetCookies($hash,$param->{httpheader}) if (!$hash->{logged_in});
  763. $data =~ tr/\r\n//d;
  764. $data =~ s/\h+/ /g;
  765. $data =~ s/\\\//\//g;
  766. Log3 $name, (length $data > 120)?4:5, "$name: tahoma_dispatch data=".decode_utf8($data);
  767. # perl exception while parsing json string captured
  768. my $json = {};
  769. eval { $json = JSON->new->utf8(0)->decode($data); };
  770. if ($@) {
  771. Log3 $name, 3, "$name: tahoma_dispatch json string is faulty";
  772. $hash->{lastError} = 'json string is faulty';
  773. $hash->{logged_in} = 0;
  774. return;
  775. }
  776. if( (ref $json ne 'ARRAY') && ($json->{errorResponse}) ) {
  777. $hash->{lastError} = $json->{errorResponse}{message};
  778. $hash->{logged_in} = 0;
  779. Log3 $name, 3, "$name: tahoma_dispatch error: $hash->{lastError}";
  780. return;
  781. }
  782. if( (ref $json ne 'ARRAY') && ($json->{error}) ) {
  783. $hash->{lastError} = $json->{error};
  784. $hash->{logged_in} = 0;
  785. Log3 $name, 3, "$name: tahoma_dispatch error: $hash->{lastError}";
  786. return;
  787. }
  788. if( $param->{page} eq 'getEvents' ) {
  789. tahoma_parseGetEvents($hash,$json);
  790. } elsif( $param->{page} eq 'apply' ) {
  791. tahoma_parseApplyRequest($hashIn,$json);
  792. } elsif( $param->{page} eq 'getSetup' ) {
  793. tahoma_parseGetSetup($hash,$json);
  794. } elsif( $param->{page} eq 'refreshAllStates' ) {
  795. tahoma_parseRefreshAllStates($hash,$json);
  796. } elsif( $param->{page} eq 'getStates' ) {
  797. tahoma_parseGetStates($hash,$json);
  798. } elsif( $param->{page} eq 'login' ) {
  799. tahoma_parseLogin($hash,$json);
  800. } elsif( $param->{page} eq 'getActionGroups' ) {
  801. tahoma_parseGetActionGroups($hash,$json);
  802. } elsif( $param->{page} eq '../../enduserAPI/setup/gateways' ) {
  803. tahoma_parseEnduserAPISetupGateways($hash,$json);
  804. } elsif( $param->{page} eq 'getCurrentExecutions' ) {
  805. tahoma_parseGetCurrentExecutions($hash,$json);
  806. } elsif( $param->{page} eq 'scheduleActionGroup' ) {
  807. tahoma_parseScheduleActionGroup($hashIn,$json);
  808. } elsif( $param->{page} eq 'launchActionGroup' ) {
  809. tahoma_parseLaunchActionGroup($hashIn,$json);
  810. } elsif( $param->{page} eq 'cancelExecutions' ) {
  811. tahoma_parseCancelExecutions($hash,$json);
  812. }
  813. }
  814. }
  815. sub tahoma_autocreate($)
  816. {
  817. my($hash) = @_;
  818. my $name = $hash->{NAME};
  819. if( !$hash->{helper}{devices} ) {
  820. tahoma_getDevices($hash,1);
  821. return undef;
  822. }
  823. foreach my $d (keys %defs) {
  824. next if($defs{$d}{TYPE} ne "autocreate");
  825. return undef if(AttrVal($defs{$d}{NAME},"disable",undef));
  826. }
  827. Log3 $name, 2, "$name: tahoma_autocreate begin";
  828. my $autocreated = 0;
  829. my $devices = $hash->{helper}{devices};
  830. foreach my $device (@{$devices}) {
  831. my ($id, $fid, $devname, $define);
  832. if ($device->{deviceURL}) {
  833. $id = $device->{deviceURL};
  834. $fid = tahoma_fhemIdFromDevice($id);
  835. $devname = "tahoma_". $fid;
  836. $define = "$devname tahoma DEVICE $id";
  837. if( defined($modules{$hash->{TYPE}}{defptr}{"$fid"}) ) {
  838. Log3 $name, 4, "$name: device '$fid' already defined";
  839. next;
  840. }
  841. } elsif ( $device->{oid} ) {
  842. $id = $device->{oid};
  843. my $fid = tahoma_fhemIdFromOid($id);
  844. $devname = "tahoma_". $fid;
  845. $define = "$devname tahoma PLACE $id" if (!defined $device->{actions});
  846. $define = "$devname tahoma SCENE $id" if (defined $device->{actions});
  847. if( defined($modules{$hash->{TYPE}}{defptr}{"$fid"}) ) {
  848. Log3 $name, 4, "$name: device '$fid' already defined";
  849. next;
  850. }
  851. }
  852. Log3 $name, 3, "$name: create new device '$devname' for device '$id'";
  853. my $cmdret= CommandDefine(undef,$define);
  854. if($cmdret) {
  855. Log3 $name, 1, "$name: Autocreate: An error occurred while creating device for id '$id': $cmdret";
  856. } else {
  857. $cmdret= CommandAttr(undef,"$devname alias room ".$device->{label}) if( defined($device->{label}) && defined($device->{oid}) && !defined($device->{actions}) );
  858. $cmdret= CommandAttr(undef,"$devname alias scene ".$device->{label}) if( defined($device->{label}) && defined($device->{oid}) && defined($device->{actions}) );
  859. $cmdret= CommandAttr(undef,"$devname alias $device->{uiClass} ".$device->{label}) if( defined($device->{label}) && defined($device->{states}) );
  860. $cmdret= CommandAttr(undef,"$devname room tahoma");
  861. $cmdret= CommandAttr(undef,"$devname IODev $name");
  862. $cmdret= CommandAttr(undef,"$devname webCmd dim") if( defined($device->{uiClass}) && ($device->{uiClass} eq "RollerShutter") );
  863. $autocreated++;
  864. }
  865. }
  866. CommandSave(undef,undef) if( $autocreated && AttrVal( "autocreate", "autosave", 1 ) );
  867. Log3 $name, 2, "$name: tahoma_autocreate end, new=$autocreated";
  868. }
  869. sub tahoma_defineCommands($)
  870. {
  871. my($hash) = @_;
  872. my $name = $hash->{NAME};
  873. Log3 $name, 4, "$name: tahoma_defineCommands";
  874. my $devices = $hash->{helper}{devices};
  875. foreach my $device (@{$devices}) {
  876. my ($id, $fid, $devname, $define);
  877. if ($device->{deviceURL}) {
  878. $id = $device->{deviceURL};
  879. $fid = tahoma_fhemIdFromDevice($id);
  880. $devname = "tahoma_". $fid;
  881. $define = "$devname tahoma DEVICE $id";
  882. my $commandlist = "";
  883. if( defined $device->{definition}{commands}[0]{commandName} ) {
  884. $commandlist = "dim:slider,0,1,100 cancel:noArg";
  885. foreach my $command (@{$device->{definition}{commands}}) {
  886. $commandlist .= " " . $command->{commandName};
  887. $commandlist .= ":noArg" if ($command->{nparams} == 0);
  888. }
  889. }
  890. if( defined($modules{$hash->{TYPE}}{defptr}{"$fid"}) ) {
  891. $modules{$hash->{TYPE}}{defptr}{"$fid"}{COMMANDS} = $commandlist;
  892. Log3 $name, 4, "$name: tahoma_defineCommands fid=$fid commandlist=$commandlist";
  893. }
  894. }
  895. }
  896. }
  897. sub tahoma_parseLogin($$)
  898. {
  899. my($hash, $json) = @_;
  900. my $name = $hash->{NAME};
  901. Log3 $name, 4, "$name: tahoma_parseLogin";
  902. if (defined $json->{errorResponse}) {
  903. $hash->{logged_in} = 0;
  904. $hash->{STATE} = $json->{errorResponse}{message};
  905. } else {
  906. $hash->{inVersion} = $json->{version};
  907. $hash->{logged_in} = 1;
  908. $hash->{loginRetryTimer} = 5,
  909. }
  910. Log3 $name, 2, "$name: login end, logged_in=".$hash->{logged_in};
  911. }
  912. sub tahoma_parseGetEvents($$)
  913. {
  914. my($hash, $json) = @_;
  915. my $name = $hash->{NAME};
  916. Log3 $name, 5, "$name: tahoma_parseGetEvent";
  917. $hash->{refresh_event} = $json;
  918. if( $hash->{logged_in} ) {
  919. $hash->{STATE} = "Connected";
  920. } else {
  921. $hash->{STATE} = "Disconnected";
  922. }
  923. if( ref $json eq 'ARRAY' ) {
  924. #print Dumper($json);
  925. foreach my $devices ( @{$json} ) {
  926. if( defined($devices->{deviceURL}) ) {
  927. #print "\nDevice=$devices->{deviceURL} found\n";
  928. my $id = $devices->{deviceURL};
  929. my $fid = tahoma_fhemIdFromDevice($id);
  930. my $devname = "tahoma_". $fid;
  931. my $d = $modules{$hash->{TYPE}}{defptr}{"$fid"};
  932. if( defined($d) )# && $d->{NAME} eq $devname )
  933. {
  934. #print "\nDevice=$devices->{deviceURL} updated\n";
  935. readingsBeginUpdate($d);
  936. foreach my $state (@{$devices->{deviceStates}}) {
  937. #print "$devname $state->{name} = $state->{value}\n";
  938. next if (!defined($state->{name}) || !defined($state->{value}));
  939. if ($state->{name} eq "core:ClosureState") {
  940. $state->{value} = 100 - $state->{value} if ($attr{$d->{NAME}}{levelInvert});
  941. readingsBulkUpdate($d, "state", "dim".$state->{value});
  942. } elsif ($state->{name} eq "core:OpenClosedState") {
  943. readingsBulkUpdate($d, "devicestate", $state->{value});
  944. }
  945. readingsBulkUpdate($d, (split(":",$state->{name}))[-1], $state->{value});
  946. }
  947. my ($seconds) = gettimeofday();
  948. readingsBulkUpdate( $d, ".lastupdate", $seconds, 0 );
  949. readingsEndUpdate($d,1);
  950. }
  951. }
  952. elsif( defined($devices->{name}) && (defined($devices->{execId}) || defined($devices->{triggerId})) )
  953. {
  954. foreach my $module (keys %{$modules{$hash->{TYPE}}{defptr}})
  955. {
  956. my $def = $modules{$hash->{TYPE}}{defptr}{"$module"};
  957. if (defined($def->{inExecId}) && ($def->{inExecId} eq $devices->{execId}))
  958. {
  959. if ($devices->{name} eq 'ExecutionStateChangedEvent')
  960. {
  961. $def->{inExecState} = $devices->{newState};
  962. $def->{inExecId} = 'finished' if ($devices->{newState} == 4);
  963. $def->{inExecId} = 'canceled' if ($devices->{newState} == 5);
  964. }
  965. }
  966. elsif (defined($def->{inTriggerId}) && ($def->{inTriggerId} eq $devices->{triggerId}))
  967. {
  968. $def->{inTriggerState} = $devices->{name};
  969. $def->{inTriggerId} = 'finished' if ($devices->{name} eq '4');
  970. $def->{inTriggerId} = 'canceled' if ($devices->{name} eq '5');
  971. }
  972. }
  973. }
  974. }
  975. }
  976. }
  977. sub tahoma_parseApplyRequest($$)
  978. {
  979. my($hash, $json) = @_;
  980. my $name = $hash->{NAME};
  981. Log3 $name, 4, "$name: tahoma_parseApplyRequest";
  982. $hash->{inExecState} = 0;
  983. if (defined($json->{execId})) {
  984. $hash->{inExecId} = $json->{execId};
  985. } else {
  986. $hash->{inExecId} = "undefined";
  987. }
  988. if (defined($json->{events}) && defined($hash->{IODev}))
  989. {
  990. tahoma_parseGetEvents($hash->{IODev},$json->{events})
  991. }
  992. }
  993. sub tahoma_parseGetSetup($$)
  994. {
  995. my($hash, $json) = @_;
  996. my $name = $hash->{NAME};
  997. $hash->{gatewayId} = $json->{setup}{gateways}[0]{gatewayId};
  998. my @devices = ();
  999. foreach my $device (@{$json->{setup}{devices}}) {
  1000. Log3 $name, 4, "$name: tahoma_parseGetSetup device = $device->{label}";
  1001. push( @devices, $device );
  1002. }
  1003. $hash->{helper}{devices} = \@devices;
  1004. if ($json->{setup}{rootPlace}) {
  1005. my $places = $json->{setup}{rootPlace};
  1006. #Log3 $name, 4, "$name: tahoma_parseGetSetup places= " . Dumper($places);
  1007. tahoma_parseGetSetupPlaces($hash, $places);
  1008. }
  1009. tahoma_autocreate($hash);
  1010. tahoma_updateDevices($hash);
  1011. tahoma_defineCommands($hash);
  1012. }
  1013. sub tahoma_parseGetSetupPlaces($$)
  1014. {
  1015. my($hash, $places) = @_;
  1016. my $name = $hash->{NAME};
  1017. #Log3 $name, 4, "$name: tahoma_parseGetSetupPlaces " . Dumper($places);
  1018. my $devices = $hash->{helper}{devices};
  1019. if (ref $places eq 'ARRAY') {
  1020. #Log3 $name, 4, "$name: tahoma_parseGetSetupPlaces isArray";
  1021. foreach my $place (@{$places}) {
  1022. push( @{$devices}, $place );
  1023. my $placesNext = $place->{subPlaces};
  1024. tahoma_parseGetSetupPlaces($hash, $placesNext ) if ($placesNext);
  1025. }
  1026. }
  1027. else {
  1028. #Log3 $name, 4, "$name: tahoma_parseGetSetupPlaces isScalar";
  1029. push( @{$devices}, $places );
  1030. my $placesNext = $places->{subPlaces};
  1031. tahoma_parseGetSetupPlaces($hash, $placesNext) if ($placesNext);
  1032. }
  1033. }
  1034. sub tahoma_parseGetActionGroups($$)
  1035. {
  1036. my($hash, $json) = @_;
  1037. my $name = $hash->{NAME};
  1038. Log3 $name, 4, "$name: tahoma_parseGetActionGroups";
  1039. my $devices = $hash->{helper}{devices};
  1040. foreach my $action (@{$json->{actionGroups}}) {
  1041. push( @{$devices}, $action );
  1042. }
  1043. tahoma_autocreate($hash);
  1044. tahoma_updateDevices($hash);
  1045. tahoma_defineCommands($hash);
  1046. }
  1047. sub tahoma_parseRefreshAllStates($$)
  1048. {
  1049. my($hash, $json) = @_;
  1050. my $name = $hash->{NAME};
  1051. Log3 $name, 4, "$name: tahoma_parseRefreshAllStates";
  1052. }
  1053. sub tahoma_parseGetStates($$)
  1054. {
  1055. my($hash, $states) = @_;
  1056. my $name = $hash->{NAME};
  1057. Log3 $name, 4, "$name: tahoma_parseGetStates";
  1058. if( defined($states->{devices}) ) {
  1059. foreach my $devices ( @{$states->{devices}} ) {
  1060. if( defined($devices->{deviceURL}) ) {
  1061. my $id = $devices->{deviceURL};
  1062. my $fid = tahoma_fhemIdFromDevice($id);
  1063. my $devname = "tahoma_". $fid;
  1064. my $d = $modules{$hash->{TYPE}}{defptr}{"$fid"};
  1065. if( defined($d) )# && $d->{NAME} eq $devname )
  1066. {
  1067. readingsBeginUpdate($d);
  1068. foreach my $state (@{$devices->{states}}) {
  1069. next if (!defined($state->{name}) || !defined($state->{value}));
  1070. if ($state->{name} eq "core:ClosureState") {
  1071. $state->{value} = 100 - $state->{value} if ($attr{$d->{NAME}}{levelInvert});
  1072. readingsBulkUpdate($d, "state", "dim".$state->{value});
  1073. } elsif ($state->{name} eq "core:OpenClosedState") {
  1074. readingsBulkUpdate($d, "devicestate", $state->{value});
  1075. }
  1076. readingsBulkUpdate($d, (split(":",$state->{name}))[-1], $state->{value});
  1077. }
  1078. my ($seconds) = gettimeofday();
  1079. readingsBulkUpdate( $d, ".lastupdate", $seconds, 0 );
  1080. readingsEndUpdate($d,1);
  1081. }
  1082. }
  1083. }
  1084. }
  1085. }
  1086. sub tahoma_parseEnduserAPISetupGateways($$)
  1087. {
  1088. my($hash, $json) = @_;
  1089. my $name = $hash->{NAME};
  1090. Log3 $name, 4, "$name: tahoma_parseEnduserAPISetupGateways";
  1091. eval { $hash->{inGateway} = $json->{result}; };
  1092. eval { $hash->{inGateway} = $json->[0]{gatewayId}; };
  1093. }
  1094. sub tahoma_parseGetCurrentExecutions($$)
  1095. {
  1096. my($hash, $json) = @_;
  1097. my $name = $hash->{NAME};
  1098. Log3 $name, 4, "$name: tahoma_parseGetCurrentExecutions";
  1099. }
  1100. sub tahoma_parseScheduleActionGroup($$)
  1101. {
  1102. my($hash, $json) = @_;
  1103. my $name = $hash->{NAME};
  1104. Log3 $name, 4, "$name: tahoma_parseScheduleActionGroup";
  1105. if (defined $json->{actionGroup})
  1106. {
  1107. $hash->{inTriggerState} = 0;
  1108. if (defined($json->{actionGroup}[0]{triggerId})) {
  1109. $hash->{inTriggerId} = $json->{actionGroup}[0]{triggerId};
  1110. } else {
  1111. $hash->{inTriggerId} = "undefined";
  1112. }
  1113. }
  1114. if (defined($json->{events}) && defined($hash->{IODev}))
  1115. {
  1116. tahoma_parseGetEvents($hash->{IODev},$json->{events})
  1117. }
  1118. }
  1119. sub tahoma_parseLaunchActionGroup($$)
  1120. {
  1121. my($hash, $json) = @_;
  1122. my $name = $hash->{NAME};
  1123. Log3 $name, 4, "$name: tahoma_parseLaunchActionGroup";
  1124. if (defined $json->{actionGroup})
  1125. {
  1126. $hash->{inExecState} = 0;
  1127. if (defined($json->{actionGroup}[0]{execId})) {
  1128. $hash->{inExecId} = $json->{actionGroup}[0]{execId};
  1129. } else {
  1130. $hash->{inExecId} = "undefined";
  1131. }
  1132. }
  1133. if (defined($json->{events}) && defined($hash->{IODev}))
  1134. {
  1135. tahoma_parseGetEvents($hash->{IODev},$json->{events})
  1136. }
  1137. }
  1138. sub tahoma_parseCancelExecutions($$)
  1139. {
  1140. my($hash, $json) = @_;
  1141. my $name = $hash->{NAME};
  1142. Log3 $name, 4, "$name: tahoma_parseCancelExecutions";
  1143. }
  1144. sub tahoma_Get($$@)
  1145. {
  1146. my ($hash, $name, $cmd) = @_;
  1147. my $list;
  1148. if( $hash->{SUBTYPE} eq "DEVICE" ) {
  1149. $list = "updateAll:noArg";
  1150. if( $cmd eq "updateAll" ) {
  1151. my ($seconds) = gettimeofday();
  1152. $hash->{refreshStateTimer} = $seconds;
  1153. return undef;
  1154. }
  1155. } elsif( $hash->{SUBTYPE} eq "SCENE"
  1156. || $hash->{SUBTYPE} eq "GROUP"
  1157. || $hash->{SUBTYPE} eq "PLACE" ) {
  1158. $list = "";
  1159. } elsif( $hash->{SUBTYPE} eq "ACCOUNT" ) {
  1160. $list = "devices:noArg reset:noArg";
  1161. if( $cmd eq "devices" ) {
  1162. my $devices = tahoma_getDevices($hash,0);
  1163. my $ret;
  1164. foreach my $device (@{$devices}) {
  1165. $ret .= "$device->{deviceURL}\t".$device->{label}."\t$device->{uiClass}\t$device->{controllable}\t\n" if ($device->{deviceURL});
  1166. $ret .= "$device->{oid}\t".$device->{label}."\n" if ($device->{oid});
  1167. }
  1168. $ret = "id\t\t\t\tname\t\t\tuiClass\t\tcontrollable\n" . $ret if( $ret );
  1169. $ret = "no devices found" if( !$ret );
  1170. return $ret;
  1171. }
  1172. elsif( $cmd eq "reset" ) {
  1173. HttpUtils_Close($hash);
  1174. $hash->{logged_in} = undef;
  1175. $hash->{loginRetryTimer} = undef;
  1176. return "connection closed";
  1177. }
  1178. }
  1179. return "Unknown argument $cmd, choose one of $list";
  1180. }
  1181. sub tahoma_Set($$@)
  1182. {
  1183. my ($hash, $name, $cmd, $val) = @_;
  1184. #Log3 $name, 3, "$name: tahoma_Set $cmd $val $hash->{SUBTYPE} $hash->{COMMANDS}";
  1185. my $list = "";
  1186. if( $hash->{SUBTYPE} eq "DEVICE" ||
  1187. $hash->{SUBTYPE} eq "GROUP" ||
  1188. $hash->{SUBTYPE} eq "PLACE" ) {
  1189. $list = "dim:slider,0,1,100 setClosure open:noArg close:noArg my:noArg stop:noArg cancel:noArg";
  1190. $list = $hash->{COMMANDS} if (defined $hash->{COMMANDS});
  1191. if( $cmd eq "cancel" ) {
  1192. tahoma_cancelExecutions($hash);
  1193. return undef;
  1194. }
  1195. $cmd = "setClosure" if( $cmd eq "dim" );
  1196. my @commands = split(" ",$list);
  1197. foreach my $command (@commands)
  1198. {
  1199. if( $cmd eq (split(":",$command))[0])
  1200. {
  1201. tahoma_applyRequest($hash,$cmd,$val);
  1202. return undef;
  1203. }
  1204. }
  1205. }
  1206. if( $hash->{SUBTYPE} eq "SCENE") {
  1207. $list = "start:noArg startAt cancel:noArg";
  1208. if( $cmd eq "start" ) {
  1209. tahoma_launchActionGroup($hash);
  1210. return undef;
  1211. }
  1212. if( $cmd eq "startAt" ) {
  1213. tahoma_scheduleActionGroup($hash,$val);
  1214. return undef;
  1215. }
  1216. if( $cmd eq "cancel" ) {
  1217. tahoma_cancelExecutions($hash);
  1218. return undef;
  1219. }
  1220. }
  1221. if( $hash->{SUBTYPE} eq "ACCOUNT") {
  1222. $list = "cancel:noArg";
  1223. if( $cmd eq "cancel" ) {
  1224. tahoma_cancelExecutions($hash);
  1225. return undef;
  1226. }
  1227. }
  1228. return "Unknown argument $cmd, choose one of $list";
  1229. }
  1230. sub tahoma_Attr($$$)
  1231. {
  1232. my ($cmd, $name, $attrName, $attrVal) = @_;
  1233. my $orig = $attrVal;
  1234. if( $attrName eq "interval" ) {
  1235. my $hash = $defs{$name};
  1236. $attrVal = int($attrVal);
  1237. $attrVal = 60*5 if ($attrVal < 60*5 && $attrVal != 0);
  1238. $hash->{INTERVAL} = $attrVal;
  1239. } elsif( $attrName eq "disable" ) {
  1240. my $hash = $defs{$name};
  1241. RemoveInternalTimer($hash);
  1242. if( $cmd eq "set" && $attrVal ne "0" ) {
  1243. } else {
  1244. $attr{$name}{$attrName} = 0;
  1245. }
  1246. } elsif( $attrName eq "blocking" ) {
  1247. my $hash = $defs{$name};
  1248. $hash->{BLOCKING} = $attrVal;
  1249. } elsif( $attrName eq "placeClasses" ) {
  1250. my $hash = $defs{$name};
  1251. $hash->{inClass} = $attrVal if $hash->{SUBTYPE} eq "PLACE";
  1252. }
  1253. elsif ( $attrName eq "levelInvert" ) {
  1254. my $hash = $defs{$name};
  1255. my $device = tahoma_getDeviceDetail( $hash, $hash->{device} );
  1256. $device->{levelInvert} = $attrVal if (defined $device);
  1257. }
  1258. if( $cmd eq "set" ) {
  1259. if( $orig ne $attrVal ) {
  1260. $attr{$name}{$attrName} = $attrVal;
  1261. return $attrName ." set to ". $attrVal;
  1262. }
  1263. }
  1264. return;
  1265. }
  1266. sub tahoma_UserAgent_NonblockingGet($)
  1267. {
  1268. my ($param) = @_;
  1269. my ($hash) = $param->{hash};
  1270. $hash = $hash->{IODev} if (defined ($hash->{IODev}));
  1271. my $name = $hash->{NAME};
  1272. Log3 $name, 5, "$name: tahoma_UserAgent_NonblockingGet page=$param->{page}";
  1273. #"User-Agent":"TaHoma/7980 CFNetwork/758.5.3 Darwin/15.6.0","Proxy-Connection":"keep-alive","Accept":"*/*","Connection":"keep-alive","Content-Length":"49","Accept-Encoding":"gzip, deflate","Content-Type":"application/x-www-form-urlencoded","Accept-Language":"de-de","Host":"www.tahomalink.com"
  1274. $param->{header} = {'User-Agent' => $hash->{userAgent} }; #, 'Accept-Language' => "de-de", 'Accept-Encoding' => "gzip, deflate"};
  1275. $param->{header}{Cookie} = $hash->{HTTPCookies} if ($hash->{HTTPCookies});
  1276. $param->{compress} = 1;
  1277. $param->{keepalive} = 1;
  1278. if (index($hash->{url},'file:') == 0)
  1279. {
  1280. $param->{url} = $hash->{url} . $param->{page} . '.json';
  1281. my $find = "../";
  1282. $find = quotemeta $find; # escape regex metachars if present
  1283. $param->{url} =~ s/$find//g;
  1284. }
  1285. else
  1286. {
  1287. $param->{url} = $hash->{url} . $param->{page};
  1288. $param->{url} .= $param->{subpage} if ($param->{subpage});
  1289. }
  1290. $hash->{request_active} = 1;
  1291. $hash->{request_time} = time;
  1292. if ($param->{blocking})
  1293. {
  1294. my($err,$data) = HttpUtils_BlockingGet($param);
  1295. $param->{callback}($param, $err, $data, length $data) if($param->{callback});
  1296. }
  1297. else
  1298. {
  1299. my($err,$data) = HttpUtils_NonblockingGet($param);
  1300. }
  1301. }
  1302. sub tahoma_encode_utf8($)
  1303. {
  1304. my ($text) = @_;
  1305. $text =~ s/Ä/Ae/g;
  1306. $text =~ s/Ö/Oe/g;
  1307. $text =~ s/Ü/Ue/g;
  1308. $text =~ s/ä/ae/g;
  1309. $text =~ s/ö/oe/g;
  1310. $text =~ s/ü/ue/g;
  1311. $text =~ s/ß/ss/g;
  1312. return $text;
  1313. }
  1314. sub tahoma_GetCookies($$)
  1315. {
  1316. my ($hash, $header) = @_;
  1317. my $name = $hash->{NAME};
  1318. Log3 $name, 5, "$name: tahoma_GetCookies looking for Cookies in header";
  1319. foreach my $cookie ($header =~ m/set-cookie: ?(.*)/gi) {
  1320. Log3 $name, 5, "$name: Set-Cookie: $cookie";
  1321. $cookie =~ /([^,; ]+)=([^,; ]+)[;, ]*(.*)/;
  1322. Log3 $name, 4, "$name: Cookie: $1 Wert $2 Rest $3";
  1323. $hash->{HTTPCookieHash}{$1}{Value} = $2;
  1324. $hash->{HTTPCookieHash}{$1}{Options} = ($3 ? $3 : "");
  1325. }
  1326. $hash->{HTTPCookies} = join ("; ", map ($_ . "=".$hash->{HTTPCookieHash}{$_}{Value},
  1327. sort keys %{$hash->{HTTPCookieHash}}));
  1328. }
  1329. sub tahoma_encrypt($)
  1330. {
  1331. my ($decoded) = @_;
  1332. my $key = getUniqueId();
  1333. my $encoded;
  1334. return $decoded if( $decoded =~ /^crypt:(.*)/ );
  1335. for my $char (split //, $decoded) {
  1336. my $encode = chop($key);
  1337. $encoded .= sprintf("%.2x",ord($char)^ord($encode));
  1338. $key = $encode.$key;
  1339. }
  1340. return 'crypt:'. $encoded;
  1341. }
  1342. sub tahoma_decrypt($)
  1343. {
  1344. my ($encoded) = @_;
  1345. my $key = getUniqueId();
  1346. my $decoded;
  1347. return $encoded if not ( $encoded =~ /^crypt:(.*)/ );
  1348. $encoded = $1 if( $encoded =~ /^crypt:(.*)/ );
  1349. for my $char (map { pack('C', hex($_)) } ($encoded =~ /(..)/g)) {
  1350. my $decode = chop($key);
  1351. $decoded .= chr(ord($char)^ord($decode));
  1352. $key = $decode.$key;
  1353. }
  1354. return $decoded;
  1355. }
  1356. 1;
  1357. =pod
  1358. =item summary commumication modul for io-homecontrol&reg gateway TaHoma&reg
  1359. =item summary_DE Kommunicationsmodul f&uuml;er io-homecontrol&reg Gateway TaHoma&reg
  1360. =begin html
  1361. <a name="tahoma"></a>
  1362. <h3>tahoma</h3>
  1363. <ul>
  1364. The module realizes the communication with io-homecontrol&reg; Devices e.g. from Somfy&reg; or Velux&reg;<br>
  1365. A registered TaHoma&reg; Connect gateway from Overkiz&reg; sold by Somfy&reg; which is continously connected to the internet is necessary for the module.<br>
  1366. <br><br>
  1367. Notes:
  1368. <ul>
  1369. <li>JSON has to be installed on the FHEM host.</li>
  1370. <li>on problems refer also the fhem forum <a href="https://forum.fhem.de/index.php/topic,28045.0.html">IO-Homecontrol Devices &uuml;ber Tahoma Box einbinden</a></li>
  1371. </ul><br>
  1372. <a name="tahoma_Define"></a>
  1373. <b>Define</b>
  1374. <ul>
  1375. <code>define &lt;name&gt; tahoma ACCOUNT &lt;username&gt; &lt;password&gt;</code><br>
  1376. <code>define &lt;name&gt; tahoma DEVICE &lt;DeviceURL&gt;</code><br>
  1377. <code>define &lt;name&gt; tahoma PLACE &lt;oid&gt;</code><br>
  1378. <code>define &lt;name&gt; tahoma SCENE &lt;oid&gt;</code><br>
  1379. <code>define &lt;name&gt; tahoma GROUP &lt;tahoma_device1&gt;,&lt;tahoma_device2&gt;,&lt;tahoma_device3&gt;</code><br>
  1380. <br>
  1381. <br>
  1382. A definition is only necessary for a tahoma device:<br>
  1383. <code>define &lt;name&gt; tahoma ACCOUNT &lt;username&gt; &lt;password&gt;</code><br>
  1384. <b>If a tahoma device of the type ACCOUNT is created, all other devices acessable by the tahoma gateway are automaticaly created!</b><br>
  1385. If the account is valid, the setup will be read from the server.<br>
  1386. All registrated devices are automatically created with name tahoma_12345 (device number 12345 is used from setup)<br>
  1387. All defined rooms will be are automatically created.<br>
  1388. Also all defined scenes will be automatically created.<br>
  1389. Groups of devices can be manually added to send out one group command for all attached devices<br>
  1390. <br>
  1391. <br>
  1392. <b>global Attributes for ACCOUNT:</b>
  1393. <ul>
  1394. If autocreate is disabled, no devices, places and scenes will be created automatically:<br>
  1395. <code>attr autocreate disable</code><br>
  1396. </ul>
  1397. <br>
  1398. <b>local Attributes for ACCOUNT:</b>
  1399. <ul>
  1400. Normally, the web commands will be send asynchron, and this can be forced to wait of the result by blocking=1<br>
  1401. <code>attr tahoma1 blocking 1</code><br>
  1402. </ul>
  1403. <ul>
  1404. Normally, the login data is stored encrypted after the first start, but this functionality can be disabled by cryptLoginData=0<br>
  1405. <code>attr tahoma1 cryptLoginData 0</code><br>
  1406. </ul>
  1407. <br>
  1408. <b>local Attributes for DEVICE:</b>
  1409. <ul>
  1410. If the closure value 0..100 should be 100..0, the level can be inverted:<br>
  1411. <code>attr tahoma_23234545 levelInvert 1</code><br>
  1412. </ul>
  1413. <br>
  1414. <b>local Attributes for PLACE:</b>
  1415. <ul>
  1416. The commands in a room will only affect the devices in the room with inClass=RollerShutter.<br>
  1417. This can be extend or changed by setting the placeClasses attribut:<br>
  1418. <code>attr tahoma_abc12345 placeClasses RollerShutter ExteriorScreen Window</code><br>
  1419. </ul>
  1420. <br>
  1421. <b>Examples:</b>
  1422. <ul>
  1423. <code>define tahoma1 tahoma ACCOUNT abc@test.com myPassword</code><br>
  1424. <code>attr tahoma1 blocking 0</code><br>
  1425. <code>attr tahoma1 room tahoma</code><br>
  1426. <br>
  1427. <br>Automatic created device e.g.:<br>
  1428. <code>define tahoma_23234545 tahoma DEVICE io://0234-5678-9012/23234545</code><br>
  1429. <code>attr tahoma_23234545 IODev tahoma1</code><br>
  1430. <code>attr tahoma_23234545 alias RollerShutter Badezimmer</code><br>
  1431. <code>attr tahoma_23234545 room tahoma</code><br>
  1432. <code>attr tahoma_23234545 webCmd dim</code><br>
  1433. <br>
  1434. <br>Automatic created place e.g.:<br>
  1435. <code>define tahoma_abc12345 tahoma PLACE abc12345-0a23-0b45-0c67-d5e6f7a1b2c3</code><br>
  1436. <code>attr tahoma_abc12345 IODev tahoma1</code><br>
  1437. <code>attr tahoma_abc12345 alias room Wohnzimmer</code><br>
  1438. <code>attr tahoma_abc12345 room tahoma</code><br>
  1439. <br>
  1440. <br>Automatic created scene e.g.:<br>
  1441. <code>define tahoma_4ef30a23 tahoma SCENE 4ef30a23-0b45-0c67-d5e6-f7a1b2c32e3f</code><br>
  1442. <code>attr tahoma_4ef30a23 IODev tahoma1</code><br>
  1443. <code>attr tahoma_4ef30a23 alias scene Rolladen S&uuml;dfenster zu</code><br>
  1444. <code>attr tahoma_4ef30a23 room tahoma</code><br>
  1445. <br>
  1446. <br>manual created group e.g.:<br>
  1447. <code>define tahoma_group1 tahoma GROUP tahoma_23234545,tahoma_23234546,tahoma_23234547</code><br>
  1448. <code>attr tahoma_group1 IODev tahoma1</code><br>
  1449. <code>attr tahoma_group1 alias Gruppe Rolladen Westen</code><br>
  1450. <code>attr tahoma_group1 room tahoma</code><br>
  1451. </ul>
  1452. </ul><br>
  1453. </ul>
  1454. =end html
  1455. =cut