70_PHTV.pm 131 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644
  1. # $Id: 70_PHTV.pm 13442 2017-02-19 12:28:14Z loredo $
  2. ##############################################################################
  3. #
  4. # 70_PHTV.pm
  5. # An FHEM Perl module for controlling Philips Televisons
  6. # via network connection.
  7. #
  8. # Copyright by Julian Pawlowski
  9. # e-mail: julian.pawlowski at gmail.com
  10. #
  11. # This file is part of fhem.
  12. #
  13. # Fhem is free software: you can redistribute it and/or modify
  14. # it under the terms of the GNU General Public License as published by
  15. # the Free Software Foundation, either version 2 of the License, or
  16. # (at your option) any later version.
  17. #
  18. # Fhem 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. # You should have received a copy of the GNU General Public License
  24. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  25. #
  26. ##############################################################################
  27. package main;
  28. use 5.012;
  29. use strict;
  30. use warnings;
  31. use Data::Dumper;
  32. use Time::HiRes qw(gettimeofday);
  33. use HttpUtils;
  34. use Color;
  35. use SetExtensions;
  36. use Encode;
  37. sub PHTV_Set($@);
  38. sub PHTV_Get($@);
  39. sub PHTV_GetStatus($;$);
  40. sub PHTV_Define($$);
  41. sub PHTV_Undefine($$);
  42. #########################
  43. # Forward declaration for remotecontrol module
  44. #sub PHTV_RClayout_TV();
  45. #sub PHTV_RCmakenotify($$);
  46. ###################################
  47. sub PHTV_Initialize($) {
  48. my ($hash) = @_;
  49. Log3 $hash, 5, "PHTV_Initialize: Entering";
  50. $hash->{GetFn} = "PHTV_Get";
  51. $hash->{SetFn} = "PHTV_Set";
  52. $hash->{DefFn} = "PHTV_Define";
  53. $hash->{UndefFn} = "PHTV_Undefine";
  54. $hash->{AttrList} =
  55. "disable:0,1 timeout sequentialQuery:0,1 drippyFactor:0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 inputs ambiHueLeft ambiHueRight ambiHueTop ambiHueBottom ambiHueLatency:150,200,250,300,350,400,450,500,550,600,650,700,750,800,850,900,950,1000,1100,1200,1300,1400,1500,1600,1700,1800,1900,2000 jsversion:1,5,6 macaddr:textField model wakeupCmd:textField channelsMax:slider,30,1,200 device_id auth_key "
  56. . $readingFnAttributes;
  57. $data{RC_layout}{PHTV_SVG} = "PHTV_RClayout_SVG";
  58. $data{RC_layout}{PHTV} = "PHTV_RClayout";
  59. $data{RC_makenotify}{PHTV} = "PHTV_RCmakenotify";
  60. # 98_powerMap.pm support
  61. $hash->{powerMap} = {
  62. model => {
  63. '55PFL8008S/12' => {
  64. rname_E => 'energy',
  65. rname_P => 'consumption',
  66. map => {
  67. stateAV => {
  68. absent => 0,
  69. off => 0.1,
  70. '*' => 90,
  71. },
  72. },
  73. },
  74. },
  75. };
  76. FHEM_colorpickerInit();
  77. return;
  78. }
  79. #####################################
  80. sub PHTV_GetStatus($;$) {
  81. my ( $hash, $update ) = @_;
  82. my $name = $hash->{NAME};
  83. my $interval = $hash->{INTERVAL};
  84. my $sequential = AttrVal( $name, "sequentialQuery", 0 );
  85. my $querySent = 0;
  86. Log3 $name, 5, "PHTV $name: called function PHTV_GetStatus()";
  87. $interval = $interval * 1.6
  88. if ( ReadingsVal( $name, "ambiHue", "off" ) eq "on" );
  89. RemoveInternalTimer($hash);
  90. InternalTimer( gettimeofday() + $interval, "PHTV_GetStatus", $hash, 0 );
  91. return
  92. if ( IsDisabled($name) );
  93. # try to fetch only some information to check device availability
  94. if ( !$update ) {
  95. PHTV_SendCommand( $hash, "audio/volume" );
  96. # in case we should query the device gently, mark we already sent a query
  97. $querySent = 1 if $sequential;
  98. $hash->{helper}{sequentialQueryCounter} = 1 if $sequential;
  99. }
  100. # fetch other info if device is on
  101. if ( !$querySent
  102. && ( ReadingsVal( $name, "state", "off" ) eq "on" || $update ) )
  103. {
  104. # Read device info every 15 minutes only
  105. if (
  106. !$querySent
  107. && (
  108. !defined( $hash->{helper}{lastFullUpdate} )
  109. || ( !$update
  110. && $hash->{helper}{lastFullUpdate} + 900 le time() )
  111. )
  112. )
  113. {
  114. PHTV_SendCommand( $hash, "system" );
  115. PHTV_SendCommand( $hash, "ambilight/topology" );
  116. $querySent = 1 if $sequential;
  117. $hash->{helper}{sequentialQueryCounter}++ if $sequential;
  118. # Update state
  119. $hash->{helper}{lastFullUpdate} = time();
  120. }
  121. # read ambilight details
  122. if ( !$querySent ) {
  123. # read ambilight mode
  124. PHTV_SendCommand( $hash, "ambilight/mode" );
  125. $querySent = 1 if $sequential;
  126. $hash->{helper}{sequentialQueryCounter}++ if $sequential;
  127. # read ambilight RGB value
  128. PHTV_SendCommand( $hash, "ambilight/cached" )
  129. if ( ReadingsVal( $name, "ambiMode", "internal" ) ne "internal" );
  130. }
  131. # read all sources if not existing
  132. if (
  133. !$querySent
  134. && ( !defined( $hash->{helper}{device}{sourceName} )
  135. || !defined( $hash->{helper}{device}{sourceID} ) )
  136. )
  137. {
  138. PHTV_SendCommand( $hash, "sources" );
  139. $querySent = 1 if $sequential;
  140. $hash->{helper}{sequentialQueryCounter}++ if $sequential;
  141. }
  142. # otherwise read current source
  143. elsif ( !$querySent ) {
  144. PHTV_SendCommand( $hash, "sources/current" );
  145. $querySent = 1 if $sequential;
  146. $hash->{helper}{sequentialQueryCounter}++ if $sequential;
  147. }
  148. # read all channels if not existing
  149. if (
  150. !$querySent
  151. && ( !defined( $hash->{helper}{device}{channelName} )
  152. || !defined( $hash->{helper}{device}{channelID} ) )
  153. )
  154. {
  155. PHTV_SendCommand( $hash, "channels" );
  156. $querySent = 1 if $sequential;
  157. $hash->{helper}{sequentialQueryCounter}++ if $sequential;
  158. }
  159. # otherwise read current channel
  160. elsif ( !$querySent ) {
  161. PHTV_SendCommand( $hash, "channels/current" );
  162. $querySent = 1 if $sequential;
  163. $hash->{helper}{sequentialQueryCounter}++ if $sequential;
  164. }
  165. }
  166. # Input alias handling
  167. #
  168. if ( AttrVal( $name, "inputs", "" ) ne "" ) {
  169. my @inputs = split( ':', AttrVal( $name, "inputs", ":" ) );
  170. if (@inputs) {
  171. foreach (@inputs) {
  172. if (m/[^,\s]+(,[^,\s]+)+/) {
  173. my @input_names = split( ',', $_ );
  174. $input_names[1] =~ s/\s/_/g;
  175. $hash->{helper}{device}{inputAliases}{ $input_names[0] } =
  176. $input_names[1];
  177. $hash->{helper}{device}{inputNames}{ $input_names[1] } =
  178. $input_names[0];
  179. }
  180. }
  181. }
  182. }
  183. return;
  184. }
  185. ###################################
  186. sub PHTV_Get($@) {
  187. my ( $hash, @a ) = @_;
  188. my $name = $hash->{NAME};
  189. my $state = ReadingsVal( $name, "state", "Initialized" );
  190. my $what;
  191. Log3 $name, 5, "PHTV $name: called function PHTV_Get()";
  192. return "argument is missing" if ( int(@a) < 2 );
  193. return if ( $state =~ /^(pairing.*|initialized)$/i );
  194. $what = $a[1];
  195. if ( $what =~ /^(power|input|volume|mute|rgb)$/ ) {
  196. return ReadingsVal( $name, $what, "no such reading: $what" );
  197. }
  198. else {
  199. return
  200. "Unknown argument $what, choose one of power:noArg input:noArg volume:noArg mute:noArg rgb:noArg ";
  201. }
  202. }
  203. ###################################
  204. sub PHTV_Set($@) {
  205. my ( $hash, @a ) = @_;
  206. my $name = $hash->{NAME};
  207. my $state = ReadingsVal( $name, "state", "Initialized" );
  208. my $channel = ReadingsVal( $name, "channel", "" );
  209. my $channels = "";
  210. my $inputs_txt = "";
  211. $hash->{helper}{lastInput} = ReadingsVal( $name, "input", "-" );
  212. my $input = $hash->{helper}{lastInput};
  213. Log3 $name, 5, "PHTV $name: called function PHTV_Set()";
  214. return "No Argument given" unless ( defined( $a[1] ) );
  215. # depending on current FHEMWEB instance's allowedCommands,
  216. # restrict set commands if there is "set-user" in it
  217. my $adminMode = 1;
  218. my $FWallowedCommands = 0;
  219. $FWallowedCommands = AttrVal( $FW_wname, "allowedCommands", 0 )
  220. if ( defined($FW_wname) );
  221. if ( $FWallowedCommands && $FWallowedCommands =~ m/\bset-user\b/ ) {
  222. $adminMode = 0;
  223. return "Forbidden command: set " . $a[1]
  224. if ( lc( $a[1] ) eq "statusrequest" );
  225. }
  226. # Input alias handling
  227. if ( AttrVal( $name, "inputs", "" ) ne "" ) {
  228. my @inputs = split( ':', AttrVal( $name, "inputs", ":" ) );
  229. $inputs_txt = "-," if ( $state ne "on" );
  230. if (@inputs) {
  231. foreach (@inputs) {
  232. if (m/[^,\s]+(,[^,\s]+)+/) {
  233. my @input_names = split( ',', $_ );
  234. $inputs_txt .= $input_names[1] . ",";
  235. $input_names[1] =~ s/\s/_/g;
  236. $hash->{helper}{device}{inputAliases}{ $input_names[0] } =
  237. $input_names[1];
  238. $hash->{helper}{device}{inputNames}{ $input_names[1] } =
  239. $input_names[0];
  240. }
  241. else {
  242. $inputs_txt .= $_ . ",";
  243. }
  244. }
  245. }
  246. $inputs_txt =~ s/\s/_/g;
  247. $inputs_txt = substr( $inputs_txt, 0, -1 );
  248. }
  249. # load channel list
  250. if ( defined( $hash->{helper}{device}{channelPreset} )
  251. && ref( $hash->{helper}{device}{channelPreset} ) eq "HASH" )
  252. {
  253. my $i = 1;
  254. my $count = scalar( keys %{ $hash->{helper}{device}{channelPreset} } );
  255. my $channelsMax = AttrVal( $name, "channelsMax", "80" );
  256. $count = $channelsMax if ( $count > $channelsMax );
  257. while ( $i <= $count ) {
  258. if ( defined( $hash->{helper}{device}{channelPreset}{$i}{name} )
  259. && $hash->{helper}{device}{channelPreset}{$i}{name} ne "" )
  260. {
  261. $channels .=
  262. $hash->{helper}{device}{channelPreset}{$i}{name} . ",";
  263. }
  264. $i++;
  265. }
  266. }
  267. if ( $channel ne ""
  268. && $channels !~ /$channel/ )
  269. {
  270. $channels = $channel . "," . $channels . ",";
  271. }
  272. chop($channels) if ( $channels ne "" );
  273. # create inputList reading for frontends
  274. readingsSingleUpdate( $hash, "inputList", $inputs_txt, 1 )
  275. if ( ReadingsVal( $name, "inputList", "-" ) ne $inputs_txt );
  276. # create channelList reading for frontends
  277. readingsSingleUpdate( $hash, "channelList", $channels, 1 )
  278. if ( ReadingsVal( $name, "channelList", "-" ) ne $channels );
  279. my $usage =
  280. "Unknown argument "
  281. . $a[1]
  282. . ", choose one of toggle:noArg on:noArg off:noArg play:noArg pause:noArg stop:noArg record:noArg volume:slider,1,1,100 volumeUp:noArg volumeDown:noArg channelUp:noArg channelDown:noArg remoteControl ambiHue:off,on ambiMode:internal,manual,expert ambiPreset:rainbow,rainbow-pastel rgb:colorpicker,rgb hue:slider,0,1,65534 sat:slider,0,1,255 pct:slider,0,1,100 bri:slider,0,1,255";
  283. $usage .=
  284. " volumeStraight:slider,"
  285. . $hash->{helper}{audio}{min} . ",1,"
  286. . $hash->{helper}{audio}{max}
  287. if ( defined( $hash->{helper}{audio}{min} )
  288. && defined( $hash->{helper}{audio}{max} ) );
  289. $usage .= " mute:-,on,off"
  290. if ( ReadingsVal( $name, "mute", "" ) eq "-" );
  291. $usage .= " mute:on,off"
  292. if ( ReadingsVal( $name, "mute", "" ) ne "-" );
  293. $usage .= " input:" . $inputs_txt if ( $inputs_txt ne "" );
  294. $usage .= " channel:$channels" if ( $channels ne "" );
  295. if ($adminMode) {
  296. $usage .= " statusRequest:noArg";
  297. }
  298. $usage = "" if ( $state eq "Initialized" );
  299. $usage = "pin" if ( $state =~ /^pairing.*/ );
  300. my $cmd = '';
  301. my $result;
  302. # pairing grant / PIN
  303. if ( lc( $a[1] ) eq "pin" ) {
  304. return "Missing PIN code" unless ( defined( $a[2] ) );
  305. return "Not in pairing mode"
  306. unless ( defined( $hash->{pairing} )
  307. && defined( $hash->{pairing}{auth_key} )
  308. && defined( $hash->{pairing}{timestamp} ) );
  309. readingsSingleUpdate( $hash, "state", "pairing-grant", 1 );
  310. $hash->{pairing}{grant} = {
  311. auth_AppId => 1,
  312. pin => trim( $a[2] ),
  313. auth_timestamp => $hash->{pairing}{timestamp},
  314. auth_signature => PHTV_createAuthSignature(
  315. $hash->{pairing}{timestamp},
  316. $a[2],
  317. "ZmVay1EQVFOaZhwQ4Kv81ypLAZNczV9sG4KkseXWn1NEk6cXmPKO/MCa9sryslvLCFMnNe4Z4CPXzToowvhHvA=="
  318. ),
  319. };
  320. PHTV_SendCommand( $hash, "pair/grant", $hash->{pairing}{grant} );
  321. }
  322. # statusRequest
  323. elsif ( lc( $a[1] ) eq "statusrequest" ) {
  324. Log3 $name, 3, "PHTV set $name " . $a[1];
  325. delete $hash->{helper}{device}
  326. if ( defined( $hash->{helper}{device} ) );
  327. delete $hash->{helper}{supportedAPIcmds}
  328. if ( defined( $hash->{helper}{supportedAPIcmds} ) );
  329. PHTV_GetStatus($hash);
  330. }
  331. # toggle
  332. elsif ( lc( $a[1] ) eq "toggle" ) {
  333. Log3 $name, 3, "PHTV set $name " . $a[1];
  334. if ( ReadingsVal( $name, "state", "off" ) ne "on" ) {
  335. return PHTV_Set( $hash, $name, "on" );
  336. }
  337. else {
  338. return PHTV_Set( $hash, $name, "off" );
  339. }
  340. }
  341. # on
  342. elsif ( lc( $a[1] ) eq "on" ) {
  343. if ( ReadingsVal( $name, "state", "absent" ) eq "absent" ) {
  344. Log3 $name, 3, "PHTV set $name " . $a[1] . " (wakeup)";
  345. my $wakeupCmd = AttrVal( $name, "wakeupCmd", "" );
  346. my $macAddr = AttrVal( $name, "macaddr", "" );
  347. if ( $wakeupCmd ne "" ) {
  348. $wakeupCmd =~ s/\$DEVICE/$name/g;
  349. $wakeupCmd =~ s/\$MACADDR/$macAddr/g;
  350. if ( $wakeupCmd =~ s/^[ \t]*\{|\}[ \t]*$//g ) {
  351. Log3 $name, 4,
  352. "PHTV executing wake-up command (Perl): $wakeupCmd";
  353. $result = eval $wakeupCmd;
  354. }
  355. else {
  356. Log3 $name, 4,
  357. "PHTV executing wake-up command (fhem): $wakeupCmd";
  358. $result = fhem $wakeupCmd;
  359. }
  360. }
  361. elsif ( $macAddr ne "" && $macAddr ne "-" ) {
  362. $hash->{helper}{wakeup} = 1;
  363. PHTV_wake($hash);
  364. RemoveInternalTimer($hash);
  365. InternalTimer( gettimeofday() + 35, "PHTV_GetStatus", $hash,
  366. 0 );
  367. return "wake-up command sent";
  368. }
  369. else {
  370. return
  371. "Attribute macaddr not set. Device needs to be reachable to turn it on.";
  372. }
  373. }
  374. elsif ( ReadingsVal( $name, "state", "off" ) eq "off" ) {
  375. Log3 $name, 3, "PHTV set $name " . $a[1];
  376. $cmd = PHTV_GetRemotecontrolCommand("STANDBY");
  377. PHTV_SendCommand( $hash, "input/key", '"key": "' . $cmd . '"',
  378. "on" );
  379. }
  380. }
  381. # off
  382. elsif ( lc( $a[1] ) eq "off" ) {
  383. Log3 $name, 3, "PHTV set $name " . $a[1];
  384. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  385. $cmd = PHTV_GetRemotecontrolCommand("STANDBY");
  386. PHTV_SendCommand( $hash, "input/key", '"key": "' . $cmd . '"',
  387. "off" );
  388. }
  389. }
  390. # ambiHue
  391. elsif ( lc( $a[1] ) eq "ambihue" ) {
  392. Log3 $name, 3, "PHTV set $name " . $a[1] . " " . $a[2];
  393. return "No argument given" unless ( defined( $a[2] ) );
  394. return "Device does not seem to support Ambilight"
  395. if ( ReadingsVal( $name, "ambiLEDBottom", 0 ) == 0
  396. && ReadingsVal( $name, "ambiLEDLeft", 0 ) == 0
  397. && ReadingsVal( $name, "ambiLEDRight", 0 ) == 0
  398. && ReadingsVal( $name, "ambiLEDTop", 0 ) == 0 );
  399. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  400. if ( lc( $a[2] ) eq "on" ) {
  401. return
  402. "No configuration found. Please set ambiHue attributes first."
  403. unless ( AttrVal( $name, "ambiHueLeft", undef )
  404. || AttrVal( $name, "ambiHueRight", undef )
  405. || AttrVal( $name, "ambiHueTop", undef )
  406. || AttrVal( $name, "ambiHueBottom", undef ) );
  407. # enable internal Ambilight color
  408. PHTV_SendCommand( $hash, "ambilight/mode",
  409. '"current": "internal"', "internal" )
  410. if ( ReadingsVal( $name, "ambiMode", "internal" ) ne
  411. "internal" );
  412. PHTV_SendCommand( $hash, "ambilight/processed", undef, "init" );
  413. }
  414. elsif ( lc( $a[2] ) eq "off" ) {
  415. readingsSingleUpdate( $hash, "ambiHue", $a[2], 1 )
  416. if ( ReadingsVal( $name, "ambiHue", "off" ) ne $a[2] );
  417. }
  418. else {
  419. return "Unknown argument given";
  420. }
  421. }
  422. else {
  423. return "Device needs to be ON to turn on Ambilight+Hue.";
  424. }
  425. }
  426. # ambiMode
  427. elsif ( lc( $a[1] ) eq "ambimode" ) {
  428. Log3 $name, 3, "PHTV set $name " . $a[1] . " " . $a[2];
  429. return "No argument given" unless ( defined( $a[2] ) );
  430. return "Device does not seem to support Ambilight"
  431. if ( ReadingsVal( $name, "ambiLEDBottom", 0 ) == 0
  432. && ReadingsVal( $name, "ambiLEDLeft", 0 ) == 0
  433. && ReadingsVal( $name, "ambiLEDRight", 0 ) == 0
  434. && ReadingsVal( $name, "ambiLEDTop", 0 ) == 0 );
  435. if ( ReadingsVal( $name, "state", "absent" ) ne "absent" ) {
  436. if ( lc( $a[2] ) eq "internal"
  437. || lc( $a[2] ) eq "manual"
  438. || lc( $a[2] ) eq "expert" )
  439. {
  440. PHTV_SendCommand( $hash, "ambilight/mode",
  441. '"current": "' . $a[2] . '"', $a[2] );
  442. readingsSingleUpdate( $hash, "rgb", "000000", 1 )
  443. if ( lc( $a[2] ) eq "internal" );
  444. }
  445. else {
  446. return
  447. "Unknown argument given, choose one of internal manual expert";
  448. }
  449. }
  450. else {
  451. return "Device needs to be reachable to control Ambilight mode.";
  452. }
  453. }
  454. # ambiPreset
  455. elsif ( lc( $a[1] ) eq "ambipreset" ) {
  456. Log3 $name, 3, "PHTV set $name " . $a[1] . " " . $a[2];
  457. return "No argument given" unless ( defined( $a[2] ) );
  458. return "Device does not seem to support Ambilight"
  459. if ( ReadingsVal( $name, "ambiLEDBottom", 0 ) == 0
  460. && ReadingsVal( $name, "ambiLEDLeft", 0 ) == 0
  461. && ReadingsVal( $name, "ambiLEDRight", 0 ) == 0
  462. && ReadingsVal( $name, "ambiLEDTop", 0 ) == 0 );
  463. if ( ReadingsVal( $name, "state", "absent" ) ne "absent" ) {
  464. if ( ReadingsVal( $name, "ambiLEDLayers", undef ) ) {
  465. my $json;
  466. # rainbow
  467. if ( lc( $a[2] ) eq "rainbow" ) {
  468. my $layer = ( $a[3] ) ? $a[3] : 1;
  469. return "Layer $layer is not numeric"
  470. if ( !PHTV_isinteger($layer) );
  471. return "Layer $layer is not existing"
  472. if (
  473. $layer > ReadingsVal( $name, "ambiLEDLayers", undef ) );
  474. while (
  475. $layer <= ReadingsVal( $name, "ambiLEDLayers", undef ) )
  476. {
  477. my $rgb;
  478. foreach my $side ( 'Left', 'Top', 'Right', 'Bottom' ) {
  479. my $ambiLED = "ambiLED$side";
  480. my $side = lc($side);
  481. my $l = "layer" . $layer;
  482. if ( ReadingsVal( $name, $ambiLED, 0 ) > 0 ) {
  483. $rgb = { "r" => 255, "g" => 0, "b" => 0 }
  484. if ( $side eq "left"
  485. || $side eq "right" );
  486. # run clockwise for left and top
  487. if ( $side eq "left" || $side eq "top" ) {
  488. my $led = 0;
  489. while ( $led <=
  490. ReadingsVal( $name, $ambiLED, 0 ) - 1 )
  491. {
  492. $json->{$l}{$side}{$led}{r} =
  493. $rgb->{r};
  494. $json->{$l}{$side}{$led}{g} =
  495. $rgb->{g};
  496. $json->{$l}{$side}{$led}{b} =
  497. $rgb->{b};
  498. if ( $rgb->{r} == 255 ) {
  499. $rgb = {
  500. "r" => 0,
  501. "g" => 255,
  502. "b" => 0
  503. };
  504. }
  505. elsif ( $rgb->{g} == 255 ) {
  506. $rgb = {
  507. "r" => 0,
  508. "g" => 0,
  509. "b" => 255
  510. };
  511. }
  512. elsif ( $rgb->{b} == 255 ) {
  513. $rgb = {
  514. "r" => 255,
  515. "g" => 0,
  516. "b" => 0
  517. };
  518. }
  519. $led++;
  520. }
  521. }
  522. # run anti-clockwise for right and bottom
  523. elsif ($side eq "right"
  524. || $side eq "bottom" )
  525. {
  526. my $led =
  527. ReadingsVal( $name, $ambiLED, 0 ) - 1;
  528. while ( $led >= 0 ) {
  529. $json->{$l}{$side}{$led}{r} =
  530. $rgb->{r};
  531. $json->{$l}{$side}{$led}{g} =
  532. $rgb->{g};
  533. $json->{$l}{$side}{$led}{b} =
  534. $rgb->{b};
  535. if ( $rgb->{r} == 255 ) {
  536. $rgb = {
  537. "r" => 0,
  538. "g" => 255,
  539. "b" => 0
  540. };
  541. }
  542. elsif ( $rgb->{g} == 255 ) {
  543. $rgb = {
  544. "r" => 0,
  545. "g" => 0,
  546. "b" => 255
  547. };
  548. }
  549. elsif ( $rgb->{b} == 255 ) {
  550. $rgb = {
  551. "r" => 255,
  552. "g" => 0,
  553. "b" => 0
  554. };
  555. }
  556. $led--;
  557. }
  558. }
  559. }
  560. }
  561. last if ( defined( $a[3] ) );
  562. $layer++;
  563. }
  564. # enable manual Ambilight color
  565. PHTV_SendCommand( $hash, "ambilight/mode",
  566. '"current": "manual"', "manual" )
  567. if ( ReadingsVal( $name, "ambiMode", "manual" ) ne
  568. "manual" );
  569. }
  570. # rainbow-pastel
  571. elsif ( lc( $a[2] ) eq "rainbow-pastel" ) {
  572. my $layer = ( $a[3] ) ? $a[3] : 1;
  573. return "Layer $layer is not numeric"
  574. if ( !PHTV_isinteger($layer) );
  575. return "Layer $layer is not existing"
  576. if ( $layer > ReadingsVal( $name, "ambiLEDLayers", 0 ) );
  577. PHTV_Set( $hash, $name, "ambiPreset", "rainbow" );
  578. # enable manual Ambilight color
  579. PHTV_SendCommand( $hash, "ambilight/mode",
  580. '"current": "expert"',
  581. "expert", 0.5 )
  582. if ( ReadingsVal( $name, "ambiMode", "expert" ) ne
  583. "expert" );
  584. }
  585. # unknown preset
  586. else {
  587. return "Unknown preset, choose one of rainbow";
  588. }
  589. PHTV_SendCommand( $hash, "ambilight/cached", $json );
  590. }
  591. else {
  592. return "Devices does not seem to support Ambilight.";
  593. }
  594. }
  595. else {
  596. return "Device needs to be reachable to control Ambilight mode.";
  597. }
  598. }
  599. # rgb
  600. elsif ( lc( $a[1] ) eq "rgb" ) {
  601. Log3 $name, 4, "PHTV set $name " . $a[1] . " " . $a[2];
  602. return "No argument given" unless ( defined( $a[2] ) );
  603. return "Device does not seem to support Ambilight"
  604. if ( ReadingsVal( $name, "ambiLEDBottom", 0 ) == 0
  605. && ReadingsVal( $name, "ambiLEDLeft", 0 ) == 0
  606. && ReadingsVal( $name, "ambiLEDRight", 0 ) == 0
  607. && ReadingsVal( $name, "ambiLEDTop", 0 ) == 0 );
  608. if ( ReadingsVal( $name, "state", "absent" ) ne "absent" ) {
  609. # set all LEDs at once
  610. if ( uc( $a[2] ) =~ /^(..)(..)(..)$/ ) {
  611. my $json;
  612. my $hsb;
  613. my $hue;
  614. my $sat;
  615. my $bri;
  616. my $pct;
  617. my ( $r, $g, $b ) = ( hex($1), hex($2), hex($3) );
  618. my $rgbsum = $r + $g + $b;
  619. $json .= '"r": ' . $r . ',';
  620. $json .= '"g": ' . $g . ',';
  621. $json .= '"b": ' . $b;
  622. $hsb = PHTV_rgb2hsb( $r, $g, $b );
  623. $hue = $hsb->{h};
  624. $sat = $hsb->{s};
  625. $bri = $hsb->{b};
  626. $pct = PHTV_bri2pct($bri);
  627. PHTV_SendCommand( $hash, "ambilight/cached", $json,
  628. uc( $a[2] ) );
  629. # enable manual Ambilight color if RGB!=000000
  630. PHTV_SendCommand( $hash, "ambilight/mode",
  631. '"current": "manual"', "manual" )
  632. if (
  633. ReadingsVal( $name, "ambiMode", "internal" ) eq "internal"
  634. && $rgbsum > 0 );
  635. # disable manual Ambilight color if RGB=000000
  636. PHTV_SendCommand( $hash, "ambilight/mode",
  637. '"current": "internal"', "internal" )
  638. if (
  639. ReadingsVal( $name, "ambiMode", "internal" ) ne "internal"
  640. && $rgbsum == 0 );
  641. readingsBeginUpdate($hash);
  642. readingsBulkUpdateIfChanged( $hash, "pct", $pct );
  643. readingsBulkUpdateIfChanged( $hash, "level", $pct . " %" );
  644. readingsBulkUpdateIfChanged( $hash, "hue", $hue );
  645. readingsBulkUpdateIfChanged( $hash, "sat", $sat );
  646. readingsBulkUpdateIfChanged( $hash, "bri", $bri );
  647. readingsBulkUpdateIfChanged( $hash, "rgb", uc( $a[2] ) );
  648. readingsEndUpdate( $hash, 1 );
  649. }
  650. # direct control per LED
  651. elsif ( uc( $a[2] ) =~ /^L[1-9].*/ ) {
  652. my $json;
  653. my $rgbsum = 0;
  654. my $i = 2;
  655. while ( exists( $a[$i] ) ) {
  656. my ( $layer, $side, $led, $rgb );
  657. my ( $addr, $hex ) = split( ':', $a[$i] );
  658. # calculate LED address
  659. $layer = "layer" . substr( $addr, 1, 1 )
  660. if ( length($addr) > 1
  661. && PHTV_isinteger( substr( $addr, 1, 1 ) ) );
  662. if ( length($addr) > 2 ) {
  663. $side = "left" if ( substr( $addr, 2, 1 ) eq "L" );
  664. $side = "top" if ( substr( $addr, 2, 1 ) eq "T" );
  665. $side = "right" if ( substr( $addr, 2, 1 ) eq "R" );
  666. $side = "bottom"
  667. if ( substr( $addr, 2, 1 ) eq "B" );
  668. }
  669. $led = substr( $addr, 3 )
  670. if ( length($addr) > 3
  671. && PHTV_isinteger( substr( $addr, 3 ) ) );
  672. # get desired color
  673. if ( defined($hex) ) {
  674. if ( $hex =~ /^(..)(..)(..)$/ ) {
  675. $rgb = PHTV_hex2rgb($hex);
  676. }
  677. else {
  678. return
  679. "Color "
  680. . $hex
  681. . " for address "
  682. . $addr
  683. . " is not in HEX format";
  684. }
  685. }
  686. else {
  687. return
  688. "Please add color in HEX format for address $addr";
  689. }
  690. # update json hash
  691. if ( defined( $rgb->{r} )
  692. && defined( $rgb->{g} )
  693. && defined( $rgb->{b} ) )
  694. {
  695. $rgbsum += $rgb->{r} + $rgb->{g} + $rgb->{b};
  696. if ( defined($led)
  697. && defined($side)
  698. && defined($layer) )
  699. {
  700. $json->{$layer}{$side}{$led}{r} = $rgb->{r};
  701. $json->{$layer}{$side}{$led}{g} = $rgb->{g};
  702. $json->{$layer}{$side}{$led}{b} = $rgb->{b};
  703. }
  704. elsif ( defined($side) && defined($layer) ) {
  705. $json->{$layer}{$side}{r} = $rgb->{r};
  706. $json->{$layer}{$side}{g} = $rgb->{g};
  707. $json->{$layer}{$side}{b} = $rgb->{b};
  708. }
  709. elsif ( defined($layer) ) {
  710. $json->{$layer}{r} = $rgb->{r};
  711. $json->{$layer}{g} = $rgb->{g};
  712. $json->{$layer}{b} = $rgb->{b};
  713. }
  714. else {
  715. return "Invalid LED address format " . $addr;
  716. }
  717. }
  718. $i++;
  719. }
  720. PHTV_SendCommand( $hash, "ambilight/cached", $json );
  721. # enable manual Ambilight color if RGB!=000000
  722. PHTV_SendCommand( $hash, "ambilight/mode",
  723. '"current": "manual"', "manual" )
  724. if (
  725. ReadingsVal( $name, "ambiMode", "internal" ) eq "internal"
  726. && $rgbsum > 0 );
  727. }
  728. else {
  729. return "Invalid RGB code " . $a[2];
  730. }
  731. }
  732. else {
  733. return "Device needs to be reachable to set Ambilight color.";
  734. }
  735. }
  736. # hue
  737. elsif ( lc( $a[1] ) eq "hue" ) {
  738. Log3 $name, 3, "PHTV set $name " . $a[1] . " " . $a[2];
  739. return "No argument given" unless ( defined( $a[2] ) );
  740. return "Device does not seem to support Ambilight"
  741. if ( ReadingsVal( $name, "ambiLEDBottom", 0 ) == 0
  742. && ReadingsVal( $name, "ambiLEDLeft", 0 ) == 0
  743. && ReadingsVal( $name, "ambiLEDRight", 0 ) == 0
  744. && ReadingsVal( $name, "ambiLEDTop", 0 ) == 0 );
  745. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  746. if ( ReadingsVal( $name, "rgb", "" ) ne "" ) {
  747. my $hsb;
  748. my $hex;
  749. if ( $a[2] =~ m/^\d+$/ && $a[2] >= 0 && $a[2] <= 65535 ) {
  750. $hsb = PHTV_hex2hsb( ReadingsVal( $name, "rgb", "" ) );
  751. $hex = PHTV_hsb2hex( $a[2], $hsb->{s}, $hsb->{b} );
  752. Log3 $name, 4,
  753. "PHTV $name hue - old: "
  754. . ReadingsVal( $name, "rgb", "" )
  755. . " new: $hex(h="
  756. . $a[2] . " s="
  757. . $hsb->{s} . " b="
  758. . $hsb->{b};
  759. return PHTV_Set( $hash, $name, "rgb", $hex );
  760. }
  761. else {
  762. return
  763. "Argument does not seem to be a valid integer between 0 and 100";
  764. }
  765. }
  766. }
  767. else {
  768. return "Device needs to be ON to set Ambilight color.";
  769. }
  770. }
  771. # sat
  772. elsif ( lc( $a[1] ) eq "sat" ) {
  773. Log3 $name, 3, "PHTV set $name " . $a[1] . " " . $a[2];
  774. return "No argument given" unless ( defined( $a[2] ) );
  775. return "Device does not seem to support Ambilight"
  776. if ( ReadingsVal( $name, "ambiLEDBottom", 0 ) == 0
  777. && ReadingsVal( $name, "ambiLEDLeft", 0 ) == 0
  778. && ReadingsVal( $name, "ambiLEDRight", 0 ) == 0
  779. && ReadingsVal( $name, "ambiLEDTop", 0 ) == 0 );
  780. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  781. if ( ReadingsVal( $name, "rgb", "" ) ne "" ) {
  782. my $hsb;
  783. my $hex;
  784. if ( $a[2] =~ m/^\d+$/ && $a[2] >= 0 && $a[2] <= 255 ) {
  785. $hsb = PHTV_hex2hsb( ReadingsVal( $name, "rgb", "" ) );
  786. $hex = PHTV_hsb2hex( $hsb->{h}, $a[2], $hsb->{b} );
  787. Log3 $name, 4,
  788. "PHTV $name sat - old: "
  789. . ReadingsVal( $name, "rgb", "" )
  790. . " new: $hex(h="
  791. . $hsb->{h} . " s="
  792. . $a[2] . " b="
  793. . $hsb->{b};
  794. return PHTV_Set( $hash, $name, "rgb", $hex );
  795. }
  796. else {
  797. return
  798. "Argument does not seem to be a valid integer between 0 and 100";
  799. }
  800. }
  801. }
  802. else {
  803. return "Device needs to be ON to set Ambilight color.";
  804. }
  805. }
  806. # bri
  807. elsif ( lc( $a[1] ) eq "bri" ) {
  808. Log3 $name, 3, "PHTV set $name " . $a[1] . " " . $a[2];
  809. return "No argument given" unless ( defined( $a[2] ) );
  810. return "Device does not seem to support Ambilight"
  811. if ( ReadingsVal( $name, "ambiLEDBottom", 0 ) == 0
  812. && ReadingsVal( $name, "ambiLEDLeft", 0 ) == 0
  813. && ReadingsVal( $name, "ambiLEDRight", 0 ) == 0
  814. && ReadingsVal( $name, "ambiLEDTop", 0 ) == 0 );
  815. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  816. if ( ReadingsVal( $name, "rgb", "" ) ne "" ) {
  817. my $hsb;
  818. my $hex;
  819. if ( $a[2] =~ m/^\d+$/ && $a[2] >= 0 && $a[2] <= 255 ) {
  820. $hsb = PHTV_hex2hsb( ReadingsVal( $name, "rgb", "" ) );
  821. $hex = PHTV_hsb2hex( $hsb->{h}, $hsb->{s}, $a[2] );
  822. Log3 $name, 4,
  823. "PHTV $name bri - old: "
  824. . ReadingsVal( $name, "rgb", "" )
  825. . " new: $hex(h="
  826. . $hsb->{h} . " s="
  827. . $hsb->{s} . " b="
  828. . $a[2] . ")";
  829. return PHTV_Set( $hash, $name, "rgb", $hex );
  830. }
  831. else {
  832. return
  833. "Argument does not seem to be a valid integer between 0 and 100";
  834. }
  835. }
  836. }
  837. else {
  838. return "Device needs to be ON to set Ambilight color.";
  839. }
  840. }
  841. # pct
  842. elsif ( lc( $a[1] ) eq "pct" ) {
  843. Log3 $name, 3, "PHTV set $name " . $a[1] . " " . $a[2];
  844. return "No argument given" unless ( defined( $a[2] ) );
  845. return "Device does not seem to support Ambilight"
  846. if ( ReadingsVal( $name, "ambiLEDBottom", 0 ) == 0
  847. && ReadingsVal( $name, "ambiLEDLeft", 0 ) == 0
  848. && ReadingsVal( $name, "ambiLEDRight", 0 ) == 0
  849. && ReadingsVal( $name, "ambiLEDTop", 0 ) == 0 );
  850. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  851. if ( ReadingsVal( $name, "rgb", "" ) ne "" ) {
  852. my $hsb;
  853. my $bri;
  854. my $hex;
  855. if ( $a[2] =~ m/^\d+$/ && $a[2] >= 0 && $a[2] <= 100 ) {
  856. $hsb = PHTV_hex2hsb( ReadingsVal( $name, "rgb", "" ) );
  857. $bri = PHTV_pct2bri( $a[2] );
  858. $hex = PHTV_hsb2hex( $hsb->{h}, $hsb->{s}, $bri );
  859. Log3 $name, 4,
  860. "PHTV $name pct - old: "
  861. . ReadingsVal( $name, "rgb", "" )
  862. . " new: $hex(h="
  863. . $hsb->{h} . " s="
  864. . $hsb->{s}
  865. . " b=$bri)";
  866. return PHTV_Set( $hash, $name, "rgb", $hex );
  867. }
  868. else {
  869. return
  870. "Argument does not seem to be a valid integer between 0 and 100";
  871. }
  872. }
  873. }
  874. else {
  875. return "Device needs to be ON to set Ambilight color.";
  876. }
  877. }
  878. # volume
  879. elsif ( lc( $a[1] ) eq "volume" ) {
  880. Log3 $name, 3, "PHTV set $name " . $a[1] . " " . $a[2];
  881. return "No argument given" unless ( defined( $a[2] ) );
  882. my $vol;
  883. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  884. if ( $a[2] =~ m/^\d+$/ && $a[2] >= 1 && $a[2] <= 100 ) {
  885. if ( defined( $hash->{helper}{audio}{min} )
  886. && defined( $hash->{helper}{audio}{max} ) )
  887. {
  888. $vol = int(
  889. ( $a[2] / 100 * $hash->{helper}{audio}{max} ) + 0.5 );
  890. }
  891. else {
  892. $vol = $a[2];
  893. }
  894. $cmd = '"current": ' . $vol;
  895. }
  896. else {
  897. return
  898. "Argument does not seem to be a valid integer between 1 and 100";
  899. }
  900. PHTV_SendCommand( $hash, "audio/volume", $cmd );
  901. readingsBeginUpdate($hash);
  902. readingsBulkUpdateIfChanged( $hash, "volume", $a[2] );
  903. readingsBulkUpdateIfChanged( $hash, "volumeStraight", $vol )
  904. if ( defined($vol) );
  905. readingsEndUpdate( $hash, 1 );
  906. }
  907. else {
  908. return "Device needs to be ON to adjust volume.";
  909. }
  910. }
  911. # volumeStraight
  912. elsif ( lc( $a[1] ) eq "volumestraight" ) {
  913. Log3 $name, 3, "PHTV set $name " . $a[1] . " " . $a[2];
  914. return "No argument given" unless ( defined( $a[2] ) );
  915. my $vol;
  916. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  917. if ( $a[2] =~ m/^\d+$/
  918. && $a[2] >= $hash->{helper}{audio}{min}
  919. && $a[2] <= $hash->{helper}{audio}{max} )
  920. {
  921. $vol =
  922. int( ( $a[2] / $hash->{helper}{audio}{max} * 100 ) + 0.5 );
  923. $cmd = '"current": ' . $a[2];
  924. }
  925. else {
  926. return
  927. "Argument does not seem to be a valid integer between "
  928. . $hash->{helper}{audio}{min} . " and "
  929. . $hash->{helper}{audio}{max};
  930. }
  931. PHTV_SendCommand( $hash, "audio/volume", $cmd );
  932. readingsBeginUpdate($hash);
  933. readingsBulkUpdateIfChanged( $hash, "volume", $vol );
  934. readingsBulkUpdateIfChanged( $hash, "volumeStraight", $a[2] );
  935. readingsEndUpdate( $hash, 1 );
  936. }
  937. else {
  938. return "Device needs to be ON to adjust volume.";
  939. }
  940. }
  941. # volumeUp/volumeDown
  942. elsif ( lc( $a[1] ) =~ /^(volumeup|volumedown)$/ ) {
  943. Log3 $name, 3, "PHTV set $name " . $a[1];
  944. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  945. if ( lc( $a[1] ) eq "volumeup" ) {
  946. $cmd = PHTV_GetRemotecontrolCommand("VOLUP");
  947. }
  948. else {
  949. $cmd = PHTV_GetRemotecontrolCommand("VOLDOWN");
  950. }
  951. PHTV_SendCommand( $hash, "input/key", '"key": "' . $cmd . '"',
  952. "volume" );
  953. }
  954. else {
  955. return "Device needs to be ON to adjust volume.";
  956. }
  957. }
  958. # mute
  959. elsif ( lc( $a[1] ) eq "mute" || lc( $a[1] ) eq "mutet" ) {
  960. if ( defined( $a[2] ) ) {
  961. Log3 $name, 3, "PHTV set $name " . $a[1] . " " . $a[2];
  962. }
  963. else {
  964. Log3 $name, 3, "PHTV set $name " . $a[1];
  965. }
  966. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  967. if ( !defined( $a[2] ) || $a[2] eq "toggle" ) {
  968. if ( ReadingsVal( $name, "mute", "" ) eq "off" ) {
  969. $cmd = '"muted": true';
  970. readingsSingleUpdate( $hash, "mute", "on", 1 );
  971. }
  972. elsif ( ReadingsVal( $name, "mute", "" ) eq "on" ) {
  973. $cmd = '"muted": false';
  974. readingsSingleUpdate( $hash, "mute", "off", 1 );
  975. }
  976. }
  977. elsif ( lc( $a[2] ) eq "off" ) {
  978. if ( ReadingsVal( $name, "mute", "" ) ne "off" ) {
  979. $cmd = '"muted": false';
  980. readingsSingleUpdate( $hash, "mute", "off", 1 );
  981. }
  982. }
  983. elsif ( lc( $a[2] ) eq "on" ) {
  984. if ( ReadingsVal( $name, "mute", "" ) ne "on" ) {
  985. $cmd = '"muted": true';
  986. readingsSingleUpdate( $hash, "mute", "on", 1 );
  987. }
  988. }
  989. else {
  990. return "Unknown argument " . $a[2];
  991. }
  992. $result = PHTV_SendCommand( $hash, "audio/volume", $cmd )
  993. if ( $cmd ne "" );
  994. }
  995. else {
  996. return "Device needs to be ON to mute/unmute audio.";
  997. }
  998. }
  999. # remoteControl
  1000. elsif ( lc( $a[1] ) eq "remotecontrol" ) {
  1001. Log3 $name, 3, "PHTV set $name " . $a[1] . " " . $a[2];
  1002. if ( ReadingsVal( $name, "state", "absent" ) ne "absent" ) {
  1003. unless ( defined( $a[2] ) ) {
  1004. my $commandKeys = "";
  1005. for (
  1006. sort keys %{
  1007. PHTV_GetRemotecontrolCommand(
  1008. "GetRemotecontrolCommands")
  1009. }
  1010. )
  1011. {
  1012. $commandKeys = $commandKeys . " " . $_;
  1013. }
  1014. return "No argument given, choose one of" . $commandKeys;
  1015. }
  1016. $cmd = PHTV_GetRemotecontrolCommand( uc( $a[2] ) );
  1017. if ( uc( $a[2] ) eq "MUTE" ) {
  1018. PHTV_Set( $hash, $name, "mute" );
  1019. }
  1020. elsif ( uc( $a[2] ) eq "CHANUP" ) {
  1021. PHTV_Set( $hash, $name, "channelUp" );
  1022. }
  1023. elsif ( uc( $a[2] ) eq "CHANDOWN" ) {
  1024. PHTV_Set( $hash, $name, "channelDown" );
  1025. }
  1026. elsif ( $cmd ne "" ) {
  1027. PHTV_SendCommand( $hash, "input/key", '"key": "' . $cmd . '"' );
  1028. }
  1029. else {
  1030. my $commandKeys = "";
  1031. for (
  1032. sort keys %{
  1033. PHTV_GetRemotecontrolCommand(
  1034. "GetRemotecontrolCommands")
  1035. }
  1036. )
  1037. {
  1038. $commandKeys = $commandKeys . " " . $_;
  1039. }
  1040. return
  1041. "Unknown argument "
  1042. . $a[2]
  1043. . ", choose one of"
  1044. . $commandKeys;
  1045. }
  1046. }
  1047. else {
  1048. return "Device needs to be reachable to be controlled remotely.";
  1049. }
  1050. }
  1051. # channel
  1052. elsif ( lc( $a[1] ) eq "channel" ) {
  1053. if ( defined( $a[2] )
  1054. && ReadingsVal( $name, "presence", "absent" ) eq "present"
  1055. && ReadingsVal( $name, "state", "off" ) ne "on" )
  1056. {
  1057. Log3 $name, 4, "PHTV $name: indirect switching request to ON";
  1058. PHTV_Set( $hash, $name, "on" );
  1059. }
  1060. Log3 $name, 3, "PHTV set $name " . $a[1] . " " . $a[2];
  1061. return
  1062. "No argument given, choose one of channel presetNumber channelName "
  1063. unless ( defined( $a[2] ) );
  1064. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  1065. my $channelName = $a[2];
  1066. if (
  1067. defined( $hash->{helper}{device}{channelID}{$channelName}{id} )
  1068. )
  1069. {
  1070. $cmd = $hash->{helper}{device}{channelID}{$channelName}{id};
  1071. readingsSingleUpdate( $hash, "channel", $channelName, 1 )
  1072. if ( ReadingsVal( $name, "channel", "" ) ne $channelName );
  1073. }
  1074. elsif (
  1075. $channelName =~ /^(\d+):(.*):$/
  1076. && defined(
  1077. $hash->{helper}{device}{channelPreset}{$channelName}{id}
  1078. )
  1079. )
  1080. {
  1081. $cmd =
  1082. $hash->{helper}{device}{channelPreset}{$channelName}{id};
  1083. }
  1084. else {
  1085. return "Argument " . $channelName
  1086. . " is not a valid integer between 0 and 9999 or servicereference is invalid";
  1087. }
  1088. PHTV_SendCommand( $hash, "channels/current",
  1089. '"id": "' . $cmd . '"', $cmd );
  1090. }
  1091. else {
  1092. return
  1093. "Device needs to be reachable to switch to a specific channel.";
  1094. }
  1095. }
  1096. # channelUp/channelDown
  1097. elsif ( lc( $a[1] ) =~ /^(channelup|channeldown)$/ ) {
  1098. Log3 $name, 3, "PHTV set $name " . $a[1];
  1099. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  1100. if ( lc( $a[1] ) eq "channelup" ) {
  1101. $cmd = PHTV_GetRemotecontrolCommand("CHANUP");
  1102. }
  1103. else {
  1104. $cmd = PHTV_GetRemotecontrolCommand("CHANDOWN");
  1105. }
  1106. PHTV_SendCommand( $hash, "input/key", '"key": "' . $cmd . '"',
  1107. "channel" );
  1108. }
  1109. else {
  1110. return "Device needs to be ON to switch channel.";
  1111. }
  1112. }
  1113. # input
  1114. elsif ( lc( $a[1] ) eq "input" ) {
  1115. if ( defined( $a[2] )
  1116. && ReadingsVal( $name, "presence", "absent" ) eq "present"
  1117. && ReadingsVal( $name, "state", "off" ) ne "on" )
  1118. {
  1119. Log3 $name, 4, "PHTV $name: indirect switching request to ON";
  1120. PHTV_Set( $hash, $name, "on" );
  1121. }
  1122. return "No 2nd argument given" unless ( defined( $a[2] ) );
  1123. Log3 $name, 3, "PHTV set $name " . $a[1] . " " . $a[2];
  1124. # Alias handling
  1125. $a[2] = $hash->{helper}{device}{inputNames}{ $a[2] }
  1126. if ( defined( $hash->{helper}{device}{inputNames}{ $a[2] } ) );
  1127. # Resolve input ID name
  1128. my $input_id;
  1129. if ( defined( $hash->{helper}{device}{sourceID}{ $a[2] } ) ) {
  1130. $input_id = $hash->{helper}{device}{sourceID}{ $a[2] };
  1131. }
  1132. elsif ( defined( $hash->{helper}{device}{sourceName}{ $a[2] } ) ) {
  1133. $input_id = $a[2];
  1134. }
  1135. else {
  1136. return "Unknown source input '" . $a[2] . "' on that device.";
  1137. }
  1138. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  1139. PHTV_SendCommand( $hash, "sources/current",
  1140. '"id": "' . $input_id . '"', $input_id );
  1141. readingsSingleUpdate( $hash, "input", $a[2], 1 )
  1142. if ( ReadingsVal( $name, "input", "" ) ne $a[2] );
  1143. }
  1144. else {
  1145. return "Device needs to be reachable to switch input.";
  1146. }
  1147. }
  1148. # play / pause
  1149. elsif ( lc( $a[1] ) =~ /^(play|pause)$/ ) {
  1150. Log3 $name, 3, "PHTV set $name " . $a[1];
  1151. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  1152. $cmd = PHTV_GetRemotecontrolCommand("PLAYPAUSE");
  1153. PHTV_SendCommand( $hash, "input/key", '"key": "' . $cmd . '"' );
  1154. }
  1155. else {
  1156. return "Device needs to be ON to play or pause video.";
  1157. }
  1158. }
  1159. # stop
  1160. elsif ( lc( $a[1] ) eq "stop" ) {
  1161. Log3 $name, 3, "PHTV set $name " . $a[1];
  1162. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  1163. $cmd = PHTV_GetRemotecontrolCommand("STOP");
  1164. PHTV_SendCommand( $hash, "input/key", '"key": "' . $cmd . '"' );
  1165. }
  1166. else {
  1167. return "Device needs to be ON to stop video.";
  1168. }
  1169. }
  1170. # record
  1171. elsif ( lc( $a[1] ) eq "record" ) {
  1172. Log3 $name, 3, "PHTV set $name " . $a[1];
  1173. if ( ReadingsVal( $name, "state", "off" ) eq "on" ) {
  1174. $cmd = PHTV_GetRemotecontrolCommand("RECORD");
  1175. PHTV_SendCommand( $hash, "input/key", '"key": "' . $cmd . '"' );
  1176. }
  1177. else {
  1178. return "Device needs to be ON to start instant recording.";
  1179. }
  1180. }
  1181. # return usage hint
  1182. else {
  1183. return $usage;
  1184. }
  1185. return;
  1186. }
  1187. ###################################
  1188. sub PHTV_Define($$) {
  1189. my ( $hash, $def ) = @_;
  1190. my @a = split( "[ \t][ \t]*", $def );
  1191. my $name = $hash->{NAME};
  1192. Log3 $name, 5, "PHTV $name: called function PHTV_Define()";
  1193. eval {
  1194. require JSON;
  1195. import JSON qw( decode_json encode_json );
  1196. };
  1197. return "Please install Perl JSON to use module PHTV"
  1198. if ($@);
  1199. if ( int(@a) < 3 ) {
  1200. my $msg =
  1201. "Wrong syntax: define <name> PHTV <ip-or-hostname> [<poll-interval>]";
  1202. Log3 $name, 4, $msg;
  1203. return $msg;
  1204. }
  1205. $hash->{TYPE} = "PHTV";
  1206. my $address = $a[2];
  1207. $hash->{helper}{ADDRESS} = $address;
  1208. # use interval of 45sec if not defined
  1209. my $interval = $a[3] || 45;
  1210. $hash->{INTERVAL} = $interval;
  1211. readingsSingleUpdate( $hash, "ambiHue", "off", 0 )
  1212. if ( ReadingsVal( $name, "ambiHue", "" ) ne "off" );
  1213. $hash->{model} = ReadingsVal( $name, "model", undef )
  1214. if ( ReadingsVal( $name, "model", undef ) );
  1215. $hash->{swversion} = ReadingsVal( $name, "softwareversion", undef )
  1216. if ( ReadingsVal( $name, "softwareversion", undef ) );
  1217. # set default settings on first define
  1218. if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
  1219. fhem 'attr ' . $name . ' webCmd volume:input:rgb';
  1220. fhem 'attr ' . $name
  1221. . ' devStateIcon on:rc_GREEN:off off:rc_YELLOW:on absent:rc_STOP:on';
  1222. fhem 'attr ' . $name . ' icon it_television';
  1223. }
  1224. # start the status update timer
  1225. RemoveInternalTimer($hash);
  1226. InternalTimer( gettimeofday() + 2, "PHTV_GetStatus", $hash, 1 );
  1227. return;
  1228. }
  1229. ############################################################################################################
  1230. #
  1231. # Begin of helper functions
  1232. #
  1233. ############################################################################################################
  1234. ###################################
  1235. sub PHTV_SendCommandDelayed($) {
  1236. my ($par) = @_;
  1237. Log3 $par->{hash}->{NAME}, 5,
  1238. "PHTV "
  1239. . $par->{hash}->{NAME}
  1240. . ": called function PHTV_SendCommandDelayed()";
  1241. PHTV_SendCommand( $par->{hash}, $par->{service}, $par->{cmd},
  1242. $par->{type} );
  1243. }
  1244. ###################################
  1245. sub PHTV_SendCommand($$;$$$) {
  1246. my ( $hash, $service, $cmd, $type, $delay ) = @_;
  1247. my $name = $hash->{NAME};
  1248. my $address = $hash->{helper}{ADDRESS};
  1249. my $protoV = AttrVal( $name, "jsversion", 1 );
  1250. my $device_id = AttrVal( $name, "device_id", undef );
  1251. my $auth_key = AttrVal( $name, "auth_key", undef );
  1252. my $timestamp = gettimeofday();
  1253. my $data;
  1254. my $timeout;
  1255. if ( defined($delay) && $delay > 0 ) {
  1256. my %par = (
  1257. hash => $hash,
  1258. service => $service,
  1259. cmd => $cmd,
  1260. type => $type
  1261. );
  1262. InternalTimer( gettimeofday() + $delay,
  1263. "PHTV_SendCommandDelayed", \%par, 0 );
  1264. return;
  1265. }
  1266. if ( defined($cmd) && ref($cmd) eq "HASH" ) {
  1267. $data = encode_json($cmd);
  1268. }
  1269. elsif ( defined($cmd) && $cmd !~ /^{/ ) {
  1270. $data = "{ " . $cmd . " }";
  1271. }
  1272. Log3 $name, 5, "PHTV $name: called function PHTV_SendCommand()";
  1273. my $URL;
  1274. my $response;
  1275. my $return;
  1276. if ( defined( $hash->{helper}{supportedAPIcmds}{$service} )
  1277. && $hash->{helper}{supportedAPIcmds}{$service} == 0 )
  1278. {
  1279. Log3 $name, 5,
  1280. "PHTV $name: API command '" . $service . "' not supported by device.";
  1281. return;
  1282. }
  1283. if ( !defined($data) || ref($cmd) eq "HASH" && $data eq "" ) {
  1284. Log3 $name, 4, "PHTV $name: REQ $service";
  1285. }
  1286. else {
  1287. Log3 $name, 4, "PHTV $name: REQ $service/" . urlDecode($data);
  1288. }
  1289. # add missing port if required
  1290. if ( $address !~ m/^.+:[0-9]+$/ ) {
  1291. $address .= ":1925" if ( $protoV <= 5 );
  1292. $address .= ":1926" if ( $protoV > 5 );
  1293. }
  1294. # special authentication handling during pairing
  1295. $auth_key = undef if ( $service =~ /^pair\/.+/ );
  1296. $auth_key = $hash->{pairing}{auth_key}
  1297. if ( $service eq "pair/grant"
  1298. && defined( $hash->{pairing} )
  1299. && defined( $hash->{pairing}{auth_key} ) );
  1300. $URL = "http://";
  1301. $URL = "https://" if ( $protoV > 5 || $address =~ m/^.+:1926$/ );
  1302. $URL .= "$device_id:$auth_key@" if ( $device_id && $auth_key );
  1303. $URL .= $address . "/" . $protoV . "/" . $service;
  1304. $timeout = AttrVal( $name, "timeout", 7 );
  1305. $timeout = 7 unless ( $timeout =~ /^\d+$/ );
  1306. # send request via HTTP-POST method
  1307. Log3 $name, 5, "PHTV $name: GET " . $URL . " (" . urlDecode($data) . ")"
  1308. if ( defined($data) && ref($cmd) ne "HASH" );
  1309. Log3 $name, 5, "PHTV $name: GET " . $URL . " (#HASH)"
  1310. if ( defined($data) && ref($cmd) eq "HASH" );
  1311. Log3 $name, 5, "PHTV $name: GET " . $URL
  1312. unless ( defined($data) );
  1313. HttpUtils_NonblockingGet(
  1314. {
  1315. url => $URL,
  1316. timeout => $timeout,
  1317. noshutdown => 1,
  1318. data => $data,
  1319. hash => $hash,
  1320. service => $service,
  1321. cmd => $cmd,
  1322. type => $type,
  1323. timestamp => $timestamp,
  1324. httpversion => "1.1",
  1325. callback => \&PHTV_ReceiveCommand,
  1326. header => {
  1327. 'Content-Type' => 'application/json',
  1328. },
  1329. }
  1330. );
  1331. return;
  1332. }
  1333. ###################################
  1334. sub PHTV_ReceiveCommand($$$) {
  1335. my ( $param, $err, $data ) = @_;
  1336. my $hash = $param->{hash};
  1337. my $name = $hash->{NAME};
  1338. my $service = $param->{service};
  1339. my $cmd = $param->{cmd};
  1340. my $code = $param->{code} ? $param->{code} : 0;
  1341. my $sequential = AttrVal( $name, "sequentialQuery", 0 );
  1342. my $protoV = AttrVal( $name, "jsversion", 1 );
  1343. my $device_id = AttrVal( $name, "device_id", undef );
  1344. my $state = ReadingsVal( $name, "state", "" );
  1345. my $newstate = "absent";
  1346. my $type = $param->{type} ? $param->{type} : "";
  1347. my $return;
  1348. Log3 $name, 5, "PHTV $name: called function PHTV_ReceiveCommand()";
  1349. readingsBeginUpdate($hash);
  1350. # pairing request reply
  1351. if ( $service eq "pair/request" ) {
  1352. my $log;
  1353. my $loglevel = 4;
  1354. if ( $data && $data =~ m/^\s*([{\[][\s\S]+[}\]])\s*$/i ) {
  1355. $return = decode_json( Encode::encode_utf8($1) ) if ($1);
  1356. if ( ref($return) eq "HASH"
  1357. && $return->{timestamp}
  1358. && $return->{auth_key}
  1359. && $return->{timeout}
  1360. && $return->{error_id}
  1361. && $return->{error_id} =~ m/^SUCCESS$/i )
  1362. {
  1363. $log = "$code - Pairing enabled";
  1364. $loglevel = 3;
  1365. readingsBulkUpdate( $hash, "state", "pairing" );
  1366. $hash->{PAIRING_BEGIN} = time();
  1367. $hash->{PAIRING_END} =
  1368. $hash->{PAIRING_BEGIN} + $return->{timeout};
  1369. $hash->{pairing}{auth_key} = $return->{auth_key};
  1370. $hash->{pairing}{timeout} = $return->{timeout};
  1371. $hash->{pairing}{timestamp} = $return->{timestamp};
  1372. }
  1373. else {
  1374. $log = "ERROR/$code - Pairing request failed";
  1375. $log .= "\n $err" if ($err);
  1376. $log .= "\n Data:\n$err" if ($data);
  1377. readingsBulkUpdate( $hash, "state", "pairing request failed" );
  1378. }
  1379. }
  1380. else {
  1381. $log = "ERROR/$code - Pairing not supported";
  1382. $log .= "\n $err" if ($err);
  1383. $log .= "\n Data:\n$err" if ($data);
  1384. readingsBulkUpdate( $hash, "state", "pairing not supported" );
  1385. }
  1386. Log3 $name, $loglevel, "PHTV $name: $log";
  1387. readingsEndUpdate( $hash, 1 );
  1388. return;
  1389. }
  1390. # pairing grant reply
  1391. elsif ( $service eq "pair/grant" ) {
  1392. my $log;
  1393. my $loglevel = 4;
  1394. my $interval = 10;
  1395. if ( $data && $data =~ m/^\s*([{\[][\s\S]+[}\]])\s*$/i ) {
  1396. $return = decode_json( Encode::encode_utf8($1) ) if ($1);
  1397. if ( ref($return) eq "HASH"
  1398. && $return->{error_id}
  1399. && $return->{error_id} =~ m/^SUCCESS$/i )
  1400. {
  1401. $log = "$code - Pairing successful";
  1402. $loglevel = 3;
  1403. readingsBulkUpdate( $hash, "state", "paired" );
  1404. fhem 'attr '
  1405. . $name
  1406. . ' auth_key '
  1407. . $hash->{pairing}{auth_key};
  1408. $interval = 3;
  1409. }
  1410. else {
  1411. $log = "ERROR/$code - Pairing failed";
  1412. $log .= "\n $err" if ($err);
  1413. $log .= "\n Data:\n$err" if ($data);
  1414. readingsBulkUpdate( $hash, "state", "pairing failed" );
  1415. }
  1416. }
  1417. else {
  1418. $log = "ERROR/$code - Pairing grant not supported";
  1419. $log .= "\n $err" if ($err);
  1420. $log .= "\n Data:\n$err" if ($data);
  1421. readingsBulkUpdate( $hash, "state", "pairing grant not supported" );
  1422. }
  1423. Log3 $name, $loglevel, "PHTV $name: $log";
  1424. delete $hash->{pairing};
  1425. delete $hash->{PAIRING_BEGIN};
  1426. delete $hash->{PAIRING_END};
  1427. RemoveInternalTimer($hash);
  1428. InternalTimer( gettimeofday() + $interval, "PHTV_GetStatus", $hash, 0 );
  1429. readingsEndUpdate( $hash, 1 );
  1430. return;
  1431. }
  1432. # authorization/pairing needed
  1433. elsif ( $code == 401 ) {
  1434. if ( defined( $hash->{pairing} )
  1435. && defined( $hash->{PAIRING_END} )
  1436. && $hash->{PAIRING_END} < time() )
  1437. {
  1438. readingsEndUpdate( $hash, 1 );
  1439. return;
  1440. }
  1441. readingsBulkUpdateIfChanged( $hash, "presence", "present" );
  1442. readingsBulkUpdateIfChanged( $hash, "power", "on" );
  1443. eval {
  1444. require Digest::SHA;
  1445. import Digest::SHA qw( hmac_sha1_base64 hmac_sha1_hex );
  1446. };
  1447. if ($@) {
  1448. readingsBulkUpdate( $hash, "state",
  1449. "pairing: Missing Perl module Digest::SHA" );
  1450. readingsEndUpdate( $hash, 1 );
  1451. return;
  1452. }
  1453. readingsBulkUpdate( $hash, "state", "pairing-request" );
  1454. fhem 'attr ' . $name . ' jsversion 6'
  1455. unless ( $protoV > 1 );
  1456. unless ($device_id) {
  1457. $device_id = PHTV_createDeviceId();
  1458. fhem 'attr ' . $name . ' device_id ' . $device_id;
  1459. }
  1460. delete $hash->{pairing} if ( defined( $hash->{pairing} ) );
  1461. delete $hash->{PAIRING_BEGIN} if ( defined( $hash->{PAIRING_BEGIN} ) );
  1462. delete $hash->{PAIRING_END} if ( defined( $hash->{PAIRING_END} ) );
  1463. $hash->{pairing}{request} = {
  1464. device_name => 'fhem',
  1465. device_os => 'Android',
  1466. app_name => 'FHEM PHTV',
  1467. type => 'native',
  1468. scope => [ "read", "write", "control" ],
  1469. app_id => 'org.fhem.PHTV',
  1470. id => $device_id,
  1471. };
  1472. PHTV_SendCommand( $hash, "pair/request", $hash->{pairing}{request} );
  1473. readingsEndUpdate( $hash, 1 );
  1474. return;
  1475. }
  1476. # device error
  1477. elsif ( $err && ( !$code || $code ne "200" ) ) {
  1478. my $errtype = "TIMEOUT";
  1479. $errtype = "ERROR/$code" if ($code);
  1480. if ( !defined($cmd) || ref($cmd) eq "HASH" || $cmd eq "" ) {
  1481. Log3 $name, 4, "PHTV $name: RCV $errtype $service" . "\n $err";
  1482. }
  1483. else {
  1484. Log3 $name, 4,
  1485. "PHTV $name: RCV $errtype $service/"
  1486. . urlDecode($cmd)
  1487. . "\n $err";
  1488. }
  1489. # device is not reachable or
  1490. # does not even support master command for audio
  1491. if ( $service eq "audio/volume"
  1492. || ( $service eq "input/key" && $type eq "off" ) )
  1493. {
  1494. $newstate = "off" if ($code);
  1495. readingsBulkUpdateIfChanged( $hash, "presence", "present" )
  1496. if ($code);
  1497. readingsBulkUpdateIfChanged( $hash, "presence", "absent" )
  1498. unless ($code);
  1499. }
  1500. # device behaves naughty
  1501. elsif ( $code || ( $data && $data ne "" ) ) {
  1502. $newstate = "on";
  1503. readingsBulkUpdateIfChanged( $hash, "presence", "present" );
  1504. # because it does not seem to support the command
  1505. unless ( defined( $hash->{helper}{supportedAPIcmds}{$service} ) ) {
  1506. $hash->{helper}{supportedAPIcmds}{$service} = 0;
  1507. Log3 $name, 4,
  1508. "PHTV $name: API command '"
  1509. . $service
  1510. . "' not supported by device.";
  1511. }
  1512. }
  1513. }
  1514. # data received
  1515. elsif ($data) {
  1516. readingsBulkUpdateIfChanged( $hash, "presence", "present" );
  1517. if ( !defined($cmd) || ref($cmd) eq "HASH" | $cmd eq "" ) {
  1518. Log3 $name, 4, "PHTV $name: RCV $service";
  1519. }
  1520. else {
  1521. Log3 $name, 4, "PHTV $name: RCV $service/" . urlDecode($cmd);
  1522. }
  1523. if ( $data =~
  1524. m/^\s*(([{\[][\s\S]+[}\]])|(<html>\s*<head>\s*<title>\s*Ok\s*<\/title>\s*<\/head>\s*<body>\s*Ok\s*<\/body>\s*<\/html>))\s*$/i
  1525. )
  1526. {
  1527. $return = decode_json( Encode::encode_utf8($2) ) if ($2);
  1528. $return = "ok" if ($3);
  1529. if ( !defined($cmd) || ref($cmd) eq "HASH" || $cmd eq "" ) {
  1530. Log3 $name, 5, "PHTV $name: RES $service\n" . $data;
  1531. }
  1532. else {
  1533. Log3 $name, 5,
  1534. "PHTV $name: RES $service/" . urlDecode($cmd) . "\n" . $data;
  1535. }
  1536. $hash->{helper}{supportedAPIcmds}{$service} = 1
  1537. unless ( defined( $hash->{helper}{supportedAPIcmds}{$service} )
  1538. || $service =~ /^channels\/.*/
  1539. || $service =~ /^channellists\/.*/ );
  1540. }
  1541. elsif ( $data ne "" ) {
  1542. if ( !defined($cmd) || ref($cmd) eq "HASH" || $cmd eq "" ) {
  1543. Log3 $name, 5, "PHTV $name: RES ERROR/$code $service\n" . $data;
  1544. }
  1545. else {
  1546. Log3 $name, 5,
  1547. "PHTV $name: RES ERROR/$code $service/"
  1548. . urlDecode($cmd) . "\n"
  1549. . $data;
  1550. }
  1551. unless ( defined( $hash->{helper}{supportedAPIcmds}{$service} ) ) {
  1552. $hash->{helper}{supportedAPIcmds}{$service} = 0;
  1553. Log3 $name, 4,
  1554. "PHTV $name: API command '"
  1555. . $service
  1556. . "' not supported by device.";
  1557. }
  1558. return undef;
  1559. }
  1560. #######################
  1561. # process return data
  1562. #
  1563. $newstate = "on";
  1564. # audio/volume
  1565. if ( $service eq "audio/volume" ) {
  1566. if ( ref($return) eq "HASH" ) {
  1567. # calculate volume
  1568. my $vol = ( $return->{current} ) ? $return->{current} : 0;
  1569. if ( defined( $return->{min} )
  1570. && defined( $return->{max} ) )
  1571. {
  1572. $hash->{helper}{audio}{min} = $return->{min};
  1573. $hash->{helper}{audio}{max} = $return->{max};
  1574. $vol =
  1575. int(
  1576. ( $return->{current} / $return->{max} * 100 ) + 0.5 );
  1577. }
  1578. # volume
  1579. readingsBulkUpdateIfChanged( $hash, "volume", $vol );
  1580. # volumeStraight
  1581. if ( defined( $return->{current} ) ) {
  1582. readingsBulkUpdateIfChanged( $hash, "volumeStraight",
  1583. $return->{current} );
  1584. }
  1585. if ( defined( $return->{muted} ) ) {
  1586. if ( $return->{muted} eq "false" ) {
  1587. readingsBulkUpdateIfChanged( $hash, "mute", "off" );
  1588. }
  1589. elsif ( $return->{muted} eq "true" ) {
  1590. readingsBulkUpdateIfChanged( $hash, "mute", "on" );
  1591. }
  1592. }
  1593. # send on command in case device came up and
  1594. # macaddr attribute is set
  1595. if ( $newstate eq "on"
  1596. && $newstate ne $state
  1597. && defined( $hash->{helper}{wakeup} ) )
  1598. {
  1599. Log3 $name, 4,
  1600. "PHTV $name: Wakeup successful, turning device on";
  1601. delete $hash->{helper}{wakeup};
  1602. $cmd = PHTV_GetRemotecontrolCommand("STANDBY");
  1603. PHTV_SendCommand( $hash, "input/key",
  1604. '"key": "' . $cmd . '"', "on" );
  1605. }
  1606. # trigger query cascade in case the device
  1607. # just came up or sequential query is enabled
  1608. if ( $sequential
  1609. || ( $newstate eq "on" && $newstate ne $state ) )
  1610. {
  1611. # reset API command monitoring
  1612. delete $hash->{helper}{supportedAPIcmds}
  1613. if ( defined( $hash->{helper}{supportedAPIcmds} ) );
  1614. # add some delay if the device just came up
  1615. # and user set attribut for lazy devices
  1616. if ( $newstate eq "on"
  1617. && $newstate ne $state
  1618. && AttrVal( $name, "drippyFactor", -1 ) ge 0 )
  1619. {
  1620. RemoveInternalTimer($hash);
  1621. InternalTimer(
  1622. gettimeofday() +
  1623. AttrVal( $name, "drippyFactor", 0 ),
  1624. "PHTV_GetStatus", $hash, 1
  1625. );
  1626. }
  1627. else {
  1628. PHTV_GetStatus( $hash, 1 );
  1629. }
  1630. }
  1631. }
  1632. }
  1633. # system
  1634. elsif ( $service eq "system" ) {
  1635. if ( ref($return) eq "HASH" ) {
  1636. # language
  1637. if ( defined( $return->{menulanguage} ) ) {
  1638. readingsBulkUpdateIfChanged( $hash, "language",
  1639. $return->{menulanguage} );
  1640. }
  1641. # name
  1642. if ( defined( $return->{name} ) ) {
  1643. readingsBulkUpdateIfChanged( $hash, "systemname",
  1644. $return->{name} );
  1645. }
  1646. # country
  1647. if ( defined( $return->{country} ) ) {
  1648. readingsBulkUpdateIfChanged( $hash, "country",
  1649. $return->{country} );
  1650. }
  1651. # serialnumber
  1652. if ( defined( $return->{serialnumber} ) ) {
  1653. readingsBulkUpdateIfChanged( $hash, "serialnumber",
  1654. $return->{serialnumber} );
  1655. }
  1656. # softwareversion
  1657. if ( defined( $return->{softwareversion} ) ) {
  1658. readingsBulkUpdateIfChanged( $hash, "softwareversion",
  1659. $return->{softwareversion} );
  1660. $hash->{swversion} = $return->{softwareversion};
  1661. }
  1662. # model
  1663. if ( defined( $return->{model} ) ) {
  1664. readingsBulkUpdateIfChanged( $hash, "model",
  1665. uc( $return->{model} ) );
  1666. $hash->{model} = uc( $return->{model} );
  1667. $attr{$name}{model} = uc( $return->{model} );
  1668. }
  1669. }
  1670. # continue query cascade in case sequential query is enabled
  1671. PHTV_GetStatus( $hash, 1 ) if $sequential;
  1672. }
  1673. # sources
  1674. elsif ( $service eq "sources" ) {
  1675. if ( ref($return) eq "HASH" ) {
  1676. # Safe input names
  1677. my $inputs;
  1678. foreach my $input (
  1679. sort
  1680. keys %{$return}
  1681. )
  1682. {
  1683. my $input_name = $return->{$input}{name};
  1684. $input_name =~ s/\s/_/g;
  1685. $hash->{helper}{device}{sourceName}{$input} =
  1686. $input_name;
  1687. $hash->{helper}{device}{sourceID}{$input_name} = $input;
  1688. $inputs .= $input_name . ":";
  1689. }
  1690. unless ( defined( AttrVal( $name, "inputs", undef ) ) ) {
  1691. $inputs = substr( $inputs, 0, -1 );
  1692. $attr{$name}{inputs} = $inputs;
  1693. }
  1694. PHTV_SendCommand( $hash, "sources/current" );
  1695. $hash->{helper}{sequentialQueryCounter}++ if $sequential;
  1696. }
  1697. }
  1698. # sources/current
  1699. elsif ( $service eq "sources/current" ) {
  1700. if ( ref($return) eq "HASH" ) {
  1701. if ( defined( $return->{id} ) ) {
  1702. $return->{id} =~ s/^\s+//;
  1703. $return->{id} =~ s/\s+$//;
  1704. $cmd = (
  1705. $hash->{helper}{device}{sourceName}{ $return->{id} }
  1706. ? $hash->{helper}{device}{sourceName}{ $return->{id} }
  1707. : "-"
  1708. );
  1709. }
  1710. else {
  1711. $cmd = "-";
  1712. }
  1713. # Alias handling
  1714. $cmd = $hash->{helper}{device}{inputAliases}{$cmd}
  1715. if ( defined( $hash->{helper}{device}{inputAliases}{$cmd} ) );
  1716. readingsBulkUpdateIfChanged( $hash, "input", $cmd );
  1717. }
  1718. elsif ( $return eq "ok" ) {
  1719. $cmd =
  1720. ( $hash->{helper}{device}{sourceName}{$type} )
  1721. ? $hash->{helper}{device}{sourceName}{$type}
  1722. : $type;
  1723. # Alias handling
  1724. $cmd = $hash->{helper}{device}{inputAliases}{$cmd}
  1725. if ( defined( $hash->{helper}{device}{inputAliases}{$cmd} ) );
  1726. readingsBulkUpdateIfChanged( $hash, "input", $cmd );
  1727. }
  1728. # SEQUENTIAL QUERY CASCADE - next: channels
  1729. # read all channels if not existing
  1730. if (
  1731. $sequential
  1732. && ( !defined( $hash->{helper}{device}{channelName} )
  1733. || !defined( $hash->{helper}{device}{channelID} ) )
  1734. )
  1735. {
  1736. PHTV_SendCommand( $hash, "channels" );
  1737. $hash->{helper}{sequentialQueryCounter}++;
  1738. }
  1739. # otherwise read current channel
  1740. elsif ($sequential) {
  1741. PHTV_SendCommand( $hash, "channels/current" );
  1742. $hash->{helper}{sequentialQueryCounter}++;
  1743. }
  1744. }
  1745. # channels
  1746. elsif ( $service eq "channels" ) {
  1747. if ( ref($return) eq "HASH" ) {
  1748. # Safe channel names
  1749. foreach my $channel (
  1750. sort
  1751. keys %{$return}
  1752. )
  1753. {
  1754. my $channel_name = $return->{$channel}{name};
  1755. $channel_name =~ s/^\s+//;
  1756. $channel_name =~ s/\s+$//;
  1757. $channel_name =~ s/\s/_/g;
  1758. $channel_name =~ s/,/./g;
  1759. $channel_name =~ s///g;
  1760. if ( $channel_name ne "" ) {
  1761. $hash->{helper}{device}{channelName}{$channel}{name} =
  1762. $channel_name;
  1763. $hash->{helper}{device}{channelName}{$channel}{preset}
  1764. = $return->{$channel}{preset};
  1765. $hash->{helper}{device}{channelID}{$channel_name}{id} =
  1766. $channel;
  1767. $hash->{helper}{device}{channelID}{$channel_name}
  1768. {preset} = $return->{$channel}{preset};
  1769. $hash->{helper}{device}{channelPreset}
  1770. { $return->{$channel}{preset} }{id} = $channel;
  1771. $hash->{helper}{device}{channelPreset}
  1772. { $return->{$channel}{preset} }{name} = $channel_name;
  1773. }
  1774. }
  1775. PHTV_SendCommand( $hash, "channels/current" );
  1776. $hash->{helper}{sequentialQueryCounter}++ if $sequential;
  1777. }
  1778. }
  1779. # channels/current
  1780. elsif ( $service eq "channels/current" ) {
  1781. if ( ref($return) eq "HASH" ) {
  1782. if ( defined( $return->{id} ) ) {
  1783. $return->{id} =~ s/^\s+//;
  1784. $return->{id} =~ s/\s+$//;
  1785. $cmd =
  1786. ( $hash->{helper}{device}{channelName}{ $return->{id} }
  1787. {name} )
  1788. ? $hash->{helper}{device}{channelName}{ $return->{id} }
  1789. {name}
  1790. : "-";
  1791. }
  1792. else {
  1793. $cmd = "-";
  1794. }
  1795. readingsBulkUpdateIfChanged( $hash, "channel", $cmd );
  1796. # read channel details if type is known
  1797. if ( defined( $return->{id} ) && $return->{id} ne "" ) {
  1798. PHTV_SendCommand( $hash, "channels/" . $return->{id} );
  1799. $hash->{helper}{sequentialQueryCounter}++
  1800. if $sequential;
  1801. }
  1802. # read all channellists if not existing
  1803. elsif ( !defined( $hash->{helper}{device}{channellists} ) ) {
  1804. PHTV_SendCommand( $hash, "channellists" );
  1805. $hash->{helper}{sequentialQueryCounter}++
  1806. if $sequential;
  1807. }
  1808. }
  1809. elsif ( $return eq "ok" ) {
  1810. $cmd =
  1811. ( $hash->{helper}{device}{channelName}{$type} )
  1812. ? $hash->{helper}{device}{channelName}{$type}{name}
  1813. : $type;
  1814. readingsBulkUpdateIfChanged( $hash, "channel", $cmd );
  1815. # read channel details if type is known
  1816. if ( defined($type) && $type ne "" ) {
  1817. PHTV_SendCommand( $hash, "channels/" . $type );
  1818. $hash->{helper}{sequentialQueryCounter}++
  1819. if $sequential;
  1820. }
  1821. # read all channellists if not existing
  1822. elsif ( !defined( $hash->{helper}{device}{channellists} ) ) {
  1823. PHTV_SendCommand( $hash, "channellists" );
  1824. $hash->{helper}{sequentialQueryCounter}++
  1825. if $sequential;
  1826. }
  1827. }
  1828. }
  1829. # channels/id
  1830. elsif ( $service =~ /^channels\/.*/ ) {
  1831. if ( ref($return) eq "HASH" ) {
  1832. # currentMedia
  1833. if ( defined( $return->{preset} ) ) {
  1834. readingsBulkUpdateIfChanged( $hash, "currentMedia",
  1835. $return->{preset} );
  1836. }
  1837. else {
  1838. readingsBulkUpdateIfChanged( $hash, "currentMedia", "-" );
  1839. }
  1840. # servicename
  1841. if ( defined( $return->{name} ) ) {
  1842. readingsBulkUpdateIfChanged( $hash, "servicename",
  1843. $return->{name} );
  1844. }
  1845. else {
  1846. readingsBulkUpdateIfChanged( $hash, "servicename", "-" );
  1847. }
  1848. # frequency
  1849. if ( defined( $return->{frequency} ) ) {
  1850. readingsBulkUpdateIfChanged( $hash, "frequency",
  1851. $return->{frequency} );
  1852. }
  1853. else {
  1854. readingsBulkUpdateIfChanged( $hash, "frequency", "-" );
  1855. }
  1856. # onid
  1857. if ( defined( $return->{onid} ) ) {
  1858. readingsBulkUpdateIfChanged( $hash, "onid",
  1859. $return->{onid} );
  1860. }
  1861. else {
  1862. readingsBulkUpdateIfChanged( $hash, "onid", "-" );
  1863. }
  1864. # tsid
  1865. if ( defined( $return->{tsid} ) ) {
  1866. readingsBulkUpdateIfChanged( $hash, "tsid",
  1867. $return->{tsid} );
  1868. }
  1869. else {
  1870. readingsBulkUpdateIfChanged( $hash, "tsid", "-" );
  1871. }
  1872. # sid
  1873. if ( defined( $return->{sid} ) ) {
  1874. readingsBulkUpdateIfChanged( $hash, "sid", $return->{sid} );
  1875. }
  1876. else {
  1877. readingsBulkUpdateIfChanged( $hash, "sid", "-" );
  1878. }
  1879. # receiveMode
  1880. if ( defined( $return->{analog} )
  1881. && defined( $return->{digital} ) )
  1882. {
  1883. my $receiveMode =
  1884. ( $return->{analog} eq "false" )
  1885. ? $return->{digital}
  1886. : "analog";
  1887. readingsBulkUpdateIfChanged( $hash, "receiveMode",
  1888. $receiveMode );
  1889. }
  1890. else {
  1891. readingsBulkUpdateIfChanged( $hash, "receiveMode", "-" );
  1892. }
  1893. }
  1894. # read all channellists if not existing
  1895. unless ( defined( $hash->{helper}{device}{channellists} ) ) {
  1896. PHTV_SendCommand( $hash, "channellists" );
  1897. $hash->{helper}{sequentialQueryCounter}++ if $sequential;
  1898. }
  1899. }
  1900. # channellists
  1901. elsif ( $service eq "channellists" ) {
  1902. if ( ref($return) eq "HASH" ) {
  1903. # request each lists content
  1904. foreach my $item (
  1905. sort
  1906. keys %{$return}
  1907. )
  1908. {
  1909. PHTV_SendCommand( $hash, "channellists/$item", undef,
  1910. $item );
  1911. }
  1912. $hash->{helper}{sequentialQueryCounter}++ if $sequential;
  1913. }
  1914. }
  1915. # channellists/id
  1916. elsif ( $service =~ /^channellists\/.*/ ) {
  1917. if ( ref($return) eq "ARRAY" ) {
  1918. $hash->{helper}{device}{channellists}{$type} = $return;
  1919. }
  1920. }
  1921. # input/key
  1922. elsif ( $service eq "input/key" ) {
  1923. if ( ref($return) ne "HASH" && $return eq "ok" ) {
  1924. # toggle standby
  1925. if ( defined($type) && $type eq "off" ) {
  1926. $newstate = "off";
  1927. }
  1928. # toggle standby
  1929. elsif ( defined($type) && $type eq "on" ) {
  1930. $newstate = "on";
  1931. }
  1932. # volumeUp volumeDown
  1933. elsif ( defined($type) && $type eq "volume" ) {
  1934. PHTV_SendCommand( $hash, "audio/volume" );
  1935. }
  1936. # channelUp channelDown
  1937. elsif ( defined($type) && $type eq "channel" ) {
  1938. PHTV_SendCommand( $hash, "channels/current" );
  1939. }
  1940. }
  1941. }
  1942. # ambilight/topology
  1943. elsif ( $service eq "ambilight/topology" ) {
  1944. if ( ref($return) eq "HASH" ) {
  1945. if ( defined( $return->{layers} ) ) {
  1946. readingsBulkUpdateIfChanged( $hash, "ambiLEDLayers",
  1947. $return->{layers} );
  1948. }
  1949. if ( defined( $return->{left} ) ) {
  1950. readingsBulkUpdateIfChanged( $hash, "ambiLEDLeft",
  1951. $return->{left} );
  1952. }
  1953. if ( defined( $return->{top} ) ) {
  1954. readingsBulkUpdateIfChanged( $hash, "ambiLEDTop",
  1955. $return->{top} );
  1956. }
  1957. if ( defined( $return->{right} ) ) {
  1958. readingsBulkUpdateIfChanged( $hash, "ambiLEDRight",
  1959. $return->{right} );
  1960. }
  1961. if ( defined( $return->{bottom} ) ) {
  1962. readingsBulkUpdateIfChanged( $hash, "ambiLEDBottom",
  1963. $return->{bottom} );
  1964. }
  1965. }
  1966. }
  1967. # ambilight/mode
  1968. elsif ( $service eq "ambilight/mode" ) {
  1969. if ( ref($return) eq "HASH" ) {
  1970. if ( defined( $return->{current} ) ) {
  1971. readingsBulkUpdateIfChanged( $hash, "ambiMode",
  1972. $return->{current} );
  1973. }
  1974. }
  1975. elsif ( $return eq "ok" ) {
  1976. readingsBulkUpdateIfChanged( $hash, "ambiMode", $type );
  1977. }
  1978. # SEQUENTIAL QUERY CASCADE - next: sources
  1979. # read all sources if not existing
  1980. if (
  1981. $sequential
  1982. && ( !defined( $hash->{helper}{device}{sourceName} )
  1983. || !defined( $hash->{helper}{device}{sourceID} ) )
  1984. )
  1985. {
  1986. PHTV_SendCommand( $hash, "sources" );
  1987. $hash->{helper}{sequentialQueryCounter}++;
  1988. }
  1989. # otherwise read current source
  1990. elsif ($sequential) {
  1991. PHTV_SendCommand( $hash, "sources/current" );
  1992. $hash->{helper}{sequentialQueryCounter}++;
  1993. }
  1994. }
  1995. # ambilight/cached (rgb)
  1996. elsif ( $service eq "ambilight/cached" ) {
  1997. if ( ref($return) eq "HASH" ) {
  1998. my $hexsum = "";
  1999. foreach my $layer ( keys %{$return} ) {
  2000. foreach my $side ( keys %{ $return->{$layer} } ) {
  2001. foreach my $led ( keys %{ $return->{$layer}{$side} } ) {
  2002. my $hex = "";
  2003. my $l = $layer;
  2004. my $s = $side;
  2005. $l =~ s/layer/L/;
  2006. $s =~ s/left/L/ if ( $side eq "left" );
  2007. $s =~ s/top/T/ if ( $side eq "top" );
  2008. $s =~ s/right/R/ if ( $side eq "right" );
  2009. $s =~ s/bottom/B/ if ( $side eq "bottom" );
  2010. my $readingname = "rgb_" . $l . $s . $led;
  2011. $hex = PHTV_rgb2hex(
  2012. $return->{$layer}{$side}{$led}{r},
  2013. $return->{$layer}{$side}{$led}{g},
  2014. $return->{$layer}{$side}{$led}{b}
  2015. );
  2016. $hexsum = $hex if ( $hexsum eq "" );
  2017. $hexsum = "diff" if ( $hexsum ne $hex );
  2018. readingsBulkUpdateIfChanged( $hash,
  2019. $readingname, $hex );
  2020. }
  2021. }
  2022. }
  2023. if ( $hexsum ne "diff" ) {
  2024. my $hsb = PHTV_hex2hsb($hexsum);
  2025. my $hue = $hsb->{h};
  2026. my $sat = $hsb->{s};
  2027. my $bri = $hsb->{b};
  2028. my $pct = PHTV_bri2pct($bri);
  2029. readingsBulkUpdateIfChanged( $hash, "rgb", $hexsum );
  2030. readingsBulkUpdateIfChanged( $hash, "hue", $hue );
  2031. readingsBulkUpdateIfChanged( $hash, "sat", $sat );
  2032. readingsBulkUpdateIfChanged( $hash, "bri", $bri );
  2033. readingsBulkUpdateIfChanged( $hash, "pct", $pct );
  2034. readingsBulkUpdateIfChanged( $hash, "level", $pct . " %" );
  2035. }
  2036. }
  2037. elsif ( $return eq "ok" ) {
  2038. if ( $type =~ /^(..)(..)(..)$/
  2039. && ReadingsVal( $name, "ambiLEDLayers", undef ) )
  2040. {
  2041. my $hsb = PHTV_hex2hsb($type);
  2042. my $hue = $hsb->{h};
  2043. my $sat = $hsb->{s};
  2044. my $bri = $hsb->{b};
  2045. my $pct = PHTV_bri2pct($bri);
  2046. readingsBulkUpdateIfChanged( $hash, "rgb", $type );
  2047. readingsBulkUpdateIfChanged( $hash, "hue", $hue );
  2048. readingsBulkUpdateIfChanged( $hash, "sat", $sat );
  2049. readingsBulkUpdateIfChanged( $hash, "bri", $bri );
  2050. readingsBulkUpdateIfChanged( $hash, "pct", $pct );
  2051. readingsBulkUpdateIfChanged( $hash, "level", $pct . " %" );
  2052. if ( ReadingsVal( $name, "ambiLEDLayers", undef ) ) {
  2053. my $layer = 1;
  2054. while ( $layer <=
  2055. ReadingsVal( $name, "ambiLEDLayers", undef ) )
  2056. {
  2057. foreach
  2058. my $side ( 'Left', 'Top', 'Right', 'Bottom' )
  2059. {
  2060. my $ambiLED = "ambiLED$side";
  2061. my $side = lc($side);
  2062. my $l = "L" . $layer;
  2063. my $s = $side;
  2064. $s =~ s/left/L/ if ( $side eq "left" );
  2065. $s =~ s/top/T/ if ( $side eq "top" );
  2066. $s =~ s/right/R/ if ( $side eq "right" );
  2067. $s =~ s/bottom/B/ if ( $side eq "bottom" );
  2068. if ( ReadingsVal( $name, $ambiLED, 0 ) > 0 ) {
  2069. my $led = 0;
  2070. while ( $led <=
  2071. ReadingsVal( $name, $ambiLED, 0 ) - 1 )
  2072. {
  2073. my $readingname =
  2074. "rgb_" . $l . $s . $led;
  2075. readingsBulkUpdateIfChanged( $hash,
  2076. $readingname, $type );
  2077. $led++;
  2078. }
  2079. }
  2080. }
  2081. $layer++;
  2082. }
  2083. }
  2084. }
  2085. }
  2086. }
  2087. # ambilight/processed (ambiHue)
  2088. elsif ( $service eq "ambilight/processed" ) {
  2089. if ( ref($return) eq "HASH" ) {
  2090. readingsBulkUpdate( $hash, "ambiHue", "on" )
  2091. if ( $type eq "init" );
  2092. # run ambiHue
  2093. if (
  2094. (
  2095. ReadingsVal( $name, "ambiHue", "off" ) eq "on"
  2096. || $type eq "init"
  2097. )
  2098. && ( defined( AttrVal( $name, "ambiHueLeft", undef ) )
  2099. || defined( AttrVal( $name, "ambiHueRight", undef ) )
  2100. || defined( AttrVal( $name, "ambiHueTop", undef ) )
  2101. || defined( AttrVal( $name, "ambiHueBottom", undef ) ) )
  2102. )
  2103. {
  2104. my $transitiontime =
  2105. int(
  2106. AttrVal( $name, "ambiHueLatency", 300 ) / 100 + 0.5 );
  2107. $transitiontime = 3 if ( $transitiontime < 3 );
  2108. foreach my $side ( 'Left', 'Top', 'Right', 'Bottom' ) {
  2109. my $ambiHue = "ambiHue$side";
  2110. my $ambiLED = "ambiLED$side";
  2111. my $s = lc($side);
  2112. # $ambiHue
  2113. if ( AttrVal( $name, $ambiHue, "" ) ne ""
  2114. && defined( $return->{layer1}->{$s} )
  2115. && ref( $return->{layer1}->{$s} ) eq "HASH"
  2116. && ReadingsVal( $name, $ambiLED, 0 ) > 0 )
  2117. {
  2118. my @devices =
  2119. split( " ", AttrVal( $name, $ambiHue, "" ) );
  2120. Log3 $name, 5,
  2121. "PHTV $name: processing devices from attribute $ambiHue";
  2122. foreach my $devled (@devices) {
  2123. my ( $dev, $led, $sat, $bri ) =
  2124. split( /:/, $devled );
  2125. my @leds;
  2126. my $logtext =
  2127. "PHTV $name: processing $ambiHue -> $devled -> dev=$dev";
  2128. $logtext .= " led=$led"
  2129. if ( defined($led) );
  2130. $logtext .= " sat=$sat"
  2131. if ( defined($sat) );
  2132. $logtext .= " bri=$bri"
  2133. if ( defined($bri) );
  2134. Log3 $name, 5, $logtext;
  2135. # next for if HUE device is not ready
  2136. if ( !defined( $defs{$dev} )
  2137. || !defined( $defs{$dev}{TYPE} )
  2138. || $defs{$dev}{TYPE} ne "HUEDevice"
  2139. || ReadingsVal( $dev, "reachable", 0 ) ne
  2140. "1" )
  2141. {
  2142. Log3 $name, 5,
  2143. "PHTV $name: $devled seems to be unreachable, skipping it";
  2144. next;
  2145. }
  2146. # determine reference LEDs
  2147. if ( !defined($led) || $led eq "" ) {
  2148. my $led_middle = int(
  2149. ReadingsVal( $name, $ambiLED, 0 ) / 2 +
  2150. 0.5 ) - 1;
  2151. # take the middle LED and
  2152. # one left and right each
  2153. push(
  2154. @leds,
  2155. (
  2156. $led_middle,
  2157. $led_middle - 1,
  2158. $led_middle + 1
  2159. )
  2160. );
  2161. }
  2162. # user named reference LED(s)
  2163. else {
  2164. my ( $ledB, $ledE ) =
  2165. split( /-/, $led );
  2166. $ledB -= 1;
  2167. $ledE -= 1
  2168. if ( defined($ledE) && $ledE ne "" );
  2169. if ( !defined($ledE) || $ledE eq "" ) {
  2170. push( @leds, ($ledB) );
  2171. }
  2172. else {
  2173. my $i = $ledB;
  2174. while ( $i <= $ledE ) {
  2175. push( @leds, ($i) );
  2176. $i++;
  2177. }
  2178. }
  2179. }
  2180. # get current RGB values
  2181. my $Hsum = 0;
  2182. my $Ssum = 0;
  2183. my $Bsum = 0;
  2184. my $countLEDs = 0;
  2185. foreach my $l (@leds) {
  2186. if (
  2187. defined(
  2188. $return->{layer1}->{$s}->{$l}
  2189. )
  2190. )
  2191. {
  2192. Log3 $name, 5,
  2193. "PHTV $name: $devled - getting color from LED $l";
  2194. my $hsb = PHTV_rgb2hsb(
  2195. $return->{layer1}->{$s}->{$l}->{r},
  2196. $return->{layer1}->{$s}->{$l}->{g},
  2197. $return->{layer1}->{$s}->{$l}->{b}
  2198. );
  2199. # only consider color if:
  2200. # - hue color delta <4000
  2201. # - sat&bri >5
  2202. # to avoid huge color skips between LEDs
  2203. if (
  2204. (
  2205. $countLEDs > 0 && abs(
  2206. $Hsum / $countLEDs -
  2207. $hsb->{h}
  2208. ) < 4000
  2209. )
  2210. || $countLEDs == 0
  2211. )
  2212. {
  2213. if (
  2214. (
  2215. $hsb->{s} > 5
  2216. && $hsb->{b} > 5
  2217. )
  2218. || $countLEDs == 0
  2219. )
  2220. {
  2221. Log3 $name, 5,
  2222. "PHTV $name: $devled - LED $l added to sum of $countLEDs";
  2223. $Hsum += $hsb->{h};
  2224. $Ssum += $hsb->{s};
  2225. $Bsum += $hsb->{b};
  2226. $countLEDs++;
  2227. }
  2228. }
  2229. }
  2230. }
  2231. # consider user defined values
  2232. my $satF =
  2233. ( $sat && $sat > 0 && $sat < 100 )
  2234. ? $sat / 100
  2235. : 1;
  2236. my $briF =
  2237. ( $bri && $bri > 0 && $bri < 100 )
  2238. ? $bri / 100
  2239. : 1;
  2240. my ( $hDec, $sDec, $bDec, $h, $s, $b );
  2241. if ( $countLEDs > 0 ) {
  2242. $hDec =
  2243. int( $Hsum / $countLEDs / 256 + 0.5 );
  2244. $sDec =
  2245. int( $Ssum / $countLEDs * $satF + 0.5 );
  2246. $bDec =
  2247. int( $Bsum / $countLEDs * $briF + 0.5 );
  2248. # keep bri=1 if user calc value
  2249. # would be below
  2250. $bDec = 1 if ( $briF < 1 && $bDec < 1 );
  2251. $h = sprintf( "%02x", $hDec );
  2252. $s = sprintf( "%02x", $sDec );
  2253. $b = sprintf( "%02x", $bDec );
  2254. }
  2255. else {
  2256. $hDec = 0;
  2257. $sDec = 0;
  2258. $bDec = 0;
  2259. $h = "00";
  2260. $s = "00";
  2261. $b = "00";
  2262. }
  2263. # temp. disable event triggers for HUEDevice
  2264. unless (
  2265. AttrVal( $dev, "event-on-change-reading",
  2266. "" ) eq "none"
  2267. )
  2268. {
  2269. $attr{$dev}{"event-on-change-reading"} =
  2270. "none";
  2271. }
  2272. # Update color only if there is a
  2273. #significant difference
  2274. my (
  2275. $hMin, $hMax, $hDiff, $sMin, $sMax,
  2276. $sDiff, $bMin, $bMax, $bDiff
  2277. );
  2278. if (
  2279. defined(
  2280. $hash->{helper}{ambiHueColor}{$side}
  2281. )
  2282. )
  2283. {
  2284. $hMin = PHTV_min(
  2285. $hash->{helper}{ambiHueColor}{$side}{h},
  2286. $hDec
  2287. );
  2288. $hMax = PHTV_max(
  2289. $hash->{helper}{ambiHueColor}{$side}{h},
  2290. $hDec
  2291. );
  2292. $hDiff = $hMax - $hMin;
  2293. $sMin = PHTV_min(
  2294. $hash->{helper}{ambiHueColor}{$side}{s},
  2295. $sDec
  2296. );
  2297. $sMax = PHTV_max(
  2298. $hash->{helper}{ambiHueColor}{$side}{s},
  2299. $sDec
  2300. );
  2301. $sDiff = $sMax - $sMin;
  2302. $bMin = PHTV_min(
  2303. $hash->{helper}{ambiHueColor}{$side}{b},
  2304. $bDec
  2305. );
  2306. $bMax = PHTV_max(
  2307. $hash->{helper}{ambiHueColor}{$side}{b},
  2308. $bDec
  2309. );
  2310. $bDiff = $bMax - $bMin;
  2311. }
  2312. if (
  2313. ( $hDec == 0 && $sDec == 0 && $bDec == 0 )
  2314. || ( !defined($hDiff)
  2315. && !defined($sDiff)
  2316. && !defined($bDiff) )
  2317. || $hDiff >= 200
  2318. || $sDiff > 3
  2319. || $bDiff > 2
  2320. )
  2321. {
  2322. Log3 $name, 4,
  2323. "PHTV $name: color changed hDiff=$hDiff sDiff=$sDiff bDiff=$bDiff"
  2324. if ( $hDiff && $sDiff && $bDiff );
  2325. $hash->{helper}{ambiHueColor}{$side}{h} =
  2326. $hDec;
  2327. $hash->{helper}{ambiHueColor}{$side}{s} =
  2328. $sDec;
  2329. $hash->{helper}{ambiHueColor}{$side}{b} =
  2330. $bDec;
  2331. # switch HUE bulb to color
  2332. if ( $b ne "00" ) {
  2333. Log3 $name, 4,
  2334. "PHTV $name: set $dev transitiontime $transitiontime : noUpdate : hsv $h$s$b";
  2335. fhem(
  2336. "set $dev transitiontime $transitiontime : noUpdate : hsv $h$s$b"
  2337. );
  2338. }
  2339. # switch HUE bulb off if brightness is 0
  2340. else {
  2341. Log3 $name, 4,
  2342. "PHTV $name: set $dev transitiontime 5 : noUpdate : off (reason: brightness=$b)";
  2343. fhem(
  2344. "set $dev transitiontime 5 : noUpdate : off"
  2345. );
  2346. }
  2347. }
  2348. }
  2349. }
  2350. }
  2351. my $duration = gettimeofday() - $param->{timestamp};
  2352. my $minLatency =
  2353. AttrVal( $name, "ambiHueLatency", 200 ) / 1000;
  2354. my $waittime = $minLatency - $duration;
  2355. # latency compensation
  2356. if ( $waittime > 0 ) {
  2357. $hash->{helper}{ambiHueDelay} =
  2358. int( ( $duration + $waittime ) * 1000 + 0.5 );
  2359. }
  2360. else {
  2361. $hash->{helper}{ambiHueDelay} =
  2362. int( $duration * 1000 + 0.5 );
  2363. }
  2364. PHTV_SendCommand( $hash, "ambilight/processed", undef,
  2365. undef, $waittime );
  2366. }
  2367. # cleanup after stopping ambiHue
  2368. elsif (
  2369. ReadingsVal( $name, "ambiHue", "off" ) eq "off"
  2370. || ( !defined( AttrVal( $name, "ambiHueLeft", undef ) )
  2371. && !defined( AttrVal( $name, "ambiHueRight", undef ) )
  2372. && !defined( AttrVal( $name, "ambiHueTop", undef ) )
  2373. && !defined( AttrVal( $name, "ambiHueBottom", undef ) )
  2374. )
  2375. )
  2376. {
  2377. delete $hash->{helper}{ambiHueDelay};
  2378. delete $hash->{helper}{ambiHueColor};
  2379. readingsBulkUpdateIfChanged( $hash, "ambiHue", "off" );
  2380. # ambiHueLeft
  2381. if ( AttrVal( $name, "ambiHueLeft", "" ) ne "" ) {
  2382. my @devices =
  2383. split( " ", AttrVal( $name, "ambiHueLeft", "" ) );
  2384. foreach (@devices) {
  2385. my ( $dev, $led ) = split( /:/, $_ );
  2386. $attr{$dev}{"event-on-change-reading"} = ".*";
  2387. fhem(
  2388. "set $dev transitiontime 10 : noUpdate : hsv 000020"
  2389. );
  2390. }
  2391. }
  2392. # ambiHueTop
  2393. if ( AttrVal( $name, "ambiHueTop", "" ) ne "" ) {
  2394. my @devices =
  2395. split( " ", AttrVal( $name, "ambiHueTop", "" ) );
  2396. foreach (@devices) {
  2397. my ( $dev, $led ) = split( /:/, $_ );
  2398. $attr{$dev}{"event-on-change-reading"} = ".*";
  2399. fhem(
  2400. "set $dev transitiontime 10 : noUpdate : hsv 000020"
  2401. );
  2402. }
  2403. }
  2404. # ambiHueRight
  2405. if ( AttrVal( $name, "ambiHueRight", "" ) ne "" ) {
  2406. my @devices =
  2407. split( " ", AttrVal( $name, "ambiHueRight", "" ) );
  2408. foreach (@devices) {
  2409. my ( $dev, $led ) = split( /:/, $_ );
  2410. $attr{$dev}{"event-on-change-reading"} = ".*";
  2411. fhem(
  2412. "set $dev transitiontime 10 : noUpdate : hsv 000020"
  2413. );
  2414. }
  2415. }
  2416. # ambiHueBottom
  2417. if ( AttrVal( $name, "ambiHueBottom", "" ) ne "" ) {
  2418. my @devices =
  2419. split( " ", $attr{$name}{ambiHueBottom} );
  2420. foreach (@devices) {
  2421. my ( $dev, $led ) = split( /:/, $_ );
  2422. $attr{$dev}{"event-on-change-reading"} = ".*";
  2423. fhem(
  2424. "set $dev transitiontime 10 : noUpdate : hsv 000020"
  2425. );
  2426. }
  2427. }
  2428. }
  2429. }
  2430. }
  2431. # all other command results
  2432. else {
  2433. Log3 $name, 2,
  2434. "PHTV $name: ERROR: method to handle response of $service not implemented";
  2435. }
  2436. }
  2437. # Set reading for power
  2438. #
  2439. my $readingPower = "off";
  2440. $readingPower = "on"
  2441. if ( $newstate eq "on" );
  2442. readingsBulkUpdateIfChanged( $hash, "power", $readingPower );
  2443. # Set reading for state
  2444. #
  2445. readingsBulkUpdateIfChanged( $hash, "state", $newstate );
  2446. # Set reading for stateAV
  2447. my $stateAV = PHTV_GetStateAV($hash);
  2448. readingsBulkUpdateIfChanged( $hash, "stateAV", $stateAV );
  2449. # Set PHTV online-only readings to "-" in case box is in
  2450. # offline or in standby mode
  2451. if ( $newstate eq "off"
  2452. || $newstate eq "absent"
  2453. || $newstate eq "undefined" )
  2454. {
  2455. foreach (
  2456. 'mute', 'volume', 'volumeStraight',
  2457. 'input', 'channel', 'currentMedia',
  2458. 'servicename', 'frequency', 'onid',
  2459. 'tsid', 'sid', 'receiveMode',
  2460. )
  2461. {
  2462. readingsBulkUpdateIfChanged( $hash, $_, "-" );
  2463. }
  2464. readingsBulkUpdateIfChanged( $hash, "ambiHue", "off" );
  2465. readingsBulkUpdateIfChanged( $hash, "ambiMode", "internal" );
  2466. readingsBulkUpdateIfChanged( $hash, "rgb", "000000" );
  2467. readingsBulkUpdateIfChanged( $hash, "hue", "0" );
  2468. readingsBulkUpdateIfChanged( $hash, "sat", "0" );
  2469. readingsBulkUpdateIfChanged( $hash, "bri", "0" );
  2470. readingsBulkUpdateIfChanged( $hash, "pct", "0" );
  2471. readingsBulkUpdateIfChanged( $hash, "level", "0 %" );
  2472. if ( ReadingsVal( $name, "ambiLEDLayers", undef ) ) {
  2473. my $layer = 1;
  2474. while ( $layer <= ReadingsVal( $name, "ambiLEDLayers", undef ) ) {
  2475. foreach my $side ( 'Left', 'Top', 'Right', 'Bottom' ) {
  2476. my $ambiLED = "ambiLED$side";
  2477. my $side = lc($side);
  2478. my $l = "L" . $layer;
  2479. my $s = $side;
  2480. $s =~ s/left/L/ if ( $side eq "left" );
  2481. $s =~ s/top/T/ if ( $side eq "top" );
  2482. $s =~ s/right/R/ if ( $side eq "right" );
  2483. $s =~ s/bottom/B/ if ( $side eq "bottom" );
  2484. if ( ReadingsVal( $name, $ambiLED, 0 ) > 0 ) {
  2485. my $led = 0;
  2486. while ( $led <= ReadingsVal( $name, $ambiLED, 0 ) - 1 )
  2487. {
  2488. my $readingname = "rgb_" . $l . $s . $led;
  2489. readingsBulkUpdateIfChanged( $hash,
  2490. $readingname, "000000" );
  2491. $led++;
  2492. }
  2493. }
  2494. }
  2495. $layer++;
  2496. }
  2497. }
  2498. }
  2499. readingsEndUpdate( $hash, 1 );
  2500. Log3 $name, 4,
  2501. "PHTV $name: sequentialQuery - finished round "
  2502. . $hash->{helper}{sequentialQueryCounter}
  2503. if $sequential;
  2504. return;
  2505. }
  2506. ###################################
  2507. sub PHTV_Undefine($$) {
  2508. my ( $hash, $arg ) = @_;
  2509. my $name = $hash->{NAME};
  2510. Log3 $name, 5, "PHTV $name: called function PHTV_Undefine()";
  2511. # Stop the internal GetStatus-Loop and exit
  2512. RemoveInternalTimer($hash);
  2513. return;
  2514. }
  2515. ###################################
  2516. sub PHTV_GetStateAV($) {
  2517. my ($hash) = @_;
  2518. my $name = $hash->{NAME};
  2519. if ( ReadingsVal( $name, "presence", "absent" ) eq "absent" ) {
  2520. return "absent";
  2521. }
  2522. elsif ( ReadingsVal( $name, "power", "off" ) eq "off" ) {
  2523. return "off";
  2524. }
  2525. elsif ( ReadingsVal( $name, "mute", "off" ) eq "on" ) {
  2526. return "muted";
  2527. }
  2528. elsif ( ReadingsVal( $name, "playStatus", "stopped" ) ne "stopped" ) {
  2529. return ReadingsVal( $name, "playStatus", "stopped" );
  2530. }
  2531. else {
  2532. return ReadingsVal( $name, "power", "off" );
  2533. }
  2534. }
  2535. ###################################
  2536. sub PHTV_wake ($) {
  2537. my ($hash) = @_;
  2538. my $name = $hash->{NAME};
  2539. my $mac_addr = AttrVal( $name, "macaddr", "-" );
  2540. my $address = '255.255.255.255';
  2541. my $port = 9;
  2542. if ( $mac_addr ne "-" ) {
  2543. my $sock = new IO::Socket::INET( Proto => 'udp' )
  2544. or die "socket : $!";
  2545. die "Can't create WOL socket" if ( !$sock );
  2546. my $ip_addr = inet_aton($address);
  2547. my $sock_addr = sockaddr_in( $port, $ip_addr );
  2548. $mac_addr =~ s/://g;
  2549. my $packet =
  2550. pack( 'C6H*', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, $mac_addr x 16 );
  2551. setsockopt( $sock, SOL_SOCKET, SO_BROADCAST, 1 )
  2552. or die "setsockopt : $!";
  2553. Log3 $name, 4,
  2554. "PHTV $name: Waking up by sending Wake-On-Lan magic package to "
  2555. . $mac_addr
  2556. . " at IP "
  2557. . $address
  2558. . " port "
  2559. . $port;
  2560. send( $sock, $packet, 0, $sock_addr ) or die "send : $!";
  2561. close($sock);
  2562. }
  2563. else {
  2564. Log3 $name, 3, "PHTV $name: Attribute macaddr not set.";
  2565. }
  2566. return 1;
  2567. }
  2568. #####################################
  2569. # Callback from 95_remotecontrol for command makenotify.
  2570. sub PHTV_RCmakenotify($$) {
  2571. my ( $nam, $ndev ) = @_;
  2572. my $nname = "notify_$nam";
  2573. fhem( "define $nname notify $nam set $ndev remoteControl " . '$EVENT', 1 );
  2574. Log3 undef, 2, "[remotecontrol:PHTV] Notify created: $nname";
  2575. return "Notify created by PHTV: $nname";
  2576. }
  2577. #####################################
  2578. # RC layouts
  2579. # Philips TV with SVG
  2580. sub PHTV_RClayout_SVG() {
  2581. my @row;
  2582. $row[0] = ":rc_BLANK.svg,:rc_BLANK.svg,POWER:rc_POWER.svg";
  2583. $row[1] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg";
  2584. $row[2] = "1:rc_1.svg,2:rc_2.svg,3:rc_3.svg";
  2585. $row[3] = "4:rc_4.svg,5:rc_5.svg,6:rc_6.svg";
  2586. $row[4] = "7:rc_7.svg,8:rc_8.svg,9:rc_9.svg";
  2587. $row[5] = "LEFTBRACE:rc_PREVIOUS.svg,0:rc_0.svg,RIGHTBRACE:rc_NEXT.svg";
  2588. $row[6] =
  2589. "RED:rc_RED.svg,GREEN:rc_GREEN.svg,YELLOW:rc_YELLOW.svg,BLUE:rc_BLUE.svg";
  2590. $row[7] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg";
  2591. $row[8] = "INFO:rc_INFO.svg,UP:rc_UP.svg,MENU:rc_MENU.svg";
  2592. $row[9] = "LEFT:rc_LEFT.svg,OK:rc_OK.svg,RIGHT:rc_RIGHT.svg";
  2593. $row[10] = "AUDIO:rc_AUDIO.svg,DOWN:rc_DOWN.svg,VIDEO:rc_VIDEO.svg";
  2594. $row[11] = ":rc_BLANK.svg,EXIT:rc_EXIT.svg,:rc_BLANK.svg";
  2595. $row[12] = "VOLUP:rc_VOLPLUS.svg,:rc_BLANK.svg,CHANNELUP:rc_UP.svg";
  2596. $row[13] =
  2597. "VOLDOWN:rc_VOLMINUS.svg,MUTE:rc_MUTE.svg,CHANNELDOWN:rc_DOWN.svg";
  2598. $row[14] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg";
  2599. $row[15] =
  2600. "REWIND:rc_REW.svg,PLAY:rc_PLAY.svg,STOP:rc_STOP.svg,FASTFORWARD:rc_FF.svg";
  2601. $row[16] =
  2602. "TV:rc_TV.svg,RADIO:rc_RADIO.svg,TEXT:rc_TEXT.svg,RECORD:rc_REC.svg";
  2603. $row[17] = "attr rc_iconpath icons";
  2604. $row[18] = "attr rc_iconprefix rc_";
  2605. return @row;
  2606. }
  2607. # Philips TV with PNG
  2608. sub PHTV_RClayout() {
  2609. my @row;
  2610. $row[0] = ":blank,:blank,POWER:POWEROFF";
  2611. $row[1] = ":blank,:blank,:blank";
  2612. $row[2] = "1,2,3";
  2613. $row[3] = "4,5,6";
  2614. $row[4] = "7,8,9";
  2615. $row[5] = "LEFTBRACE:LEFT2,0:0,RIGHTBRACE:RIGHT2";
  2616. $row[6] = "RED,GREEN,YELLOW,BLUE";
  2617. $row[7] = ":blank,:blank,:blank";
  2618. $row[8] = "INFO,UP,MENU";
  2619. $row[9] = "LEFT,OK,RIGHT";
  2620. $row[10] = "AUDIO,DOWN,VIDEO";
  2621. $row[11] = ":blank,EXIT,:blank";
  2622. $row[12] = "VOLUP:VOLUP,:blank,CHANNELUP:CHUP2";
  2623. $row[13] = "VOLDOWN:VOLDOWN,MUTE,CHANNELDOWN:CHDOWN2";
  2624. $row[14] = ":blank,:blank,:blank";
  2625. $row[15] = "REWIND,PLAY,STOP,FASTFORWARD:FF";
  2626. $row[16] = "TV,RADIO,TEXT,RECORD:REC";
  2627. $row[17] = "attr rc_iconpath icons/remotecontrol";
  2628. $row[18] = "attr rc_iconprefix black_btn_";
  2629. return @row;
  2630. }
  2631. ###################################
  2632. sub PHTV_GetRemotecontrolCommand($) {
  2633. my ($command) = @_;
  2634. my $commands = {
  2635. 'POWER' => "Standby",
  2636. 'STANDBY' => "Standby",
  2637. 'BACK' => "Back",
  2638. 'EXIT' => "Back",
  2639. 'ESC' => "Back",
  2640. 'FIND' => "Find",
  2641. 'RED' => "RedColour",
  2642. 'GREEN' => "GreenColour",
  2643. 'YELLOW' => "YellowColour",
  2644. 'BLUE' => "BlueColour",
  2645. 'HOME' => "Home",
  2646. 'MENU' => "Home",
  2647. 'VOLUP' => "VolumeUp",
  2648. 'VOLUMEUP' => "VolumeUp",
  2649. 'VOLDOWN' => "VolumeDown",
  2650. 'VOLUMEDOWN' => "VolumeDown",
  2651. 'MUTE' => "Mute",
  2652. 'OPTIONS' => "Options",
  2653. 'DOT' => "Dot",
  2654. '0' => "Digit0",
  2655. '1' => "Digit1",
  2656. '2' => "Digit2",
  2657. '3' => "Digit3",
  2658. '4' => "Digit4",
  2659. '5' => "Digit5",
  2660. '6' => "Digit6",
  2661. '7' => "Digit7",
  2662. '8' => "Digit8",
  2663. '9' => "Digit9",
  2664. 'INFO' => "Info",
  2665. 'UP' => "CursorUp",
  2666. 'DOWN' => "CursorDown",
  2667. 'LEFT' => "CursorLeft",
  2668. 'RIGHT' => "CursorRight",
  2669. 'OK' => "Confirm",
  2670. 'ENTER' => "Confirm",
  2671. 'NEXT' => "Next",
  2672. 'PREVIOUS' => "Previous",
  2673. 'ADJUST' => "Adjust",
  2674. 'TV' => "WatchTV",
  2675. 'MODE' => "Viewmode",
  2676. 'TEXT' => "Teletext",
  2677. 'SUBTITLE' => "Subtitle",
  2678. 'CHANUP' => "ChannelStepUp",
  2679. 'CHANNELUP' => "ChannelStepUp",
  2680. 'CHANDOWN' => "ChannelStepDown",
  2681. 'CHANNELDOWN' => "ChannelStepDown",
  2682. 'SOURCE' => "Source",
  2683. 'AMBI' => "AmbilightOnOff",
  2684. 'PLAYPAUSE' => "PlayPause",
  2685. 'FORWARD' => "FastForward",
  2686. 'STOP' => "Stop",
  2687. 'REWIND' => "Rewind",
  2688. 'RECORD' => "Record",
  2689. 'ONLINE' => "Online",
  2690. };
  2691. if ( defined( $commands->{$command} ) ) {
  2692. return $commands->{$command};
  2693. }
  2694. elsif ( $command eq "GetRemotecontrolCommands" ) {
  2695. return $commands;
  2696. }
  2697. else {
  2698. return "";
  2699. }
  2700. }
  2701. ###################################
  2702. sub PHTV_isinteger {
  2703. defined $_[0] && $_[0] =~ /^[+-]?\d+$/;
  2704. }
  2705. ###################################
  2706. sub PHTV_bri2pct($) {
  2707. my ($bri) = @_;
  2708. return 0 if ( $bri <= 0 );
  2709. return int( $bri / 255 * 100 + 0.5 );
  2710. }
  2711. ###################################
  2712. sub PHTV_pct2bri($) {
  2713. my ($pct) = @_;
  2714. return 0 if ( $pct <= 0 );
  2715. return int( $pct / 100 * 255 + 0.5 );
  2716. }
  2717. ###################################
  2718. sub PHTV_hex2rgb($) {
  2719. my ($hex) = @_;
  2720. if ( uc($hex) =~ /^(..)(..)(..)$/ ) {
  2721. my ( $r, $g, $b ) = ( hex($1), hex($2), hex($3) );
  2722. my $return = { "r" => $r, "g" => $g, "b" => $b };
  2723. Log3 undef, 5,
  2724. "PHTV hex2rgb: $hex > "
  2725. . $return->{r} . " "
  2726. . $return->{g} . " "
  2727. . $return->{b};
  2728. return $return;
  2729. }
  2730. }
  2731. ###################################
  2732. sub PHTV_rgb2hex($$$) {
  2733. my ( $r, $g, $b ) = @_;
  2734. my $return = sprintf( "%2.2X%2.2X%2.2X", $r, $g, $b );
  2735. Log3 undef, 5, "PHTV rgb2hex: $r $g $b > $return";
  2736. return uc($return);
  2737. }
  2738. ###################################
  2739. sub PHTV_hex2hsb($;$) {
  2740. my ( $hex, $type ) = @_;
  2741. $type = lc($type) if ( defined( ($type) && $type ne "" ) );
  2742. my $rgb = PHTV_hex2rgb($hex);
  2743. my $return = PHTV_rgb2hsb( $rgb->{r}, $rgb->{g}, $rgb->{b} );
  2744. if ( defined($type) ) {
  2745. return $return->{h} if ( $type eq "h" );
  2746. return $return->{s} if ( $type eq "s" );
  2747. return $return->{b} if ( $type eq "b" );
  2748. }
  2749. else {
  2750. return $return;
  2751. }
  2752. }
  2753. ###################################
  2754. sub PHTV_hsb2hex($$$) {
  2755. my ( $h, $s, $b ) = @_;
  2756. my $rgb = PHTV_hsb2rgb( $h, $s, $b );
  2757. return PHTV_rgb2hex( $rgb->{r}, $rgb->{g}, $rgb->{b} );
  2758. }
  2759. ###################################
  2760. sub PHTV_rgb2hsb ($$$) {
  2761. my ( $r, $g, $b ) = @_;
  2762. my $r2 = $r / 255.0;
  2763. my $g2 = $g / 255.0;
  2764. my $b2 = $b / 255.0;
  2765. my $hsv = PHTV_rgb2hsv( $r2, $g2, $b2 );
  2766. my $h = int( $hsv->{h} * 65535 );
  2767. my $s = int( $hsv->{s} * 255 );
  2768. my $bri = int( $hsv->{v} * 255 );
  2769. Log3 undef, 5, "PHTV rgb2hsb: $r $g $b > $h $s $bri";
  2770. return {
  2771. "h" => $h,
  2772. "s" => $s,
  2773. "b" => $bri
  2774. };
  2775. }
  2776. ###################################
  2777. sub PHTV_hsb2rgb ($$$) {
  2778. my ( $h, $s, $bri ) = @_;
  2779. my $h2 = $h / 65535.0;
  2780. my $s2 = $s / 255.0;
  2781. my $bri2 = $bri / 255.0;
  2782. my $rgb = PHTV_hsv2rgb( $h2, $s2, $bri2 );
  2783. my $r = int( $rgb->{r} * 255 );
  2784. my $g = int( $rgb->{g} * 255 );
  2785. my $b = int( $rgb->{b} * 255 );
  2786. Log3 undef, 5, "PHTV hsb2rgb: $h $s $bri > $r $g $b";
  2787. return {
  2788. "r" => $r,
  2789. "g" => $g,
  2790. "b" => $b
  2791. };
  2792. }
  2793. ###################################
  2794. sub PHTV_rgb2hsv($$$) {
  2795. my ( $r, $g, $b ) = @_;
  2796. my ( $M, $m, $c, $h, $s, $v );
  2797. $M = PHTV_max( $r, $g, $b );
  2798. $m = PHTV_min( $r, $g, $b );
  2799. $c = $M - $m;
  2800. if ( $c == 0 ) {
  2801. $h = 0;
  2802. }
  2803. elsif ( $M == $r ) {
  2804. $h = ( 60 * ( ( $g - $b ) / $c ) % 360 ) / 360;
  2805. }
  2806. elsif ( $M == $g ) {
  2807. $h = ( 60 * ( ( $b - $r ) / $c ) + 120 ) / 360;
  2808. }
  2809. elsif ( $M == $b ) {
  2810. $h = ( 60 * ( ( $r - $g ) / $c ) + 240 ) / 360;
  2811. }
  2812. if ( $h < 0 ) {
  2813. $h = $h + 1;
  2814. }
  2815. if ( $M == 0 ) {
  2816. $s = 0;
  2817. }
  2818. else {
  2819. $s = $c / $M;
  2820. }
  2821. $v = $M;
  2822. Log3 undef, 5, "PHTV rgb2hsv: $r $g $b > $h $s $v";
  2823. return {
  2824. "h" => $h,
  2825. "s" => $s,
  2826. "v" => $v
  2827. };
  2828. }
  2829. ###################################
  2830. sub PHTV_hsv2rgb($$$) {
  2831. my ( $h, $s, $v ) = @_;
  2832. my $r = 0.0;
  2833. my $g = 0.0;
  2834. my $b = 0.0;
  2835. if ( $s == 0 ) {
  2836. $r = $v;
  2837. $g = $v;
  2838. $b = $v;
  2839. }
  2840. else {
  2841. my $i = int( $h * 6.0 );
  2842. my $f = ( $h * 6.0 ) - $i;
  2843. my $p = $v * ( 1.0 - $s );
  2844. my $q = $v * ( 1.0 - $s * $f );
  2845. my $t = $v * ( 1.0 - $s * ( 1.0 - $f ) );
  2846. $i = $i % 6;
  2847. if ( $i == 0 ) {
  2848. $r = $v;
  2849. $g = $t;
  2850. $b = $p;
  2851. }
  2852. elsif ( $i == 1 ) {
  2853. $r = $q;
  2854. $g = $v;
  2855. $b = $p;
  2856. }
  2857. elsif ( $i == 2 ) {
  2858. $r = $p;
  2859. $g = $v;
  2860. $b = $t;
  2861. }
  2862. elsif ( $i == 3 ) {
  2863. $r = $p;
  2864. $g = $q;
  2865. $b = $v;
  2866. }
  2867. elsif ( $i == 4 ) {
  2868. $r = $t;
  2869. $g = $p;
  2870. $b = $v;
  2871. }
  2872. elsif ( $i == 5 ) {
  2873. $r = $v;
  2874. $g = $p;
  2875. $b = $q;
  2876. }
  2877. }
  2878. Log3 undef, 5, "PHTV hsv2rgb: $h $s $v > $r $g $b";
  2879. return {
  2880. "r" => $r,
  2881. "g" => $g,
  2882. "b" => $b
  2883. };
  2884. }
  2885. ###################################
  2886. sub PHTV_max {
  2887. my ( $max, @vars ) = @_;
  2888. for (@vars) {
  2889. $max = $_
  2890. if $_ > $max;
  2891. }
  2892. return $max;
  2893. }
  2894. ###################################
  2895. sub PHTV_min {
  2896. my ( $min, @vars ) = @_;
  2897. for (@vars) {
  2898. $min = $_ if $_ < $min;
  2899. }
  2900. return $min;
  2901. }
  2902. ###################################
  2903. sub PHTV_createDeviceId() {
  2904. my $deviceid;
  2905. my @chars = ( "A" .. "Z", "a" .. "z", 0 .. 9 );
  2906. $deviceid .= $chars[ rand @chars ] for 1 .. 16;
  2907. return $deviceid;
  2908. }
  2909. ###################################
  2910. sub PHTV_createAuthSignature($$$) {
  2911. my ( $timestamp, $pin, $secretkey ) = @_;
  2912. $secretkey = decode_base64($secretkey) if ( $secretkey =~ m/.*=+$/ );
  2913. my $authsignature = hmac_sha1_hex( $timestamp . trim($pin), $secretkey );
  2914. while ( length($authsignature) % 4 ) {
  2915. $authsignature .= '=';
  2916. }
  2917. return trim( encode_base64($authsignature) ) if ( $secretkey =~ m/.*=+$/ );
  2918. return $authsignature;
  2919. }
  2920. 1;
  2921. =pod
  2922. =item device
  2923. =item summary control for Philips TV devices and their Ambilight via network connection
  2924. =item summary_DE Steuerung von Philips TV Ger&auml;ten und Ambilight &uuml;ber das Netzwerk
  2925. =begin html
  2926. <a name="PHTV"></a>
  2927. <h3>PHTV</h3>
  2928. <ul>
  2929. <a name="PHTVdefine"></a>
  2930. <b>Define</b>
  2931. <ul>
  2932. <code>define &lt;name&gt; PHTV &lt;ip-address-or-hostname&gt; [&lt;poll-interval&gt;]</code>
  2933. <br><br>
  2934. This module controls Philips TV devices and their Ambilight via network connection.<br><br>
  2935. Defining a PHTV device will schedule an internal task (interval can be set
  2936. with optional parameter &lt;poll-interval&gt; in seconds, if not set, the value is 45
  2937. seconds), which periodically reads the status of the device and triggers notify/filelog commands.<br><br>
  2938. Example:<br>
  2939. <ul><code>
  2940. define PhilipsTV PHTV 192.168.0.10
  2941. <br><br>
  2942. # With custom interval of 20 seconds<br>
  2943. define PhilipsTV PHTV 192.168.0.10 20
  2944. </code></ul>
  2945. <br>
  2946. <br>
  2947. <i>Note:</i> Some older devices might need to have the API activated first. If you get no response from your
  2948. device, try to input "5646877223" on the remote while watching TV (which spells jointspace on the digits).
  2949. A popup might appear stating the API was successfully enabled.
  2950. </ul>
  2951. <br>
  2952. <br>
  2953. <a name="PHTVset"></a>
  2954. <b>Set </b>
  2955. <ul>
  2956. <code>set &lt;name&gt; &lt;command&gt; [&lt;parameter&gt;]</code>
  2957. <br><br>
  2958. Currently, the following commands are defined.<br>
  2959. <ul>
  2960. <li><b>on</b> &nbsp;&nbsp;-&nbsp;&nbsp; powers on the device and send a WoL magic package if needed</li>
  2961. <li><b>off</b> &nbsp;&nbsp;-&nbsp;&nbsp; turns the device in standby mode</li>
  2962. <li><b>toggle</b> &nbsp;&nbsp;-&nbsp;&nbsp; switch between on and off</li>
  2963. <li><b>channel</b> channel,0...999,sRef &nbsp;&nbsp;-&nbsp;&nbsp; zap to specific channel or service reference</li>
  2964. <li><b>channelUp</b> &nbsp;&nbsp;-&nbsp;&nbsp; zap to next channel</li>
  2965. <li><b>channelDown</b> &nbsp;&nbsp;-&nbsp;&nbsp; zap to previous channel</li>
  2966. <li><b>volume</b> 0...100 &nbsp;&nbsp;-&nbsp;&nbsp; set the volume level in percentage</li>
  2967. <li><b>volumeStraight</b> 1...60 &nbsp;&nbsp;-&nbsp;&nbsp; set the volume level in device specific range</li>
  2968. <li><b>volumeUp</b> &nbsp;&nbsp;-&nbsp;&nbsp; increases the volume level</li>
  2969. <li><b>volumeDown</b> &nbsp;&nbsp;-&nbsp;&nbsp; decreases the volume level</li>
  2970. <li><b>mute</b> on,off,toggle &nbsp;&nbsp;-&nbsp;&nbsp; controls volume mute</li>
  2971. <li><b>input</b> ... &nbsp;&nbsp;-&nbsp;&nbsp; switches between inputs</li>
  2972. <li><b>statusRequest</b> &nbsp;&nbsp;-&nbsp;&nbsp; requests the current status of the device</li>
  2973. <li><b>remoteControl</b> UP,DOWN,... &nbsp;&nbsp;-&nbsp;&nbsp; sends remote control commands; see remoteControl help</li>
  2974. <li><b>ambiHue</b> on,off &nbsp;&nbsp;-&nbsp;&nbsp; activates/disables Ambilight+Hue function</li>
  2975. <li><b>ambiMode</b> internal,manual,expert &nbsp;&nbsp;-&nbsp;&nbsp; set source register for Ambilight</li>
  2976. <li><b>ambiPreset</b> &nbsp;&nbsp;-&nbsp;&nbsp; set Ambilight to predefined state</li>
  2977. <li><b>rgb</b> HEX,LED address &nbsp;&nbsp;-&nbsp;&nbsp; set an RGB value for Ambilight</li>
  2978. <li><b>hue</b> 0-65534 &nbsp;&nbsp;-&nbsp;&nbsp; set the color hue value Ambilight</li>
  2979. <li><b>sat</b> 0-255 &nbsp;&nbsp;-&nbsp;&nbsp; set the saturation value for Ambilight</li>
  2980. <li><b>bri</b> 0-255 &nbsp;&nbsp;-&nbsp;&nbsp; set the brightness value for Ambilight</li>
  2981. <li><b>play</b> &nbsp;&nbsp;-&nbsp;&nbsp; starts/resumes playback</li>
  2982. <li><b>pause</b> &nbsp;&nbsp;-&nbsp;&nbsp; starts/resumes playback</li>
  2983. <li><b>stop</b> &nbsp;&nbsp;-&nbsp;&nbsp; stops current playback</li>
  2984. <li><b>record</b> &nbsp;&nbsp;-&nbsp;&nbsp; starts recording of current channel</li>
  2985. </ul>
  2986. </ul>
  2987. <ul>
  2988. <u>Note:</u> If you would like to restrict access to admin set-commands (-> statusRequest) you may set your FHEMWEB instance's attribute allowedCommands like 'set,set-user'.
  2989. The string 'set-user' will ensure only non-admin set-commands can be executed when accessing FHEM using this FHEMWEB instance.
  2990. </ul>
  2991. <br>
  2992. <br>
  2993. <ul>
  2994. <u>Advanced Ambilight Control</u><br>
  2995. <br>
  2996. <ul>
  2997. If you would like to specificly control color for individual sides or even individual LEDs, you may use special addressing to be used with set command 'rgb':<br>
  2998. <br><br>
  2999. LED addressing format:<br>
  3000. <code>&lt;Layer&gt;&lt;Side&gt;&lt;LED number&gt;</code>
  3001. <br><br>
  3002. <u>Examples:</u><br>
  3003. <ul>
  3004. <code># set LED 0 on left side within layer 1 to color RED<br>
  3005. set PhilipsTV rgb L1L0:FF0000
  3006. <br><br>
  3007. # set LED 0, 2 and 4 on left side within layer 1 to color RED<br>
  3008. set PhilipsTV rgb L1L0:FF0000 L1L2:FF0000 L1L4:FF0000
  3009. <br><br>
  3010. # set complete right side within layer 1 to color GREEN<br>
  3011. set PhilipsTV rgb L1R:00FF00
  3012. <br><br>
  3013. # set complete layer 1 to color BLUE
  3014. set PhilipsTV rgb L1:0000FF</code>
  3015. </ul><br>
  3016. </ul>
  3017. </ul>
  3018. <br>
  3019. <br>
  3020. <br>
  3021. <br>
  3022. <ul>
  3023. <u>Advanced Ambilight+HUE Control</u><br>
  3024. <br>
  3025. <ul>
  3026. Linking to your HUE devices within attributes ambiHueLeft, ambiHueTop, ambiHueRight and ambiHueBottom uses some defaults to calculate the actual color.<br>
  3027. More than one HUE device may be added using blank.<br>
  3028. The following settings can be fine tuned for each HUE device:<br>
  3029. <br>
  3030. <li>LED(s) to be used as color source<br>
  3031. either 1 single LED or a few in a raw like 2-4. Defaults to use the middle LED and it's left and right partners. Counter starts at 1. See readings ambiLED* for how many LED's your TV has.</li>
  3032. <li>saturation in percent of the original value (1-99, default=100)</li>
  3033. <li>brightness in percent of the original value (1-99, default=100)</li>
  3034. <br><br>
  3035. Use the following addressing format for fine tuning:<br>
  3036. <code>devicename:&lt;LEDs$gt;:&lt;saturation$gt;:&lt;brightness$gt;</code>
  3037. <br><br>
  3038. <u>Examples:</u><br>
  3039. <ul>
  3040. <code># to push color from top to 2 HUE devices<br>
  3041. attr PhilipsTV ambiHueTop HUEDevice0 HUEDevice1
  3042. <br><br>
  3043. # to use only LED 4 from the top as source<br>
  3044. attr PhilipsTV ambiHueTop HUEDevice0:4
  3045. <br><br>
  3046. # to use a combination of LED's 1+2 as source<br>
  3047. attr PhilipsTV ambiHueTop HUEDevice0:1-2
  3048. <br><br>
  3049. # to use LED's 1+2 and only 90% of their saturation<br>
  3050. attr PhilipsTV ambiHueTop HUEDevice0:1-2:90
  3051. <br><br>
  3052. # to use LED's 1+2 and only 50% of their brightness<br>
  3053. attr PhilipsTV ambiHueTop HUEDevice0:1-2::50
  3054. <br><br>
  3055. # to use LED's 1+2, 90% saturation and 50% brightness<br>
  3056. attr PhilipsTV ambiHueTop HUEDevice0:1-2:90:50
  3057. <br><br>
  3058. # to use default LED settings but only adjust their brightness to 50%<br>
  3059. attr PhilipsTV ambiHueTop HUEDevice0:::50</code>
  3060. </ul><br>
  3061. </ul>
  3062. </ul>
  3063. <br>
  3064. <br>
  3065. <a name="PHTVget"></a>
  3066. <b>Get</b>
  3067. <ul>
  3068. <code>get &lt;name&gt; &lt;what&gt;</code>
  3069. <br><br>
  3070. Currently, the following commands are defined:<br><br>
  3071. <ul><code>channel<br>
  3072. mute<br>
  3073. power<br>
  3074. input<br>
  3075. volume<br>
  3076. rgb<br>
  3077. </code></ul>
  3078. </ul>
  3079. <br>
  3080. <br>
  3081. <a name="PHTVattr"></a>
  3082. <b>Attributes</b><br>
  3083. <ul><ul>
  3084. <li><b>ambiHueLeft</b> - HUE devices that should get the color from left Ambilight.</li>
  3085. <li><b>ambiHueTop</b> - HUE devices that should get the color from top Ambilight.</li>
  3086. <li><b>ambiHueRight</b> - HUE devices that should get the color from right Ambilight.</li>
  3087. <li><b>ambiHueBottom</b> - HUE devices that should get the color from bottom Ambilight.</li>
  3088. <li><b>ambiHueLatency</b> - Controls the update interval for HUE devices in milliseconds; defaults to 200 ms.</li>
  3089. <li><b>channelsMax</b> - Maximum amount of channels shown in FHEMWEB. Defaults to 80.</li>
  3090. <li><b>disable</b> - Disable polling (true/false)</li>
  3091. <li><b>drippyFactor</b> - Adds some delay in seconds after low-performance devices came up to allow more time to become responsive (default=0)</li>
  3092. <li><b>inputs</b> - Presents the inputs read from device. Inputs can be renamed by adding <code>,NewName</code> right after the original name.</li>
  3093. <li><b>jsversion</b> - JointSpace protocol version; e.g. pre2014 devices use 1, 2014 devices use 5 and 2015 devices use 6. defaults to 1</li>
  3094. <li><b>sequentialQuery</b> - avoid parallel queries for low-performance devices</li>
  3095. <li><b>timeout</b> - Set different polling timeout in seconds (default=7)</li>
  3096. </ul></ul>
  3097. <br>
  3098. <br>
  3099. <br>
  3100. <b>Generated Readings/Events:</b><br>
  3101. <ul><ul>
  3102. <li><b>ambiHue</b> - Ambilight+Hue status</li>
  3103. <li><b>ambiLEDBottom</b> - Number of LEDs of bottom Ambilight</li>
  3104. <li><b>ambiLEDLayers</b> - Number of physical LED layers</li>
  3105. <li><b>ambiLEDLeft</b> - Number of LEDs of left Ambilight</li>
  3106. <li><b>ambiLEDRight</b> - Number of LEDs of right Ambilight</li>
  3107. <li><b>ambiLEDTop</b> - Number of LEDs of top Ambilight</li>
  3108. <li><b>ambiMode</b> - current Ambilight color source</li>
  3109. <li><b>channel</b> - Shows the service name of current channel; part of FHEM-4-AV-Devices compatibility</li>
  3110. <li><b>country</b> - Set country</li>
  3111. <li><b>currentMedia</b> - The preset number of this channel; part of FHEM-4-AV-Devices compatibility</li>
  3112. <li><b>frequency</b> - Shows current channels frequency</li>
  3113. <li><b>input</b> - Shows currently used input; part of FHEM-4-AV-Devices compatibility</li>
  3114. <li><b>language</b> - Set menu language</li>
  3115. <li><b>model</b> - Device model</li>
  3116. <li><b>mute</b> - Reports the mute status of the device (can be "on" or "off")</li>
  3117. <li><b>onid</b> - The ON ID</li>
  3118. <li><b>power</b> - Reports the power status of the device (can be "on" or "off")</li>
  3119. <li><b>presence</b> - Reports the presence status of the receiver (can be "absent" or "present"). In case of an absent device, control is basically limited to turn it on again. This will only work if the device supports Wake-On-LAN packages, otherwise command "on" will have no effect.</li>
  3120. <li><b>receiveMode</b> - Receiving mode (analog or DVB)</li>
  3121. <li><b>rgb</b> - Current Ambilight color if ambiMode is not set to internal and all LEDs have the same color</li>
  3122. <li><b>rgb_X</b> - Current Ambilight color of a specific LED if ambiMode is not set to internal</li>
  3123. <li><b>serialnumber</b> - Device serial number</li>
  3124. <li><b>servicename</b> - Name for current channel</li>
  3125. <li><b>sid</b> - The S-ID</li>
  3126. <li><b>state</b> - Reports current power state and an absence of the device (can be "on", "off" or "absent")</li>
  3127. <li><b>systemname</b> - Device system name</li>
  3128. <li><b>tsid</b> - The TS ID</li>
  3129. <li><b>volume</b> - Reports current volume level of the receiver in percentage values (between 0 and 100 %)</li>
  3130. <li><b>volumeStraight</b> - Reports current volume level of the receiver in device specific range</li>
  3131. </ul></ul>
  3132. </ul>
  3133. =end html
  3134. =begin html_DE
  3135. <a name="PHTV"></a>
  3136. <h3>PHTV</h3>
  3137. <ul>
  3138. Eine deutsche Version der Dokumentation ist derzeit nicht vorhanden.
  3139. Die englische Version ist hier zu finden:
  3140. </ul>
  3141. <ul>
  3142. <a href='http://fhem.de/commandref.html#PHTV'>PHTV</a>
  3143. </ul>
  3144. =end html_DE
  3145. =cut