2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2024-11-22 09:49:50 +00:00

WebViewControl: first commit

git-svn-id: https://svn.fhem.de/fhem/trunk@8178 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
dirkho 2015-03-09 00:55:07 +00:00
parent 5c1ad8e961
commit d4792c1d03
15 changed files with 7866 additions and 74 deletions

View File

@ -66,7 +66,6 @@ WS300_Initialize($)
# Provider
$hash->{AttrList} = "do_not_notify:0,1 showtime:0,1 model:ws300 ".
"loglevel:0,1,2,3,4,5,6 ".
$readingFnAttributes;
$hash->{DefFn} = "WS300_Define";
@ -669,7 +668,6 @@ NEXTPOLL:
<b>Attributes</b>
<ul>
<li><a href="#do_not_notify">do_not_notify</a></li>
<li><a href="#loglevel">loglevel</a></li>
<li><a href="#model">model</a> (ws300)</li>
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
</ul>

View File

@ -72,14 +72,14 @@ sub I2C_BMP180_Initialize($) {
my ($hash) = @_;
eval "use HiPi::Device::I2C;";
$libcheck_hasHiPi = 0 if($@);
$libcheck_hasHiPi = 0 if($@);
$hash->{DefFn} = 'I2C_BMP180_Define';
$hash->{InitFn} = 'I2C_BMP180_Init';
$hash->{AttrFn} = 'I2C_BMP180_Attr';
$hash->{SetFn} = 'I2C_BMP180_Set';
$hash->{UndefFn} = 'I2C_BMP180_Undef';
$hash->{I2CRecFn} = 'I2C_BMP180_I2CRec';
$hash->{I2CRecFn} = 'I2C_BMP180_I2CRec';
$hash->{AttrList} = 'IODev do_not_notify:0,1 showtime:0,1 model:BMP180,BMP085 ' .
'poll_interval:1,2,5,10,20,30 oversampling_settings:0,1,2,3 ' .
@ -119,10 +119,9 @@ sub I2C_BMP180_Define($$) {
return $msg;
}
if ($main::init_done || $hash->{HiPi_used}) {
eval { I2C_BMP180_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
return I2C_BMP180_Catch($@) if $@;
}
eval { I2C_BMP180_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
return I2C_BMP180_Catch($@) if $@;
}
}
sub I2C_BMP180_Init($$) {
@ -160,12 +159,12 @@ sub I2C_BMP180_Init($$) {
}
sub I2C_BMP180_Catch($) {
my $exception = shift;
if ($exception) {
$exception =~ /^(.*)( at.*FHEM.*)$/;
return $1;
}
return undef;
my $exception = shift;
if ($exception) {
$exception =~ /^(.*)( at.*FHEM.*)$/;
return $1;
}
return undef;
}
=head2 I2C_BMP180_Attr
@ -260,7 +259,7 @@ sub I2C_BMP180_Undef($$) {
sub I2C_BMP180_I2CRec ($$) {
my ($hash, $clientmsg) = @_;
my $name = $hash->{NAME};
my $name = $hash->{NAME};
my $pname = undef;
unless ($hash->{HiPi_used}) {#nicht nutzen wenn HiPi Bibliothek in Benutzung
my $phash = $hash->{IODev};
@ -275,7 +274,7 @@ sub I2C_BMP180_I2CRec ($$) {
|| $hash->{HiPi_used}) ) {
if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received}) ) {
Log3 $hash, 5, "$name empfangen: $clientmsg->{received}";
I2C_BMP180_GetCal ($hash, $clientmsg->{received}) if $clientmsg->{reg} == hex("AA");
I2C_BMP180_GetCal ($hash, $clientmsg->{received}) if $clientmsg->{reg} == hex("AA");
I2C_BMP180_GetTemp ($hash, $clientmsg->{received}) if $clientmsg->{reg} == hex("F6") && $clientmsg->{nbyte} == 2;
I2C_BMP180_GetPress ($hash, $clientmsg->{received}) if $clientmsg->{reg} == hex("F6") && $clientmsg->{nbyte} == 3;
}
@ -284,7 +283,7 @@ sub I2C_BMP180_I2CRec ($$) {
sub I2C_BMP180_GetCal ($$) {
my ($hash, $rawdata) = @_;
my @raw = split(" ",$rawdata);
my @raw = split(" ",$rawdata);
my $n = 0;
Log3 $hash, 5, "in get cal: $rawdata";
$hash->{calibrationData}{ac1} = I2C_BMP180_GetCalVar($raw[$n++], $raw[$n++]);
@ -317,23 +316,23 @@ sub I2C_BMP180_GetCalVar ($$;$) {
sub I2C_BMP180_GetTemp ($$) {
my ($hash, $rawdata) = @_;
my @raw = split(" ",$rawdata);
$hash->{uncompTemp} = $raw[0] << 8 | $raw[1];
my @raw = split(" ",$rawdata);
$hash->{uncompTemp} = $raw[0] << 8 | $raw[1];
}
sub I2C_BMP180_GetPress ($$) {
my ($hash, $rawdata) = @_;
my @raw = split(" ",$rawdata);
my @raw = split(" ",$rawdata);
my $overSamplingSettings = AttrVal($hash->{NAME}, 'oversampling_settings', 3);
my $ut = $hash->{uncompTemp};
my $ut = $hash->{uncompTemp};
delete $hash->{uncompTemp};
my $up = ( ( ($raw[0] << 16) | ($raw[1] << 8) | $raw[2] ) >> (8 - $overSamplingSettings) );
my $temperature = sprintf(
'%.' . AttrVal($hash->{NAME}, 'roundTemperatureDecimal', 1) . 'f',
I2C_BMP180_calcTrueTemperature($hash, $ut) / 10
);
'%.' . AttrVal($hash->{NAME}, 'roundTemperatureDecimal', 1) . 'f',
I2C_BMP180_calcTrueTemperature($hash, $ut) / 10
);
my $pressure = sprintf(
'%.' . AttrVal($hash->{NAME}, 'roundPressureDecimal', 1) . 'f',
@ -369,7 +368,7 @@ sub I2C_BMP180_GetPress ($$) {
=cut
sub I2C_BMP180_readUncompensatedTemperature($) {
my ($hash) = @_;
# Write 0x2E into Register 0xF4. This requests a temperature reading
I2C_BMP180_i2cwrite($hash, hex("F4"), hex("2E"));
@ -452,8 +451,8 @@ sub I2C_BMP180_i2cwrite($$$) {
CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
direction => "i2cwrite",
i2caddress => $hash->{I2C_Address},
reg => $reg,
data => join (' ',@data),
reg => $reg,
data => join (' ',@data),
});
} else {
return "no IODev assigned to '$hash->{NAME}'";
@ -531,39 +530,39 @@ sub I2C_BMP180_calcTruePressure($$$) {
via the i2c bus on Raspberry Pi.<br><br>
<b>There are two possibilities connecting to I2C bus:</b><br>
<ul>
<li><b>via RPII2C module</b><br>
The I2C messages are send through an I2C interface module like <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
or <a href="#NetzerI2C">NetzerI2C</a> so this device must be defined first.<br>
<b>attribute IODev must be set</b><br><br>
<li><b>via RPII2C module</b><br>
The I2C messages are send through an I2C interface module like <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
or <a href="#NetzerI2C">NetzerI2C</a> so this device must be defined first.<br>
<b>attribute IODev must be set</b><br><br>
</li>
<li><b>via HiPi library</b><br>
Add these two lines to your <b>/etc/modules</b> file to load the I2C relevant kernel modules
automaticly during booting your Raspberry Pi.<br>
<code><pre> i2c-bcm2708
<li><b>via HiPi library</b><br>
Add these two lines to your <b>/etc/modules</b> file to load the I2C relevant kernel modules
automaticly during booting your Raspberry Pi.<br>
<code><pre>i2c-bcm2708
i2c-dev</pre></code>
Install HiPi perl modules:<br>
<code><pre> wget http://raspberry.znix.com/hipifiles/hipi-install
Install HiPi perl modules:<br>
<code><pre>wget http://raspberry.znix.com/hipifiles/hipi-install
perl hipi-install</pre></code>
To change the permissions of the I2C device create file:<br>
<code><pre> /etc/udev/rules.d/98_i2c.rules</pre></code>
with this content:<br>
<code><pre> SUBSYSTEM=="i2c-dev", MODE="0666"</pre></code>
<b>Reboot</b><br><br>
To change the permissions of the I2C device create file:<br>
<code><pre> /etc/udev/rules.d/98_i2c.rules</pre></code>
with this content:<br>
<code><pre>SUBSYSTEM=="i2c-dev", MODE="0666"</pre></code>
<b>Reboot</b><br><br>
To use the sensor on the second I2C bus at P5 connector
(only for version 2 of Raspberry Pi) you must add the bold
line of following code to your FHEM start script:
<code><pre> case "$1" in
To use the sensor on the second I2C bus at P5 connector
(only for version 2 of Raspberry Pi) you must add the bold
line of following code to your FHEM start script:
<code><pre> case "$1" in
'start')
<b>sudo hipi-i2c e 0 1</b>
...</pre></code>
</li></ul>
<p>
</li></ul>
<p>
<b>Define</b>
<ul>
<code>define BMP180 I2C_BMP180 [&lt;I2C device&gt;]</code><br><br>
&lt;I2C device&gt; must not be used if you connect via RPII2C module. For HiPi it's mandatory. <br>
&lt;I2C device&gt; must not be used if you connect via RPII2C module. For HiPi it's mandatory. <br>
<br>
Examples:
<pre>
@ -571,7 +570,7 @@ sub I2C_BMP180_calcTruePressure($$$) {
attr BMP180 oversampling_settings 3
attr BMP180 poll_interval 5
</pre>
<pre>
<pre>
define BMP180 I2C_BMP180
attr BMP180 IODev RPiI2CMod
attr BMP180 oversampling_settings 3
@ -640,39 +639,39 @@ sub I2C_BMP180_calcTruePressure($$$) {
Dieses Modul erm&ouml;glicht das Auslesen der digitalen (Luft)drucksensoren
BMP085 und BMP180 &uuml;ber den I2C Bus des Raspberry Pi.<br><br>
<b>Es gibt zwei M&ouml;glichkeiten das Modul mit dem I2C Bus zu verbinden:</b><br>
<ul>
<li><b>&Uuml;ber das RPII2C Modul</b><br>
I2C-Botschaften werden &uuml;ber ein I2C Interface Modul wie beispielsweise das <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
oder <a href="#NetzerI2C">NetzerI2C</a> gesendet. Daher muss dieses vorher definiert werden.<br>
<b>Das Attribut IODev muss definiert sein.</b><br><br>
</li>
<li><b>&Uuml;ber die HiPi Bibliothek</b><br>
Diese beiden Zeilen m&uuml;ssen in die Datei <b>/etc/modules</b> angef&uuml;gt werden,
um die Kernel Module automatisch beim Booten des Raspberry Pis zu laden.<br>
<code><pre> i2c-bcm2708
<ul>
<li><b>&Uuml;ber das RPII2C Modul</b><br>
I2C-Botschaften werden &uuml;ber ein I2C Interface Modul wie beispielsweise das <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
oder <a href="#NetzerI2C">NetzerI2C</a> gesendet. Daher muss dieses vorher definiert werden.<br>
<b>Das Attribut IODev muss definiert sein.</b><br><br>
</li>
<li><b>&Uuml;ber die HiPi Bibliothek</b><br>
Diese beiden Zeilen m&uuml;ssen in die Datei <b>/etc/modules</b> angef&uuml;gt werden,
um die Kernel Module automatisch beim Booten des Raspberry Pis zu laden.<br>
<code><pre>i2c-bcm2708
i2c-dev</pre></code>
Installation des HiPi Perl Moduls:<br>
<code><pre> wget http://raspberry.znix.com/hipifiles/hipi-install
Installation des HiPi Perl Moduls:<br>
<code><pre>wget http://raspberry.znix.com/hipifiles/hipi-install
perl hipi-install</pre></code>
Um die Rechte f&uuml;r die I2C Devices anzupassen, folgende Datei:<br>
<code><pre> /etc/udev/rules.d/98_i2c.rules</pre></code>
mit diesem Inhalt anlegen:<br>
<code><pre> SUBSYSTEM=="i2c-dev", MODE="0666"</pre></code>
<b>Reboot</b><br><br>
Falls der Sensor am zweiten I2C Bus am Stecker P5 (nur in Version 2 des
Raspberry Pi) verwendet werden soll, muss die fett gedruckte Zeile
des folgenden Codes in das FHEM Start Skript aufgenommen werden:
<code><pre> case "$1" in
Um die Rechte f&uuml;r die I2C Devices anzupassen, folgende Datei:<br>
<code><pre> /etc/udev/rules.d/98_i2c.rules</pre></code>
mit diesem Inhalt anlegen:<br>
<code><pre>SUBSYSTEM=="i2c-dev", MODE="0666"</pre></code>
<b>Reboot</b><br><br>
Falls der Sensor am zweiten I2C Bus am Stecker P5 (nur in Version 2 des
Raspberry Pi) verwendet werden soll, muss die fett gedruckte Zeile
des folgenden Codes in das FHEM Start Skript aufgenommen werden:
<code><pre> case "$1" in
'start')
<b>sudo hipi-i2c e 0 1</b>
...</pre></code>
</li></ul>
</li></ul>
<p>
<b>Define</b>
<ul>
<code>define BMP180 &lt;BMP180_name&gt; &lt;I2C_device&gt;</code><br><br>
&lt;I2C device&gt; darf nicht verwendet werden, wenn der I2C Bus &uuml;ber das RPII2C Modul angesprochen wird. For HiPi ist es allerdings notwendig. <br>
&lt;I2C device&gt; darf nicht verwendet werden, wenn der I2C Bus &uuml;ber das RPII2C Modul angesprochen wird. For HiPi ist es allerdings notwendig. <br>
<br>
Beispiel:
<pre>
@ -680,7 +679,7 @@ sub I2C_BMP180_calcTruePressure($$$) {
attr BMP180 oversampling_settings 3
attr BMP180 poll_interval 5
</pre>
<pre>
<pre>
define BMP180 I2C_BMP180
attr BMP180 IODev RPiI2CMod
attr BMP180 oversampling_settings 3

View File

@ -324,6 +324,7 @@ contrib/71_LISTENLIVE.pm betateilchen http://forum.fhem.de Multimedi
contrib/98_geodata.pm betateilchen http://forum.fhem.de Sonstiges
contrib/98_openweathermap.pm betateilchen http://forum.fhem.de Unterstuetzende Dienste
contrib/98_PID.pm betateilchen http://forum.fhem.de Automatisierung
contrib/WebViewControl/* Dirk http://forum.fhem.de Mobile Devices
www/codemirror/* betateilchen http://forum.fhem.de Frontends
www/gplot/* rudolfkoenig http://forum.fhem.de Frontends

View File

@ -0,0 +1,311 @@
################################################################################
# 95_webViewControl.pm
# Modul for FHEM
#
# Modul for communication between WebViewControl Android App and FHEM
#
# contributed by Dirk Hoffmann 01/2013
# $Id:
#
################################################################################
package main;
use Data::Dumper; # for debugging only
use strict;
use warnings;
use URI::Escape;
use vars qw {%data %attr %defs %modules $FW_RET}; #supress errors in Eclipse EPIC
use constant {
webViewControl_Version => '0.5.1_beta',
};
#########################
# Forward declaration
sub webViewControl_Initialize($); # Initialize
sub webViewControl_Define($$); # define <name> WEBVIEWCONTROL
sub webViewControl_Undef($$); # delete
sub webViewControl_modifyJsInclude($); # include js parts
sub webViewControl_Set($@); # set
sub webViewControl_Get($@); # get
sub webViewControl_Cgi(); # analyze and parse URL
sub webViewControl_Attr(@);
#########################
# Global variables
my $fhemUrl = '/webviewcontrol' ;
my %sets = (
'screenBrightness' => 'screenBrightness', # slider,1,1,100',
'volume' => 'volume', # slider,1,1,100',
'keepScreenOn' => 'keepScreenOn',
'toastMessage' => 'toastMessage',
'reload' => 'reload',
'audioPlay' => 'audioPlay',
'audioStop' => 'audioStop',
'ttsSay' => 'ttsSay',
'voiceRec' => 'voiceRec',
'newUrl' => 'newUrl',
'reload' => 'reload',
);
my %gets = (
'powerLevel' => 1,
'powerPlugged' => 1,
'voiceRecognitionLastError' => 1,
'voiceRecognitionLastResult'=> 1,
);
my $FW_encoding="UTF-8"; # like in FHEMWEB: encoding hardcoded
################################################################################
# Implements Initialize function
#
# @param hash $hash hash of device addressed
#
################################################################################
sub webViewControl_Initialize($) {
my ($hash) = @_;
$hash->{DefFn} = 'webViewControl_Define';
$hash->{UndefFn} = 'webViewControl_Undef';
$hash->{SetFn} = 'webViewControl_Set';
$hash->{GetFn} = 'webViewControl_Get';
$hash->{AttrFn} = "webViewControl_Attr";
$hash->{AttrList} = 'loglevel:0,1,2,3,4,5,6 model userJsFile userCssFile '
. $readingFnAttributes;
# CGI
$data{FWEXT}{$fhemUrl}{FUNC} = 'webViewControl_Cgi';
}
################################################################################
# Implements DefFn function
#
# @param hash $hash hash of device addressed
# @param string $def definition string
#
# @return string
#
################################################################################
sub webViewControl_Define($$) {
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
my $name = $hash->{NAME};
return "wrong syntax: define <name> WEBVIEWCONTROL APP-ID" if int(@a)!=3;
$hash->{appID} = $a[2];
$modules{webViewControl}{defptr}{$name} = $hash;
webViewControl_modifyJsInclude($hash);
$hash->{VERSION} = webViewControl_Version;
return undef;
}
#############################
sub webViewControl_Undef($$) {
my ($hash, $name) = @_;
delete($modules{webViewControl}{defptr}{$name});
webViewControl_modifyJsInclude($hash);
return undef;
}
sub webViewControl_Attr (@) {
my (undef, $name, $attr, $val) = @_;
my $hash = $defs{$name};
my $msg = '';
if ($attr eq 'userJsFile' || $attr eq 'userCssFile') {
$attr{$name}{$attr} = $val;
webViewControl_modifyJsInclude($hash);
}
return undef;
}
sub webViewControl_modifyJsInclude($) {
my ($hash) = @_;
my $name = $hash->{NAME};
my @appsArray;
foreach my $appName (keys %{ $modules{webViewControl}{defptr} } ) {
push(@appsArray, '\'' . $modules{webViewControl}{defptr}{$appName}->{appID} . '\': \'' . $appName . '\'');
}
my $vars = 'var wvcDevices = {' . join(', ', @appsArray) . '}';
my $userJs = AttrVal($name, 'userJsFile', '');
$userJs = $userJs ? '<script type="text/javascript" src="/fhem/pgm2/' . $userJs . '"></script>' : '';
my $userCss = AttrVal($name, 'userCssFile', '');
if ($userCss) {
$vars.= '; var wvcUserCssFile="' . $userCss . '"';
}
$data{FWEXT}{$fhemUrl}{SCRIPT} = 'cordova-2.3.0.js"></script>' .
'<script type="text/javascript" src="/fhem/pgm2/webviewcontrol.js"></script>' .
$userJs .
'<script type="text/javascript">' . $vars . '</script>' .
'<script type="text/javascript" charset="UTF-8';
}
###################################
sub webViewControl_Set($@) {
my ($hash, @a) = @_;
my $setArgs = join(' ', sort values %sets);
my $name = shift @a;
if (int(@a) == 1 && $a[0] eq '?') {
my %localSets = %sets;
$localSets{screenBrightness}.=':slider,1,1,255';
$localSets{volume}.=':slider,0,1,15';
my $setArgs = join(' ', sort values %localSets);
return $setArgs;
}
if ((int(@a) < 1) || (!defined $sets{$a[0]}) ) {
return 'Please specify one of following set value: ' . $setArgs;
}
my $cmd = $a[0];
if (! (($sets{$cmd} eq 'reload') || ($sets{$cmd} eq 'audioStop')) ) {
if ($sets{$cmd} eq 'toastMessage' && (int(@a)) < 2) {
return 'Please input a text for toastMessage';
} elsif ($sets{$cmd} eq 'keepScreenOn') {
if ($a[1] ne 'on' && $a[1] ne 'off') {
return 'keepScreenOn needs on of off';
} else {
$a[1] = ($a[1] eq 'on') ? 'true' : 'false';
}
} elsif ($sets{$cmd} eq 'screenBrightness' && (int($a[1]) < 1 || int($a[1]) > 255)) {
return 'screenBrightness needs value from 1 to 255';
} elsif ($sets{$cmd} eq 'volume' && (int($a[1]) < 0 || int($a[1]) > 15)) {
return 'volume needs value from 0 to 15';
} elsif ($sets{$cmd} eq 'audioPlay' && (int(@a)) < 2 ) {
return 'Please input a url where Audio to play.';
} elsif ($sets{$cmd} eq 'ttsSay' && (int(@a)) < 2 ) {
return 'Please input a text to say.';
} elsif ($sets{$cmd} eq 'voiceRec' && ($a[1] ne 'start' && $a[1] ne 'stop')) {
return 'voiceRec must set to start or stop';
} elsif ($sets{$cmd} eq 'newUrl') {
if ((int(@a)) < 2 ) {
return 'Please input a url.';
} else {
shift(@a);
my $v = uri_escape(join(' ', @a));
@a = ($cmd, $v);
}
}
}
my $v = join(' ', @a);
$hash->{CHANGED}[0] = $v;
$hash->{STATE} = $v;
$hash->{lastCmd} = $v;
$hash->{READINGS}{state}{TIME} = TimeNow();
$hash->{READINGS}{state}{VAL} = $v;
return undef;
}
sub webViewControl_Get($@) {
my ($hash, @a) = @_;
return ('argument missing, usage is <attribute>') if(@a!=2);
if(!$gets{$a[1]}) {
return $a[1] . 'Not supported by get. Supported attributes: ' . join(' ', keys %gets) ;
}
my $retVal;
if ($hash->{READINGS}{$a[1]}{VAL}) {
$retVal = $hash->{READINGS}{$a[1]}{VAL};
} else {
$retVal = $a[1] . ' not yet set';
}
return $retVal;
}
##################
# Answer requests for webviewcontrol url for set some readings
sub webViewControl_Cgi() {
my ($htmlarg) = @_; #URL
$htmlarg =~ s/^\///;
my @htmlpart = ();
@htmlpart = split("\\?", $htmlarg) if ($htmlarg); #split URL by ?
if ($htmlpart[1]) {
$htmlpart[1] =~ s,^[?/],,;
my @states = ();
my $name = undef;
my %readings = ();
my $timeNow = TimeNow();
foreach my $pv (split("&", $htmlpart[1])) { #per each URL-section devided by &
$pv =~ s/\+/ /g;
$pv =~ s/%(..)/chr(hex($1))/ge;
my ($p,$v) = split("=",$pv, 2); #$p = parameter, $v = value
$p =~ s/[\r]\n/\\\n/g;
$v =~ s/[\r]\n/\\\n/g;
if ($p eq 'id') {
$name = $v;
} else {
$readings{$p} = $v;
push(@states, $p . '=' . $v);
}
}
if ($modules{webViewControl}{defptr}{$name}) {
my $state = join(', ', @states);
my $hash = $modules{webViewControl}{defptr}{$name};
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "state", $state);
foreach my $reading (keys %readings) {
readingsBulkUpdate($hash, $reading, $readings{$reading});
}
readingsEndUpdate($hash, 1);
}
}
return ("text/html; charset=$FW_encoding", $FW_RET); # $FW_RET composed by FW_pO, FP_pH etc
}
1;
=pod
=begin html
<a name="WebViewControl"></a>
<h3>WebViewControl</h3>
<ul>
WebViewCountrol ist the interface for the android APP WebviewControl
<br><br>
</ul>
=end html
=cut

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,147 @@
* {
-webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
-webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */
-webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
*-webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' or 'auto' */
}
input, textarea {
-webkit-user-select: auto;
}
.batteryIconWrapper .batteryIcon {
display: block;
background: transparent url('../icons/batteryIconSprite.png') no-repeat;
height: 32px;
width: 18px;
position: relative;
}
.batteryIconWrapper .txtPercent {
padding-top: 32px;
margin-left: -2px;
font-family: sans-serif;
color: #999999;
width: 25px;
text-align: center;
font-size: 10px;
}
.batteryIconWrapper .bat10green { background-position: 0px 0; width: 18px; height: 32px}
.batteryIconWrapper .bat20green { background-position: -20px 0; width: 18px; height: 32px}
.batteryIconWrapper .bat30green { background-position: -40px 0; width: 18px; height: 32px}
.batteryIconWrapper .bat40green { background-position: -60px 0; width: 18px; height: 32px}
.batteryIconWrapper .bat50green { background-position: -80px 0; width: 18px; height: 32px}
.batteryIconWrapper .bat60green { background-position: -100px 0; width: 18px; height: 32px}
.batteryIconWrapper .bat70green { background-position: -120px 0; width: 18px; height: 32px}
.batteryIconWrapper .bat80green { background-position: -139px 0; width: 18px; height: 32px}
.batteryIconWrapper .bat90green { background-position: -159px 0; width: 18px; height: 32px}
.batteryIconWrapper .bat100green { background-position: -179px 0; width: 18px; height: 32px}
.batteryIconWrapper .bat10yellow { background-position: 0 -33px; width: 18px; height: 32px}
.batteryIconWrapper .bat20yellow { background-position: -20px -33px; width: 18px; height: 32px}
.batteryIconWrapper .bat30yellow { background-position: -40px -33px; width: 18px; height: 32px}
.batteryIconWrapper .bat40yellow { background-position: -60px -33px; width: 18px; height: 32px}
.batteryIconWrapper .bat50yellow { background-position: -80px -33px; width: 18px; height: 32px}
.batteryIconWrapper .bat60yellow { background-position: -100px -33px; width: 18px; height: 32px}
.batteryIconWrapper .bat70yellow { background-position: -120px -33px; width: 18px; height: 32px}
.batteryIconWrapper .bat80yellow { background-position: -139px -33px; width: 18px; height: 32px}
.batteryIconWrapper .bat90yellow { background-position: -159px -33px; width: 18px; height: 32px}
.batteryIconWrapper .bat100yellow { background-position: -179px -33px; width: 18px; height: 32px}
.batteryIconWrapper .bat10red { background-position: 0 -65px; width: 18px; height: 32px}
.batteryIconWrapper .bat20red { background-position: -20px -65px; width: 18px; height: 32px}
.batteryIconWrapper .bat30red { background-position: -40px -65px; width: 18px; height: 32px}
.batteryIconWrapper .bat40red { background-position: -60px -65px; width: 18px; height: 32px}
.batteryIconWrapper .bat50red { background-position: -80px -65px; width: 18px; height: 32px}
.batteryIconWrapper .bat60red { background-position: -100px -65px; width: 18px; height: 32px}
.batteryIconWrapper .bat70red { background-position: -120px -65px; width: 18px; height: 32px}
.batteryIconWrapper .bat80red { background-position: -139px -65px; width: 18px; height: 32px}
.batteryIconWrapper .bat90red { background-position: -159px -65px; width: 18px; height: 32px}
.batteryIconWrapper .bat100red { background-position: -179px -65px; width: 18px; height: 32px}
.batteryIconWrapper .bat0 { background-position: 0 -98px; width: 18px; height: 32px}
.batteryIconWrapper .acConnected {
background: transparent url('../icons/batteryIconSprite.png') no-repeat;
background-position: -20px -98px;
position: absolute;
height: 31px;
width: 22px;
top: -2px;
left: 11px;
}
.onlineIconWrapper .onlineIcon {
display: block;
background: transparent url('../icons/onlineIconSprite.png') no-repeat;
background-position: -19px 0;
height: 18px;
width: 18px;
position: relative;
margin-bottom: 10px;
}
.onlineIconWrapper .online { background-position: 0px 0; width: 18px; height: 18px}
.onlineIconWrapper .offline { background-position: -19px 0; width: 18px; height: 18px}
#voiceRecOuterWrapper, #voiceRecWrapper, #voiceRecImg, #voiceRecRing {
display: -webkit-box;
-webkit-box-pack: center;
-webkit-box-align: center;
padding: 0;
margin: 0;
}
#voiceRecOuterWrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
#voiceRecWrapper {
width: 180px;
height: 180px;
background-color: rgba(68,68,68,0.8);
border: 2px solid #009EEE;
-webkit-border-radius: 10px;
}
#voiceRecImg {
background: url('../icons/mic_sprite.png') no-repeat -2px -2px;
width: 180px;
height: 180px;
}
#voiceRecImg.error {
background-position: -2px -362px;
}
#voiceRecImg.success {
background-position: -2px -182px;
}
#voiceRecState {
width: 180px;
margin-top: 130px;
text-align: center;
color: #FFFFFF;
font-family: sans-serif;
font-size: 1em;
}
#voiceRecRing {
border: 3px solid #009EEE;
-webkit-border-radius: 110px;
height: 110px;
width: 110px;
-webkit-animation: pulse 2s linear infinite;
opacity: 0.0
}
@-webkit-keyframes pulse {
0% { -webkit-transform: scale(1.0); opacity: 0.0; }
10% { opacity: 0.7; }
50% { opacity: 1.0; }
90% { opacity: 0.7; }
100% { -webkit-transform: scale(1.0); opacity: 0.0; }
}

View File

@ -0,0 +1,876 @@
/***********************
* TTS Phonegap Plugin *
***********************/
function TTS() {
// var STOPPED = 0;
// var INITIALIZING = 1;
// var STARTED = 2;
/**
* Play the passed in text as synthesized speech
*
* @param {String} text
* @param {Object} successCallback
* @param {Object} errorCallback
*/
this.speak = function (text, successCallback, errorCallback) {
return cordova.exec(successCallback, errorCallback, "TTS", "speak", [text]);
};
/**
* Interrupt any existing speech, then speak the passed in text as synthesized speech
*
* @param {String} text
* @param {Object} successCallback
* @param {Object} errorCallback
*/
this.interrupt = function (text, successCallback, errorCallback) {
return cordova.exec(successCallback, errorCallback, "TTS", "interrupt", [text]);
};
/**
* Stop any queued synthesized speech
*
* @param {Object} successCallback
* @param {Object} errorCallback
*/
this.stop = function (successCallback, errorCallback) {
return cordova.exec(successCallback, errorCallback, "TTS", "stop", []);
};
/**
* Play silence for the number of ms passed in as duration
*
* @param {number} duration
* @param {object} successCallback
* @param {object} errorCallback
* @returns {*}
*/
this.silence = function(duration, successCallback, errorCallback) {
return cordova.exec(successCallback, errorCallback, "TTS", "silence", [duration]);
};
/**
* Set speed of speech. Usable from 30 to 500. Higher values make little difference.
*
* @param {number} speed
* @param {Object} successCallback
* @param {Object} errorCallback
* @returns {*}
*/
this.speed = function(speed, successCallback, errorCallback) {
return cordova.exec(successCallback, errorCallback, "TTS", "speed", [speed]);
};
/**
* Set pitch of speech. Useful values are approximately 30 - 300
*
* @param {number} pitch
* @param {Object} successCallback
* @param {Object} errorCallback
*/
this.pitch = function(pitch, successCallback, errorCallback) {
return cordova.exec(successCallback, errorCallback, "TTS", "pitch", [pitch]);
};
/**
* Starts up the TTS Service
*
* @param {Object} successCallback
* @param {Object} errorCallback
*/
this.startup = function(successCallback, errorCallback) {
return cordova.exec(successCallback, errorCallback, "TTS", "startup", []);
};
/**
* Shuts down the TTS Service if you no longer need it.
*
* @param {Object} successCallback
* @param {Object} errorCallback
*/
this.shutdown = function(successCallback, errorCallback) {
return cordova.exec(successCallback, errorCallback, "TTS", "shutdown", []);
};
/**
* Finds out if the language is currently supported by the TTS service.
*
* @param {Sting} lang
* @param {Object} successCallback
* @param {Object} errorCallback
*/
this.isLanguageAvailable = function(lang, successCallback, errorCallback) {
return cordova.exec(successCallback, errorCallback, "TTS", "isLanguageAvailable", [lang]);
};
/**
* Finds out the current language of the TTS service.
*
* @param {Object} successCallback
* @param {Object} errorCallback
*/
this.getLanguage = function(successCallback, errorCallback) {
return cordova.exec(successCallback, errorCallback, "TTS", "getLanguage", []);
};
/**
* Sets the language of the TTS service.
*
* @param {String} lang
* @param {Object} successCallback
* @param {Object} errorCallback
*/
this.setLanguage = function(lang, successCallback, errorCallback) {
return cordova.exec(successCallback, errorCallback, "TTS", "setLanguage", [lang]);
};
}
/**
* Load TTS
*/
if(!window.plugins) {
window.plugins = {};
}
if (!window.plugins.tts) {
window.plugins.tts = new TTS();
}
/**********************************************
* HeadsetListener plugin for Cordova/Phonegap *
**********************************************/
cordova.define("cordova/plugin/headset", function(require, exports, module) {
/**
* This class contains information about the current headset status.
* @constructor
*/
var cordova = require('cordova'),
exec = require('cordova/exec');
/**
* @return {Number}
*/
function handlers() {
return headset.channels.headsetstatus.numHandlers;
}
/**
* @constructor
*/
var Headset = function() {
this._isPlugged = false;
// Create new event handlers on the window (returns a channel instance)
this.channels = {
headsetstatus:cordova.addWindowEventHandler('headsetstatus')
};
for (var key in this.channels) {
this.channels[key].onHasSubscribersChange = Headset.onHasSubscribersChange;
}
};
/**
* Event handlers for when callbacks get registered for the headset.
* Keep track of how many handlers we have so we can start and stop the native headset listener.
*/
Headset.onHasSubscribersChange = function() {
// If we just registered the first handler, make sure native listener is started.
if (this.numHandlers === 1 && handlers() === 1) {
//exec(successFunc, failureFunc, 'service', 'action', [jsonArgs]);
exec(headset._status, headset._error, 'HeadsetListener', 'start', []);
} else if (handlers() === 0) {
exec(null, null, 'HeadsetListener', 'stop', []);
}
};
/**
* Callback for headset status
*
* @param {Object} info keys: isPlugged
*/
Headset.prototype._status = function(info) {
if (info) {
var me = headset;
if (me._isPlugged !== info.isPlugged) {
// Fire headsetstatus event
cordova.fireWindowEvent('headsetstatus', info);
}
me._isPlugged = info.isPlugged;
}
};
/**
* Error callback for battery start
*/
Headset.prototype._error = function(e) {
console.log("Error initializing Headset listener: " + e);
};
var headset = new Headset();
module.exports = headset;
});
var headset = cordova.require('cordova/plugin/headset');
/************************************
* VoiceRecognition Phonegap Plugin *
************************************/
cordova.define("cordova/plugin/voiceRecognition", function(require, exports, module) {
/**
* This class contains voiceRecognition functions.
* @constructor
*/
var cordova = require('cordova'),
exec = require('cordova/exec');
var errorCodes = {
1:'Network operation timed out',
2:'Other network related errors',
3:'Audio recording error',
4:'Server sends error status',
5:'Other client side errors',
6:'No speech input',
7:'No recognition result matched',
8:'RecognitionService busy',
9:'Insufficient permissions'
};
var states = {
STATE_RECOGNISE_END: 0,
STATE_RECOGNISE_READY: 1,
STATE_RECOGNISE_BEGIN: 2,
STATE_RECOGNISE_RESULTS:3,
STATE_RECOGNISE_ERROR: 9
};
function handlers() {
return voiceRecognition.channels.voicerecognition_begin.numHandlers +
voiceRecognition.channels.voicerecognition_end.numHandlers +
voiceRecognition.channels.voicerecognition_error.numHandlers +
voiceRecognition.channels.voicerecognition_ready.numHandlers +
voiceRecognition.channels.voicerecognition_result.numHandlers;
}
var VoiceRecognition = function() {
this._state = 0;
// Create new event handlers on the window (returns a channel instance)
this.channels = {
voicerecognition_begin:cordova.addWindowEventHandler('voicerecognition_begin'),
voicerecognition_end:cordova.addWindowEventHandler('voicerecognition_end'),
voicerecognition_error:cordova.addWindowEventHandler('voicerecognition_error'),
voicerecognition_ready:cordova.addWindowEventHandler('voicerecognition_ready'),
voicerecognition_result:cordova.addWindowEventHandler('voicerecognition_result')
};
for (var key in this.channels) {
this.channels[key].onHasSubscribersChange = VoiceRecognition.onHasSubscribersChange;
}
};
/**
* Event handlers for when callbacks get registered for the voiceRecognition.
* Keep track of how many handlers we have so we can start and stop the voiceRecognition listener
*/
VoiceRecognition.onHasSubscribersChange = function() {
// If we just registered the first handler, make sure native listener is started.
if (this.numHandlers === 1 && handlers() === 1) {
exec(voiceRecognition._status, voiceRecognition._error, "VoiceRecognition", "start", []);
} else if (handlers() === 0) {
exec(null, null, "VoiceRecognition", "stop", []);
}
};
/**
* Callback for battery status
*
* @param {Object} info keys: level, isPlugged
*/
VoiceRecognition.prototype._status = function(info) {
if (info) {
var me = voiceRecognition;
var state = info.state;
if (me._state !== state) {
// Fire events
if (state == states.STATE_RECOGNISE_END) {
cordova.fireWindowEvent('voicerecognition_end', null);
} else if (state == states.STATE_RECOGNISE_READY) {
cordova.fireWindowEvent('voicerecognition_ready', null);
} else if (state == states.STATE_RECOGNISE_BEGIN) {
cordova.fireWindowEvent('voicerecognition_begin', null);
} else if (state == states.STATE_RECOGNISE_RESULTS) {
cordova.fireWindowEvent('voicerecognition_result', {word: info.result});
} else if (state == states.STATE_RECOGNISE_ERROR) {
cordova.fireWindowEvent('voicerecognition_error', {code: info.errorCode, description: errorCodes[info.errorCode]});
}
}
me._state = state;
}
};
/**
* Error callback for voice recognition start
*/
VoiceRecognition.prototype._error = function(e) {
console.log("Error initializing voice recognition listener: " + e);
};
var voiceRecognition = new VoiceRecognition();
module.exports = voiceRecognition;
});
var voiceRecognition = cordova.require('cordova/plugin/voiceRecognition');
/******************************
* Begin WebViewControl parts *
******************************/
var deviceControl = {
exec: function(command, params) {
if(cordova.exec) {
cordova.exec(
function(winParam) {},
function(error) {},
'DeviceControl',
command,
params
);
}
},
/**
* Set screen brightness
* @param level
*/
screenBrightness: function(level) {
deviceControl.exec('setScreenBrightness', [level]);
},
/**
* Set volume
* @param level
*/
volume: function(level) {
deviceControl.exec('setVolume', [level]);
},
/**
* Set keep screen on / off
* @param value
*/
keepScreenOn: function(value) {
deviceControl.exec('setKeepScreenOn', [value]);
},
/**
* Set Toast Message
* @param message
*/
toastMessage: function(message) {
deviceControl.exec('showToast', [message]);
},
/**
* Perform a reload
*/
reload: function() {
window.location.reload();
},
//"http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3"
audioPlay: function(value) {
audioPlayer.playAudio(value);
},
audioStop: function() {
audioPlayer.stopAudio();
},
ttsSay: function(txt) {
ttsPlayer.say(txt);
},
voiceRec: function(opt) {
fhemWVC.startVoiceRecognition();
},
newUrl: function(opt) {
location.href = decodeURIComponent(opt);
}
};
var audioPlayer = {
media: null,
// Play audio
playAudio: function(src, showToast) {
showToast = (typeof(showToast) == 'undefined') ? true : showToast;
if (showToast) {
deviceControl.toastMessage('playAudio(' + src + ')');
}
audioPlayer.media = new Media(
src,
function() {},
function(error) {
deviceControl.toastMessage('Error: playAudio(' + error.message + ' [' + error.code + '])');
}
);
// Play audio
audioPlayer.media.play();
},
// Stop audio
stopAudio: function() {
if (audioPlayer.media) {
audioPlayer.media.stop();
}
}
};
var ttsPlayer = {
init: function() {
window.plugins.tts.setLanguage('de', function(){}, function(){});
window.plugins.tts.startup(
function() {}, // Success
function() {deviceControl.toastMessage('TTS startup error.');}
);
},
say: function(txt) {
if (txt) {
deviceControl.toastMessage('TTS Say: ' + txt);
window.plugins.tts.speak(
txt,
function() {}, // Success
function() {deviceControl.toastMessage('TTS error.');}
);
}
}
};
var wvcApp;
wvcApp = {
exitOnBackKey:false,
// Application Constructor
initialize:function (callback) {
document.addEventListener('deviceready', function () {
// console.log('cordova ready?');
wvcApp.onDeviceReady();
if (callback) {
callback();
}
}, false);
},
// deviceready Event Handler
onDeviceReady:function () {
if (wvcApp.exitOnBackKey) {
document.addEventListener('backbutton', wvcApp.onBackKeyDown, false);
}
document.addEventListener('offline', wvcApp.onOffline, false);
document.addEventListener('online', wvcApp.onOnline, false);
document.addEventListener('pause', wvcApp.onPause, false);
document.addEventListener('resume', wvcApp.onResume, false);
window.addEventListener('batterystatus', wvcApp.onBatteryStatus, false);
window.addEventListener('voicerecognition_begin', wvcApp.onVoiceRecognitionBegin, false);
window.addEventListener('voicerecognition_end', wvcApp.onVoiceRecognitionEnd, false);
window.addEventListener('voicerecognition_error', wvcApp.onVoiceRecognitionError, false);
window.addEventListener('voicerecognition_ready', wvcApp.onVoiceRecognitionReady, false);
window.addEventListener('voicerecognition_result', wvcApp.onVoiceRecognitionResult, false);
window.addEventListener('headsetstatus', wvcApp.onHeadsetStatus, false);
ttsPlayer.init();
// document.addEventListener('menubutton', wvcApp.onMenuKeyDown, false);
},
removeEventListener:function () {
document.removeEventListener('offline', wvcApp.onOffline, false);
document.removeEventListener('online', wvcApp.onOnline, false);
document.removeEventListener('pause', wvcApp.onPause, false);
document.removeEventListener('resume', wvcApp.onResume, false);
window.removeEventListener('batterystatus', wvcApp.onBatteryStatus, false);
window.removeEventListener('voicerecognition_begin', wvcApp.onVoiceRecognitionBegin, false);
window.removeEventListener('voicerecognition_end', wvcApp.onVoiceRecognitionEnd, false);
window.removeEventListener('voicerecognition_error', wvcApp.onVoiceRecognitionError, false);
window.removeEventListener('voicerecognition_ready', wvcApp.onVoiceRecognitionReady, false);
window.removeEventListener('voicerecognition_result', wvcApp.onVoiceRecognitionResult, false);
window.removeEventListener('headsetstatus', wvcApp.onHeadsetStatus, false);
},
onVoiceRecognitionBegin:function () {
// deviceControl.toastMessage('Voice recognition Begin');
},
onVoiceRecognitionEnd:function () {
var recRing = document.getElementById('voiceRecRing');
if (recRing) {
document.getElementById('voiceRecImg').removeChild(recRing);
}
setTimeout(function() {
var recDiv = document.getElementById('voiceRecOuterWrapper');
if (recDiv) {
document.body.removeChild(recDiv);
}
}, 2000);
},
onVoiceRecognitionError:function (error) {
// deviceControl.toastMessage('Voice recognition error: ' + error.description + ' (' + error.code + ')');
audioPlayer.playAudio('/android_asset/sounds/voice_recognition_error.mp3', false);
var recDiv = document.createElement('div');
recDiv.setAttribute('id','voiceRecState');
recDiv.innerHTML = error.description;
var recImg = document.getElementById('voiceRecImg');
recImg.appendChild(recDiv);
recImg.setAttribute('class','error');
fhemWVC.informFhem('voiceRecognitionLastError', error.code + ':' + error.description);
},
onVoiceRecognitionReady:function () {
audioPlayer.playAudio('/android_asset/sounds/voice_recognition_start.mp3', false);
var recDiv = document.createElement('div');
recDiv.setAttribute('id','voiceRecOuterWrapper');
recDiv.innerHTML = '<div id="voiceRecWrapper"><div id="voiceRecImg"><div id="voiceRecRing"></div></div></div>';
document.body.appendChild(recDiv);
},
onVoiceRecognitionResult:function (result) {
audioPlayer.playAudio('/android_asset/sounds/voice_recognition_ok.mp3', false);
var recImg = document.getElementById('voiceRecImg');
recImg.setAttribute('class','success');
fhemWVC.informFhem('voiceRecognitionLastResult', result.word);
deviceControl.toastMessage('Voice recognition result: ' + result.word);
// deviceControl.ttsSay(result.word);
},
onHeadsetStatus:function (info) {
if (info.isPlugged) {
deviceControl.toastMessage('The headphones have been plugged in!');
} else {
deviceControl.toastMessage('The headphones have been unplugged!');
}
},
// Back key event handler
onBackKeyDown:function () {
overrideBackKey = false;
if (typeof(container.wvcApp.onBackKeyDown) == 'function') {
overrideBackKey = container.wvcApp.onBackKeyDown();
}
if (!overrideBackKey) {
navigator.app.exitApp();
}
},
onOffline:function () {
if (typeof(fhemWVC.onOffline) == 'function') {
fhemWVC.onOffline()
}
},
onOnline:function () {
if (typeof(fhemWVC.onOnline) == 'function') {
fhemWVC.onOnline()
}
},
onPause:function () {
if (typeof(fhemWVC.onPause) == 'function') {
fhemWVC.onPause()
}
},
onResume:function () {
if (typeof(fhemWVC.onResume) == 'function') {
fhemWVC.onResume()
}
},
onBatteryStatus:function (info) {
if (typeof(fhemWVC.onBatteryStatus) == 'function') {
fhemWVC.onBatteryStatus(info);
}
},
onConnectionError:function (errorCode, description, failingUrl) {
if (typeof(fhemWVC.onConnectionError) == 'function') {
fhemWVC.onConnectionError(errorCode, description, failingUrl);
}
}
};
/* ************************************************************************ */
var fhemWVC = {
httpRequest: null,
currResponseLine: 0,
appId: 12345,
debug: false,
deviceState: {
powerLevel: null,
powerIsPlugged: null
},
reconnect: function(timeout) {
setTimeout(function() {
fhemWVC.connect();
}, timeout);
},
connect: function () {
fhemWVC.currResponseLine = 0;
fhemWVC.httpRequest = new XMLHttpRequest();
fhemWVC.httpRequest.open("GET", '?XHR=1&inform=type=status;filter=room=all&timestamp=' + new Date().getTime(), true);
fhemWVC.httpRequest.onreadystatechange = fhemWVC.parse;
fhemWVC.httpRequest.send(null);
},
parse: function() {
var httpRequest = fhemWVC.httpRequest;
if (!fhemWVC.haveAppDevices()) {
return;
}
if(httpRequest.readyState == 4) {
fhemWVC.reconnect(100);
return;
}
if(httpRequest.readyState != 3) {
return;
}
var lines = httpRequest.responseText.split("\n");
//Pop the last (maybe empty) line after the last "\n"
//We wait until it is complete, i.e. terminated by "\n"
lines.pop();
for(var i = fhemWVC.currResponseLine; i < lines.length; i++) {
var params = lines[i].split('<<', 3); // Complete arg, 0 -> name, 1 -> value
if(params.length != 3) {
continue;
}
if (wvcDevices[fhemWVC.appId] && wvcDevices[fhemWVC.appId] == params[0]) {
var fnValue = params[1].split(' '); // fn and value
var fn = fnValue.shift();
var value = fnValue.join(' ');
fhemWVC.log(fn + " / " + value);
if (typeof(deviceControl[fn]) != 'undefined' && typeof(deviceControl[fn]) == 'function') {
deviceControl[fn](value);
}
break;
}
}
//Next time, we continue at the next line
fhemWVC.currResponseLine = lines.length;
},
haveAppDevices: function() {
var retVal = false;
if (wvcDevices) {
retVal = true;
}
return retVal;
},
/**
*
*/
initialize: function() {
var wvcDevices = {};
wvcApp.initialize(function(){
fhemWVC.createIcons();
if (typeof(wvcUserCssFile) != 'undefined') {
fhemWVC.injectCss(wvcUserCssFile);
}
fhemWVC.reconnect(50);
fhemWVC.setConnectionState(navigator.connection.type);
if (typeof(window.appInterface) != 'undefined' && typeof(window.appInterface) == 'object') {
if (typeof(window.appInterface.getAppId) != 'undefined' && typeof(window.appInterface.getAppId) == 'function') {
fhemWVC.appId = window.appInterface.getAppId();
}
}
});
window.onunload=function(){
wvcApp.removeEventListener();
};
},
createIcons: function() {
fhemWVC.injectCss('webviewcontrol.css');
var iconDiv = document.createElement('div');
iconDiv.innerHTML = '<div> <div class="onlineIconWrapper"><div id="fhemWVC_onlineIcon" class="onlineIcon"></div></div>';
iconDiv.innerHTML+= '<div onClick="fhemWVC.startVoiceRecognition();" class="batteryIconWrapper"><div id="fhemWVC_batteryIcon" class="batteryIcon bat0"><div id="fhemWVC_acConnectedIcon" class="acConnected"></div><div id="fhemWVC_batteryPercent" class="txtPercent">?%</div></div></div> </div>';
iconDiv.setAttribute('id','htIcons');
iconDiv.setAttribute('style','position: fixed; right: 0px; bottom: 0px; width: 32px; height: 80px;');
document.body.appendChild(iconDiv);
},
startVoiceRecognition: function() {
cordova.exec(null, fhemWVC.voiceRecognitionNotPresentError, "VoiceRecognition", "init", []);
cordova.exec(null, null, "VoiceRecognition", "startRecognition", []);
},
voiceRecognitionNotPresentError: function (errTxt) {
deviceControl.toastMessage(errTxt);
fhemWVC.informFhem('voiceRecognitionLastError', '-1:' + errTxt);
},
updateBatteryIcon: function(percent, isPlugged) {
var txtPercent = document.getElementById('fhemWVC_batteryPercent');
var batteryIcon = document.getElementById('fhemWVC_batteryIcon');
var acConnectedIcon = document.getElementById('fhemWVC_acConnectedIcon');
if (isPlugged) {
acConnectedIcon.className = 'acConnected';
} else {
acConnectedIcon.className = 'hidden';
}
percent = parseInt(percent);
percent = (percent > 0 ) ? percent : 0;
percent = (percent < 100 ) ? percent : 100;
txtPercent.innerText = percent + '%';
var color = (percent > 25) ? 'yellow' : 'red';
color = (percent > 50) ? 'green' : color;
percent = (percent > 0 && percent < 10) ? 10 : percent;
percent = parseInt(percent/10) * 10;
var batClass = (percent > 0) ? 'bat' + percent + color : 'bat0';
batteryIcon.className = 'batteryIcon ' + batClass;
},
/**
* Inject given css file
* @param cssFile
*/
injectCss: function(cssFile) {
var css = document.createElement('link');
css.setAttribute('href','/fhem/pgm2/' + cssFile);
css.setAttribute('rel','stylesheet');
document.getElementsByTagName('head')[0].appendChild(css);
},
injectRemoteDebugger: function() {
var js = document.createElement('script');
js.setAttribute('src','http://debug.phonegap.com/target/target-script-min.js#webViewControl');
js.setAttribute('type','text/javascript');
document.getElementsByTagName('head')[0].appendChild(js);
},
informFhem: function(command, value) {
var webViewClientId = (typeof(wvcDevices[fhemWVC.appId]) != 'undefined') ? wvcDevices[fhemWVC.appId] : 'undefined';
var getVars = '?id=' + webViewClientId;
if (command == 'powerState') {
getVars+= '&powerLevel=' + fhemWVC.deviceState.powerLevel;
getVars+= '&powerPlugged=' + fhemWVC.deviceState.powerIsPlugged;
} else {
getVars+= '&' + command + '=' + value;
}
var httpRequest = new XMLHttpRequest();
httpRequest.open("GET", '/fhem/webviewcontrol' + getVars, true);
httpRequest.send(null);
},
setConnectionState: function(networkState) {
var onlineIcon = document.getElementById('fhemWVC_onlineIcon');
var onlineClass = 'offline';
// we set the network icon on green on wifi or ethernet connection only
if (networkState == Connection.ETHERNET || networkState == Connection.WIFI) {
onlineClass = 'online';
}
onlineIcon.className = 'onlineIcon ' + onlineClass;
},
onConnectionError: function(errorCode, description, failingUrl) {
},
onBatteryStatus: function(info) {
var inform = false;
if (fhemWVC.deviceState.powerLevel != info.level || fhemWVC.deviceState.powerIsPlugged != info.isPlugged) {
inform = true;
}
fhemWVC.updateBatteryIcon(info.level, info.isPlugged);
fhemWVC.deviceState.powerLevel = info.level;
fhemWVC.deviceState.powerIsPlugged = info.isPlugged;
if (inform) {
fhemWVC.informFhem('powerState');
}
},
onResume: function() {
deviceControl.toastMessage('App resumes from pause.')
},
onPause: function() {
deviceControl.toastMessage('App go to pause.')
},
onOnline: function() {
deviceControl.toastMessage('Network online')
},
onOffline: function() {
deviceControl.toastMessage('Network offline')
},
log: function(dbgObj) {
if (fhemWVC.debug) {
console.log (dbgObj);
}
}
};
//fhemWVC.injectRemoteDebugger();
fhemWVC.initialize();
/*
// uncomment this for testing without the device
document.addEventListener("DOMContentLoaded", function() {
fhemWVC.appId = '00001234'; // Set alternative appId here
fhemWVC.createIcons();
fhemWVC.reconnect(50);
fhemWVC.debug = true;
fhemWVC.onBatteryStatus({level: 53, isPlugged: false});
},false);
*/