37_NotifyAndroidTV.pm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. # $Id: 37_NotifyAndroidTV.pm 16138 2018-02-09 20:36:36Z justme1968 $
  2. package main;
  3. use strict;
  4. use warnings;
  5. use HttpUtils;
  6. use vars qw($FW_icondir);
  7. use Data::Dumper;
  8. my $options = { position => { 'bottom-right' => 0,
  9. 'bottom-left' => 1,
  10. 'top-right' => 2,
  11. 'top-left' => 3,
  12. 'center' => 4,
  13. },
  14. width => { 'default' => 0,
  15. 'narrow' => 1,
  16. 'small' => 2,
  17. 'wide' => 3,
  18. 'extrawide' => 4,
  19. },
  20. transparency => { 'default' => 0,
  21. '0%' => 1,
  22. '25%' => 2,
  23. '50%' => 3,
  24. '75%' => 4,
  25. '100%' => 5,
  26. },
  27. interrupt => { 'true' => 1,
  28. 'false' => 0,
  29. },
  30. bkgcolor => { 'grey' => '#607d8b',
  31. 'black' => '#000000',
  32. 'indigo' => '#303F9F',
  33. 'green' => '#4CAF50',
  34. 'red' => '#F44336',
  35. 'cyan' => '#00BCD4',
  36. 'teal' => '#009688',
  37. 'amber' => '#FFC107',
  38. 'pink' => '#E91E63',
  39. },
  40. type => { 'complete' => 0,
  41. 'titleonly' => 1,
  42. 'nameonly' => 2,
  43. 'icononly' => 3,
  44. 'noimage' => 4,
  45. 'short' => 5,
  46. },
  47. duration => undef,
  48. offset => undef,
  49. offsety => undef,
  50. icon => undef,
  51. image => undef,
  52. title => undef,
  53. imageurl => undef,
  54. };
  55. sub
  56. NotifyAndroidTV_Initialize($)
  57. {
  58. my ($hash) = @_;
  59. $hash->{DefFn} = "NotifyAndroidTV_Define";
  60. $hash->{UndefFn} = "NotifyAndroidTV_Undefine";
  61. $hash->{SetFn} = "NotifyAndroidTV_Set";
  62. $hash->{AttrFn} = "NotifyAndroidTV_Attr";
  63. #$hash->{AttrList} = "defaultIcon";
  64. foreach my $option (keys %{$options}) {
  65. $hash->{AttrList} .= ' ' if( $hash->{AttrList} );
  66. #$hash->{AttrList} .= "NotifyAndroidTV#$option";
  67. $hash->{AttrList} .= "default". ucfirst $option;
  68. if( $options->{$option} ) {
  69. $hash->{AttrList} .= ":select,". join( ',', sort keys %{$options->{$option}} )
  70. }
  71. }
  72. #delete $hash->{AttrList};
  73. }
  74. #####################################
  75. sub
  76. NotifyAndroidTV_Define($$)
  77. {
  78. my ($hash, $def) = @_;
  79. my @a = split("[ \t][ \t]*", $def);
  80. return "Usage: define <name> NotifyAndroidTV <host>" if(@a != 3);
  81. my $name = $a[0];
  82. my $host = $a[2];
  83. $hash->{NAME} = $name;
  84. $hash->{host} = $host;
  85. $hash->{STATE} = 'INITIALIZED';
  86. return undef;
  87. }
  88. sub
  89. NotifyAndroidTV_Undefine($$)
  90. {
  91. my ($hash, $arg) = @_;
  92. return undef;
  93. }
  94. sub
  95. NotifyAndroidTV_addFormField($$;$)
  96. {
  97. my ($name,$data,$extra) = @_;
  98. return "--boundary\r\n".
  99. "Content-Disposition: form-data; name=\"$name\"\r\n".
  100. ($extra?"$extra\r\n":"").
  101. "\r\n".
  102. $data."\r\n"
  103. }
  104. sub
  105. NotifyAndroidTV_parseHttpAnswer($$$)
  106. {
  107. my ($param, $err, $data) = @_;
  108. my $hash = $param->{hash};
  109. my $name = $hash->{NAME};
  110. if( $err ) {
  111. Log3 $name, 2, "NotifyAndroidTV ($name) - got error while sending notificaton $err";
  112. readingsSingleUpdate($hash,"lastError", $err, 1);
  113. if( $param->{cl} && $param->{cl}{canAsyncOutput} ) {
  114. asyncOutput( $param->{cl}, "got error while sending notification $err\n" );
  115. }
  116. return;
  117. }
  118. }
  119. sub
  120. NotifyAndroidTV_Set($$@)
  121. {
  122. my ($hash, $name, $cmd, @params) = @_;
  123. $hash->{".triggerUsed"} = 1;
  124. my $list = 'msg';
  125. if( $cmd && ($cmd eq 'msg' || $cmd eq 'notify') ) {
  126. my ($param_a, $param_h) = parseParams(\@params);
  127. my $txt = join( ' ', @{$param_a} );
  128. $txt =~ s/\n/<br>/g;
  129. my $error;
  130. foreach my $option (keys %{$param_h}) {
  131. if( $options->{$option} ) {
  132. if( defined( $options->{$option}{$param_h->{$option}}) ) {
  133. $param_h->{$option} = $options->{$option}{$param_h->{$option}};
  134. } elsif( grep {$_==$param_h->{$option}} values %{$options->{$option}} ) {
  135. $param_h->{$option} = $param_h->{$option};
  136. } else {
  137. $param_h->{$option} = undef;
  138. }
  139. if( !defined($param_h->{$option}) ) {
  140. $error .= "\n";
  141. $error .= "$option value must be one of: ". join( ' ', sort keys %{$options->{$option}} );
  142. }
  143. }
  144. }
  145. return "error: $error" if( $error );
  146. foreach my $option (keys %{$options}) {
  147. if( !defined($param_h->{$option}) ) {
  148. if( my $default = AttrVal($name, "default". ucfirst $option, undef) ) {
  149. $param_h->{$option} = $default;
  150. }
  151. }
  152. }
  153. $param_h->{offset} = 0 if( !$param_h->{offset} );
  154. $param_h->{offsety} = 0 if( !$param_h->{offsety} );
  155. $param_h->{transparency} = 0 if( !$param_h->{transparency} );
  156. if( $param_h->{duration} && $param_h->{duration} eq 'unlimited' ) {
  157. $param_h->{duration} = 15;
  158. $param_h->{interrupt} = 1;
  159. }
  160. if( $txt && $txt eq '?' ) {
  161. my $usage = "usage: set $name msg";
  162. foreach my $option (sort keys %{$options}) {
  163. if( $options->{$option} ) {
  164. $usage .= " [$option=". join( '|', sort keys %{$options->{$option}} ). "]";
  165. } else {
  166. $usage .= " [$option=<$option>]";
  167. }
  168. }
  169. $usage .= " <message>";
  170. return $usage;
  171. }
  172. $param_h->{icon} = 'fhemicon.png' if( !$param_h->{icon} );
  173. $param_h->{icon} .= ".png" if( $param_h->{icon} !~ '\.' );
  174. $param_h->{icon} = "$FW_icondir/default/$param_h->{icon}" if( $param_h->{icon} !~ '^/' );
  175. Log3 $name, 5, "$name: using icon $param_h->{icon}";
  176. my $icon;
  177. local( *FH ) ;
  178. if( open( FH, $param_h->{icon} ) ) {
  179. $icon = do { local( $/ ) ; <FH> } ;
  180. close( FH );
  181. }
  182. return "icon not found: $param_h->{icon}" if( !$icon );
  183. delete $param_h->{icon};
  184. my $image;
  185. if( $param_h->{image} && $param_h->{image} =~ m/^{.*}$/ ) {
  186. $image = eval $param_h->{image};
  187. if( $@ ) {
  188. Log3 $name, 5, "$name: $@";
  189. return $@;
  190. }
  191. return "empty image returned from perl code" if(!$image);
  192. } elsif( $param_h->{image} ) {
  193. #$param_h->{image} .= ".jpg" if( $param_h->{image} !~ '\.' );
  194. #$param_h->{image} = "$FW_icondir/default/$param_h->{image}" if( $param_h->{image} !~ '^/' );
  195. Log3 $name, 5, "$name: using image $param_h->{image}";
  196. local( *FH ) ;
  197. if( open( FH, $param_h->{image} ) ) {
  198. $image = do { local( $/ ) ; <FH> } ;
  199. close(FH);
  200. }
  201. return "image not found: $param_h->{image}" if( !$image );
  202. delete $param_h->{image};
  203. }
  204. my $param;
  205. $param->{url} = "http://$hash->{host}:7676/";
  206. $param->{callback} = \&NotifyAndroidTV_parseHttpAnswer;
  207. $param->{hash} = $hash;
  208. $param->{cl} = $hash->{CL} if( ref($hash->{CL}) eq 'HASH' );
  209. $param->{noshutdown} = 1;
  210. $param->{timeout} = 5;
  211. $param->{loglevel} = 5;
  212. $param->{method} = "POST";
  213. $param->{header} = "Content-Type: multipart/form-data; boundary=boundary";
  214. $param->{data} .= NotifyAndroidTV_addFormField('msg', $txt);
  215. foreach my $option (keys %{$param_h}) {
  216. $param->{data} .= NotifyAndroidTV_addFormField($option, $param_h->{$option})
  217. }
  218. Log3 $name, 5, $param->{data};
  219. $param->{data} .= NotifyAndroidTV_addFormField('filename', $icon, "filename=\"fhemicon.png\"\r\nContent-Type: application/octet-stream");
  220. $param->{data} .= NotifyAndroidTV_addFormField('filename2', $image, "filename=\"image.png\"\r\nContent-Type: application/octet-stream") if( $image );
  221. $param->{data} .= "--boundary--";
  222. Log3 $name, 4, "NotifyAndroidTV ($name) - send notification ";
  223. #Log3 $name, 5, $param->{data};
  224. my ($err, undef) = HttpUtils_NonblockingGet($param);
  225. Log3 $name, 5, "NotifyAndroidTV ($name) - received http response code ".$param->{code} if(exists($param->{code}));
  226. if ($err) {
  227. Log3 $name, 3, "NotifyAndroidTV ($name) - got error while sending notificaton $err";
  228. return "got error while sending notification $err";
  229. }
  230. return;
  231. }
  232. $list =~ s/ $//;
  233. return "Unknown argument $cmd, choose one of $list";
  234. }
  235. sub
  236. NotifyAndroidTV_Get($$@)
  237. {
  238. my ($hash, $name, $cmd, @params) = @_;
  239. my $list = '';
  240. $list =~ s/ $//;
  241. return "Unknown argument $cmd, choose one of $list";
  242. }
  243. sub
  244. NotifyAndroidTV_Attr($$$)
  245. {
  246. my ($cmd, $name, $attrName, $attrVal) = @_;
  247. my $orig = $attrVal;
  248. $attrVal = int($attrVal) if($attrName eq "interval");
  249. my $hash = $defs{$name};
  250. if( $attrName eq 'disable' ) {
  251. }
  252. if( $cmd eq "set" ) {
  253. if( $attrName =~ m/default(.*)/ ) {
  254. my $option = lcfirst $1;
  255. if($options->{$option} && !defined($options->{$option}{$attrVal})) {
  256. return "$attrName value must be one of: ". join( ' ', sort keys %{$options->{$option}} );
  257. }
  258. }
  259. }
  260. return;
  261. }
  262. 1;
  263. =pod
  264. =item summary Notifications for Android TV/Fire TV module
  265. =item summary_DE Notifications for Android TV/Fire TV Modul
  266. =begin html
  267. <a name="NotifyAndroidTV"></a>
  268. <h3>NotifyAndroidTV</h3>
  269. <ul>
  270. This module allows you to send notifications to the
  271. <a href ='https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google'>
  272. Notifications for Android TV</a> and
  273. <a href ='https://www.amazon.de/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK'>
  274. Notifications for Fire TV</a> apps.
  275. <br><br>
  276. <code>set &lt;name&gt; msg bkgcolor=amber interrupt=true position=top-left transparency=0% duration=2 offset=10 icon=fhemicon title="der titel" das ist ein test</code>
  277. <br><br>
  278. <a name="NotifyAndroidTV_Define"></a>
  279. <b>Define</b>
  280. <ul>
  281. <code>define &lt;name&gt; NotifyAndroidTV &lt;host&gt;</code>
  282. <br><br>
  283. </ul>
  284. <a name="NotifyAndroidTV_Set"></a>
  285. <b>Set</b>
  286. <ul>
  287. <li>msg [options] &lt;message&gt;<br>
  288. possible options are: bkgcolor, interrupt, position, transparency, duration, offset, offsety, width, type, icon, image, title, imageurl. use <code>set &lt;name&gt; notify</code> to see valid values.<br>
  289. <code>set nb msg ?</code> shows a help text<br>
  290. it is better to use imageurl instad of image as it is non blocking!<br>
  291. image can be given as <code>image={&lt;perlCode&gt;}</code></li>
  292. </ul><br>
  293. <a name="NotifyAndroidTV_Get"></a>
  294. <b>Get</b>
  295. <ul>none
  296. </ul><br>
  297. <a name="NotifyAndroidTV_Attr"></a>
  298. <b>Attr</b>
  299. <ul>none
  300. </ul>
  301. </ul><br>
  302. =end html
  303. =cut