# $Id$ package main; use strict; use warnings; use HttpUtils; use vars qw($FW_icondir); use Data::Dumper; my $options = { position => { 'bottom-right' => 0, 'bottom-left' => 1, 'top-right' => 2, 'top-left' => 3, 'center' => 4, }, width => { 'default' => 0, 'narrow' => 1, 'small' => 2, 'wide' => 3, 'extrawide' => 4, }, transparency => { 'default' => 0, '0%' => 1, '25%' => 2, '50%' => 3, '75%' => 4, '100%' => 5, }, interrupt => { 'true' => 1, 'false' => 0, }, bkgcolor => { 'grey' => '#607d8b', 'black' => '#000000', 'indigo' => '#303F9F', 'green' => '#4CAF50', 'red' => '#F44336', 'cyan' => '#00BCD4', 'teal' => '#009688', 'amber' => '#FFC107', 'pink' => '#E91E63', }, type => { 'complete' => 0, 'titleonly' => 1, 'nameonly' => 2, 'icononly' => 3, 'noimage' => 4, 'short' => 5, }, fontsize => { 'default' => 2, 'small' => 1, 'medium' => 2, 'large' => 3, }, duration => undef, offset => undef, offsety => undef, icon => undef, image => undef, title => undef, imageurl => undef, }; sub NotifyAndroidTV_Initialize($) { my ($hash) = @_; $hash->{DefFn} = "NotifyAndroidTV_Define"; $hash->{UndefFn} = "NotifyAndroidTV_Undefine"; $hash->{SetFn} = "NotifyAndroidTV_Set"; $hash->{AttrFn} = "NotifyAndroidTV_Attr"; #$hash->{AttrList} = "defaultIcon"; foreach my $option (keys %{$options}) { $hash->{AttrList} .= ' ' if( $hash->{AttrList} ); #$hash->{AttrList} .= "NotifyAndroidTV#$option"; $hash->{AttrList} .= "default". ucfirst $option; if( $options->{$option} ) { $hash->{AttrList} .= ":select,". join( ',', sort keys %{$options->{$option}} ) } } #delete $hash->{AttrList}; } ##################################### sub NotifyAndroidTV_Define($$) { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); return "Usage: define NotifyAndroidTV " if(@a != 3); my $name = $a[0]; my $host = $a[2]; $hash->{NAME} = $name; $hash->{host} = $host; $hash->{STATE} = 'INITIALIZED'; return undef; } sub NotifyAndroidTV_Undefine($$) { my ($hash, $arg) = @_; return undef; } sub NotifyAndroidTV_addFormField($$;$) { my ($name,$data,$extra) = @_; return "--boundary\r\n". "Content-Disposition: form-data; name=\"$name\"\r\n". ($extra?"$extra\r\n":""). "\r\n". $data."\r\n" } sub NotifyAndroidTV_parseHttpAnswer($$$) { my ($param, $err, $data) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; if( $err ) { Log3 $name, 2, "NotifyAndroidTV ($name) - got error while sending notificaton $err"; readingsSingleUpdate($hash,"lastError", $err, 1); if( $param->{cl} && $param->{cl}{canAsyncOutput} ) { asyncOutput( $param->{cl}, "got error while sending notification $err\n" ); } return; } } sub NotifyAndroidTV_Set($$@) { my ($hash, $name, $cmd, @params) = @_; $hash->{".triggerUsed"} = 1; my $list = 'msg'; my ($param_a, $param_h) = parseParams(\@params); if( $cmd && ($cmd eq 'msg' || $cmd eq 'notify') ) { foreach my $option (keys %{$options}) { if( !defined($param_h->{$option}) ) { if( my $default = AttrVal($name, "default". ucfirst $option, undef) ) { $param_h->{$option} = $default; } } } my $txt = join( ' ', @{$param_a} ); $txt =~ s/\n/
/g; my $error; foreach my $option (keys %{$param_h}) { if( $options->{$option} ) { if( defined( $options->{$option}{$param_h->{$option}}) ) { $param_h->{$option} = $options->{$option}{$param_h->{$option}}; } elsif( grep {$_ eq $param_h->{$option}} values %{$options->{$option}} ) { $param_h->{$option} = $param_h->{$option}; } else { $param_h->{$option} = undef; } if( !defined($param_h->{$option}) ) { $error .= "\n"; $error .= "$option value must be one of: ". join( ' ', sort keys %{$options->{$option}} ); } } } return "error: $error" if( $error ); $param_h->{offset} = 0 if( !$param_h->{offset} ); $param_h->{offsety} = 0 if( !$param_h->{offsety} ); $param_h->{transparency} = 0 if( !$param_h->{transparency} ); if( $param_h->{duration} && $param_h->{duration} eq 'unlimited' ) { $param_h->{duration} = 15; $param_h->{interrupt} = 1; } if( $txt && $txt eq '?' ) { my $usage = "usage: set $name msg"; foreach my $option (sort keys %{$options}) { if( $options->{$option} ) { $usage .= " [$option=". join( '|', sort keys %{$options->{$option}} ). "]"; } else { $usage .= " [$option=<$option>]"; } } $usage .= " "; return $usage; } $param_h->{icon} = 'fhemicon.png' if( !$param_h->{icon} ); $param_h->{icon} .= ".png" if( $param_h->{icon} !~ '\.' ); $param_h->{icon} = "$FW_icondir/default/$param_h->{icon}" if( $param_h->{icon} !~ '^/' ); Log3 $name, 5, "$name: using icon $param_h->{icon}"; my $icon; local( *FH ) ; if( open( FH, $param_h->{icon} ) ) { $icon = do { local( $/ ) ; } ; close( FH ); } return "icon not found: $param_h->{icon}" if( !$icon ); delete $param_h->{icon}; my $image; if( $param_h->{image} && $param_h->{image} =~ m/^{.*}$/ ) { $image = eval $param_h->{image}; if( $@ ) { Log3 $name, 5, "$name: $@"; return $@; } return "empty image returned from perl code" if(!$image); } elsif( $param_h->{image} ) { #$param_h->{image} .= ".jpg" if( $param_h->{image} !~ '\.' ); #$param_h->{image} = "$FW_icondir/default/$param_h->{image}" if( $param_h->{image} !~ '^/' ); Log3 $name, 5, "$name: using image $param_h->{image}"; local( *FH ) ; if( open( FH, $param_h->{image} ) ) { $image = do { local( $/ ) ; } ; close(FH); } return "image not found: $param_h->{image}" if( !$image ); delete $param_h->{image}; } my $param; $param->{url} = "http://$hash->{host}:7676/"; $param->{callback} = \&NotifyAndroidTV_parseHttpAnswer; $param->{hash} = $hash; $param->{cl} = $hash->{CL} if( ref($hash->{CL}) eq 'HASH' ); $param->{noshutdown} = 1; $param->{timeout} = 5; $param->{loglevel} = 5; $param->{method} = "POST"; $param->{header} = "Content-Type: multipart/form-data; boundary=boundary"; $param->{data} .= NotifyAndroidTV_addFormField('msg', $txt); foreach my $option (keys %{$param_h}) { $param->{data} .= NotifyAndroidTV_addFormField($option, $param_h->{$option}) } Log3 $name, 5, $param->{data}; $param->{data} .= NotifyAndroidTV_addFormField('filename', $icon, "filename=\"fhemicon.png\"\r\nContent-Type: application/octet-stream"); $param->{data} .= NotifyAndroidTV_addFormField('filename2', $image, "filename=\"image.png\"\r\nContent-Type: application/octet-stream") if( $image ); $param->{data} .= "--boundary--"; Log3 $name, 4, "NotifyAndroidTV ($name) - send notification "; #Log3 $name, 5, $param->{data}; my ($err, undef) = HttpUtils_NonblockingGet($param); Log3 $name, 5, "NotifyAndroidTV ($name) - received http response code ".$param->{code} if(exists($param->{code})); if ($err) { Log3 $name, 3, "NotifyAndroidTV ($name) - got error while sending notificaton $err"; return "got error while sending notification $err"; } return; } $list =~ s/ $//; return "Unknown argument $cmd, choose one of $list"; } sub NotifyAndroidTV_Get($$@) { my ($hash, $name, $cmd, @params) = @_; my $list = ''; $list =~ s/ $//; return "Unknown argument $cmd, choose one of $list"; } sub NotifyAndroidTV_Attr($$$) { my ($cmd, $name, $attrName, $attrVal) = @_; my $orig = $attrVal; $attrVal = int($attrVal) if($attrName eq "interval"); my $hash = $defs{$name}; if( $attrName eq 'disable' ) { } if( $cmd eq "set" ) { if( $attrName =~ m/default(.*)/ ) { my $option = lcfirst $1; if($options->{$option} && !defined($options->{$option}{$attrVal})) { return "$attrName value must be one of: ". join( ' ', sort keys %{$options->{$option}} ); } } } return; } 1; =pod =item summary Notifications for Android TV/Fire TV module =item summary_DE Notifications for Android TV/Fire TV Modul =begin html

NotifyAndroidTV

    This module allows you to send notifications to the Notifications for Android TV and Notifications for Fire TV apps.

    set <name> msg bkgcolor=amber interrupt=true position=top-left transparency=0% duration=2 offset=10 icon=fhemicon title="der titel" das ist ein test

    Define
      define <name> NotifyAndroidTV <host>

    Set
    • msg [options] <message>
      possible options are: bkgcolor, interrupt, position, transparency, duration, offset, offsety, width, type, fontsize, icon, image, title, imageurl. use set <name> notify to see valid values.
      set nb msg ? shows a help text
      it is better to use imageurl instad of image as it is non blocking!
      image can be given as image={<perlCode>}

    Get
      none

    Attr
      none

=end html =cut