75_LGTV_RS232.pm 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. ##############################################
  2. # $Id: 75_LGTV_RS232.pm 9954 2015-11-21 13:58:15Z markusbloch $
  3. #
  4. # created by Markus Bloch (Notausstieg0309@googlemail.com)
  5. #
  6. # This modules controls LG Smart TV's which are connected via
  7. # a USB to Serial converter to FHEM.
  8. #
  9. # Detailed Information about the hardware setup and more possible control commands: http://www.lgrs232.com/
  10. #
  11. # This modules is able to switch the input channels, which
  12. # is not possible via the ethernet interface.
  13. #
  14. # Define: define TV LGTV_RS232 /dev/ttyUSB0
  15. #
  16. # Set: statusRequest input:hmdi1,hdmi2, ...
  17. #
  18. #
  19. #
  20. package main;
  21. use strict;
  22. use warnings;
  23. use Time::HiRes qw(gettimeofday);
  24. use DevIo;
  25. my %LGTV_RS232_response = (
  26. "a" => {
  27. "power" => {"00" => "off",
  28. "01" => "on"
  29. },
  30. "state" => {"00" => "off",
  31. "01" => "on"
  32. }
  33. },
  34. "b" => {
  35. "input" => {"00" => "digitalTV",
  36. "01" => "analogueTV",
  37. "02" => "video1",
  38. "03" => "video2",
  39. "04" => "component1",
  40. "05" => "component2",
  41. "06" => "rgbDTV",
  42. "07" => "rgbPC",
  43. "08" => "hdmi1",
  44. "09" => "hdmi2"
  45. }
  46. },
  47. "c" => {
  48. "aspectRatio" => { "01" => "normal",
  49. "02" => "wide",
  50. "03" => "horizon",
  51. "04" => "zoom1",
  52. "05" => "zoom2",
  53. "06" => "auto",
  54. "07" => "14:9",
  55. "08" => "full",
  56. "09" => "justScan",
  57. "0a" => "zoom3",
  58. "0b" => "fullWide",
  59. "10" => "cinemaZoom1",
  60. "11" => "cinemaZoom16"
  61. }
  62. },
  63. "d" => {
  64. "screenMute" => {"00" => "off",
  65. "01" => "on"
  66. },
  67. "videoOutMute" => {"00" => "off",
  68. "10" => "on"
  69. }
  70. },
  71. "e" => {
  72. "volumeMute" => {"00" => "on",
  73. "01" => "off"
  74. }
  75. }
  76. );
  77. my %LGTV_RS232_set = (
  78. "input" => {
  79. "digitalTV" => "kb 01 00",
  80. "video1" => "kb 01 02",
  81. "video2" => "kb 01 03",
  82. "component" => "kb 01 04",
  83. "hdmi1" => "kb 01 08",
  84. "hdmi2" => "kb 01 09",
  85. },
  86. "power" => {
  87. "on" => "ka 01 01",
  88. "off" => "ka 01 00",
  89. }
  90. );
  91. #####################################
  92. sub
  93. LGTV_RS232_Initialize($)
  94. {
  95. my ($hash) = @_;
  96. $hash->{DefFn} = "LGTV_RS232_Define";
  97. $hash->{UndefFn} = "LGTV_RS232_Undef";
  98. $hash->{SetFn} = "LGTV_RS232_Set";
  99. $hash->{ReadFn} = "LGTV_RS232_Read";
  100. $hash->{ReadyFn} = "LGTV_RS232_Ready";
  101. $hash->{AttrList} = " ".$readingFnAttributes;
  102. }
  103. #####################################
  104. sub
  105. LGTV_RS232_Define($$)
  106. {
  107. my ($hash, $def) = @_;
  108. my @a = split("[ \t][ \t]*", $def);
  109. my $name = $a[0];
  110. my $dev = $a[2];
  111. $hash->{helper}{RECEIVE_BUFFER} = "";
  112. $dev .= "\@9600" if(not $dev =~ m/\@\d+/);
  113. $hash->{DeviceName} = $dev;
  114. DevIo_CloseDev($hash);
  115. my $ret = DevIo_OpenDev($hash, 0, undef);
  116. delete($hash->{PARTIAL});
  117. RemoveInternalTimer($hash);
  118. LGTV_RS232_GetStatus($hash);
  119. return undef;
  120. }
  121. #####################################
  122. sub
  123. LGTV_RS232_Undef($$)
  124. {
  125. my ($hash, $arg) = @_;
  126. DevIo_CloseDev($hash);
  127. return undef;
  128. }
  129. #####################################
  130. sub
  131. LGTV_RS232_Set($@)
  132. {
  133. my ($hash, @a) = @_;
  134. my $what = $a[1];
  135. my $usage = "Unknown argument $what, choose one of statusRequest";
  136. foreach my $cmd (sort keys %LGTV_RS232_set)
  137. {
  138. $usage .= " $cmd:".join(",", sort keys %{$LGTV_RS232_set{$cmd}});
  139. }
  140. if($what eq "statusRequest")
  141. {
  142. LGTV_RS232_GetStatus($hash, 1);
  143. }
  144. elsif(exists($LGTV_RS232_set{$what}) and exists($LGTV_RS232_set{$what}{$a[2]}))
  145. {
  146. DevIo_SimpleWrite($hash, $LGTV_RS232_set{$what}{$a[2]}."\n", 0);
  147. }
  148. else
  149. {
  150. return $usage;
  151. }
  152. }
  153. #####################################
  154. # called from the global loop, when the select for hash->{FD} reports data
  155. sub
  156. LGTV_RS232_Read($)
  157. {
  158. my ($hash) = @_;
  159. my $buf = DevIo_SimpleRead($hash);
  160. return "" if(!defined($buf));
  161. my $name = $hash->{NAME};
  162. my $partial = $hash->{helper}{RECEIVE_BUFFER};
  163. Log3 $name, 5, "LGTV_RS232 ($name) - ".($partial ne "" ? "(buffer contains: $partial) " : "")."received: $buf";
  164. $partial .= $buf;
  165. while($partial =~ /(\w\s\d{2}\s[0-9a-zA-Z]+?x)(.*)/)
  166. {
  167. my $msg = $1;
  168. $partial = $2;
  169. $msg =~ s/x$//;
  170. LGTV_RS232_ParseResponse($hash, $msg);
  171. }
  172. $hash->{helper}{RECEIVE_BUFFER} = $partial;
  173. RemoveInternalTimer($hash);
  174. InternalTimer(gettimeofday()+15, "LGTV_RS232_GetStatus", $hash, 0);
  175. }
  176. #####################################
  177. # receives incoming data
  178. sub
  179. LGTV_RS232_Ready($)
  180. {
  181. my ($hash) = @_;
  182. return DevIo_OpenDev($hash, 1, undef) if($hash->{STATE} eq "disconnected");
  183. }
  184. sub
  185. LGTV_RS232_ParseResponse($$)
  186. {
  187. my ($hash, $msg) = @_;
  188. my $name = $hash->{NAME};
  189. Log3 $name, 4, "LGTV_RS232 ($name) - processing response: ".$msg;
  190. my ($code, $glue, $result, $val) = unpack("A2 A3 A2 A*", $msg);
  191. Log3 $name, 5, "LGTV_RS232 ($name) - processed code: $code - glue: $glue - result: $result - val: $val";
  192. readingsBeginUpdate($hash);
  193. if($result eq "OK")
  194. {
  195. readingsBulkUpdate($hash, "CommandAccepted", "yes");
  196. }
  197. elsif($result eq "NG")
  198. {
  199. readingsBulkUpdate($hash, "CommandAccepted", "no");
  200. return;
  201. }
  202. foreach my $reading (keys %{$LGTV_RS232_response{$code}})
  203. {
  204. if(exists($LGTV_RS232_response{$code}{$reading}{$val}))
  205. {
  206. readingsBulkUpdate($hash, $reading, $LGTV_RS232_response{$code}{$reading}{$val});
  207. }
  208. }
  209. if($code eq "f")
  210. {
  211. readingsBulkUpdate($hash, "volume", hex($val)." %");
  212. }
  213. readingsBulkUpdate($hash, "presence", "present");
  214. readingsEndUpdate($hash, 1);
  215. }
  216. #####################################
  217. # request the current state
  218. sub
  219. LGTV_RS232_GetStatus($;$)
  220. {
  221. my ($hash, $local) = @_;
  222. DevIo_SimpleWrite($hash, "ka 01 ff\n", 0);
  223. DevIo_SimpleWrite($hash, "kb 01 ff\n", 0);
  224. DevIo_SimpleWrite($hash, "kc 01 ff\n", 0);
  225. DevIo_SimpleWrite($hash, "kd 01 ff\n", 0);
  226. DevIo_SimpleWrite($hash, "kf 01 ff\n", 0);
  227. DevIo_SimpleWrite($hash, "ke 01 ff\n", 0);
  228. RemoveInternalTimer($hash);
  229. InternalTimer(gettimeofday()+2, "LGTV_RS232_TimeOut", $hash, 0);
  230. }
  231. #####################################
  232. # Is executed when a request via serial connection was not answered
  233. sub
  234. LGTV_RS232_TimeOut($)
  235. {
  236. my ($hash) = @_;
  237. readingsBeginUpdate($hash);
  238. readingsBulkUpdate($hash, "presence", "absent");
  239. readingsBulkUpdate($hash, "state", "off");
  240. readingsEndUpdate($hash, 1);
  241. RemoveInternalTimer($hash);
  242. InternalTimer(gettimeofday()+15, "LGTV_RS232_GetStatus", $hash, 0);
  243. }
  244. 1;