fhem-mirror/FHEM/95_Babble.pm
phenning c92b38e2c0 36_Shelly.pm: Bugfix
95_Babble.pm: Minimale Ergänzungen

git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@19819 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2019-07-12 06:54:38 +00:00

2328 lines
91 KiB
Perl

########################################################################################
#
# Babble.pm
#
# FHEM module for speech control of FHEM devices
#
# Prof. Dr. Peter A. Henning
#
# $Id$
#
########################################################################################
#
# This programm is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# The GNU General Public License can be found at
# http://www.gnu.org/copyleft/gpl.html.
# A copy is found in the textfile GPL.txt and important notices to the license
# from the author is found in LICENSE.txt distributed with these scripts.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
########################################################################################
package main;
use strict;
use warnings;
use vars qw(%defs); # FHEM device/button definitions
use vars qw(%intAt); # FHEM at definitions
use vars qw($FW_ME);
use JSON; # imports encode_json, decode_json, to_json and from_json.
use Encode;
my $rive = 0;
my $riveinterpreter;
#-- RiveScript missing in System
if (eval {require RiveScript;1;} ne 1) {
Log 1,"[Babble] the RiveScript module is missing from your Perl installation - chatbot functionality not available";
Log 1," check cpan or https://github.com/aichaos/rivescript-perl for download and installation";
} else {
RiveScript->import();
$rive = 1;
Log 1,"[Babble] the RiveScript module has been imported successfully, chatbot functionality available";
}
#########################
# Global variables
my $babblelinkname = "babbles"; # link text
my $babblehiddenroom = "babbleRoom"; # hidden room
my $babblepublicroom = "babble"; # public room
my $babbleversion = "1.42";
my @babblerows;
my %babble_transtable_EN = (
"ok" => "OK",
"notok" => "Not OK",
"start" => "Start",
"end" => "End",
"add" => "Add",
"added" => "added",
"remove" => "Remove",
"removed" => "removed",
"modify" => "Modify",
"modified" => "modified",
"cancel" => "Cancel",
"status" => "Status",
"notstarted" => "Not started",
"next" => "Next",
"babbledev" => "Babble Devices",
"babbleplaces" => "Babble Places",
"babbleverbs" => "Babble Verbs",
"babblename" => "Babble Name",
"babbletest" => "Babble Test",
"fhemname" => "FHEM Name",
"device" => "Device",
"place" => "Place",
"places" => "Places",
"rooms" => "Rooms",
"verb" => "Verb",
"target" => "Target",
"result" => "Result",
"unknown" => "unknown",
"infinitive" => "Infinitive",
"conjugations" => "Conjugations and Variations",
"helptext" => "Help Text",
"confirm" => "Confirmation",
"speak" => "Please speak",
"followedby" => "followed by",
"placespec" => "a place specification",
"dnu" => "Sorry, I did not understand this",
"input" => "Input",
"test" => "Test",
"exec" => "Execute",
"value" => "Value",
"save" => "Save",
"action" => "Action",
"time" => "Time",
"description" => "Description",
"settings" => "Settings",
"babbles" => "Babble System",
"setparms" => "Set Parameters",
#--
"hallo" => "Hallo",
"state" => "Security",
"unlocked" => "Unlocked",
"locked" => "Locked"
);
my %babble_transtable_DE = (
"ok" => "OK",
"notok" => "Nicht OK",
"start" => "Start",
"end" => "Ende",
"add" => "Hinzufügen",
"added" => "hinzugefügt",
"remove" => "Entfernen",
"removed" => "entfernt",
"modify" => "Ändern",
"modified" => "geändert",
"cancel" => "Abbruch",
"status" => "Status",
"notstarted" => "Nicht gestartet",
"next" => "Nächste",
"babbledev" => "Babble Devices",
"babbleplaces" => "Babble Orte",
"babbleverbs" => "Babble Verben",
"babblename" => "Babble Name",
"babbletest" => "Babble Test",
"fhemname" => "FHEM Name",
"device" => "Gerät",
"place" => "Ort",
"places" => "Orte",
"rooms" => "Räume",
"verb" => "Verb",
"target" => "Ziel",
"result" => "Ergebnis",
"unknown" => "unbekannt",
"infinitive" => "Infinitiv",
"conjugations" => "Konjugationen und Variationen",
"helptext" => "Hilfetext",
"confirm" => "Bestätigung",
"speak" => "Bitte sprich",
"followedby" => "gefolgt von",
"placespec" => "einer Ortsangabe",
"dnu" => "Es tut mir leid, das habe ich nicht verstanden",
"input" => "Input",
"test" => "Test",
"exec" => "Ausführung",
"value " => "Wert",
"save" => "Sichern",
"action" => "Aktion",
"time" => "Zeit",
"description" => "Beschreibung",
"settings" => "Einstellungen",
"babbles" => "Babble",
"setparms" => "Parameter setzen",
#--
"hallo" => "Hallo",
"state" => "Sicherheit",
"unlocked" => "Unverschlossen",
"locked" => "Verschlossen"
);
my $babble_tt;
#########################################################################################
#
# Babble_Initialize
#
# Parameter hash = hash of device addressed
#
#########################################################################################
sub Babble_Initialize ($) {
my ($hash) = @_;
$hash->{DefFn} = "Babble_Define";
$hash->{SetFn} = "Babble_Set";
$hash->{GetFn} = "Babble_Get";
$hash->{UndefFn} = "Babble_Undef";
#$hash->{AttrFn} = "Babble_Attr";
my $attst = "lockstate:locked,unlocked helpFunc confirmFunc noChatBot:0,1 dnuFile testParm0 testParm1 testParm2 testParm3 ".
"remoteFHEM0 remoteFHEM1 remoteFHEM2 remoteFHEM3 remoteFunc0 remoteFunc1 remoteFunc2 remoteFunc3 remoteToken0 remoteToken1 remoteToken2 remoteToken3 ".
"babbleIds babblePreSubs:textField-long babbleDevices:textField-long babblePlaces:textField-long babbleNotPlaces:textField-long babbleVerbs:textField-long ".
"babbleVerbParts:textField-long babblePrepos:textField-long babbleQuests:textField-long babbleArticles:textField-long babbleStatus:textField-long ".
"babbleWrites:textField-long babbleTimes:textField-long";
$hash->{AttrList} = $attst;
if( !defined($babble_tt) ){
#-- in any attribute redefinition readjust language
my $lang = AttrVal("global","language","EN");
if( $lang eq "DE"){
$babble_tt = \%babble_transtable_DE;
}else{
$babble_tt = \%babble_transtable_EN;
}
}
$babblelinkname = $babble_tt->{"babbles"};
$data{FWEXT}{babblex}{LINK} = "?room=".$babblehiddenroom;
$data{FWEXT}{babblex}{NAME} = $babblelinkname;
#-- Create a new RiveScript interpreter
Babble_createRive($hash)
if( $rive==1 && !defined($hash->{Rive})) ;
return undef;
}
#########################################################################################
#
# Babble_Define - Implements DefFn function
#
# Parameter hash = hash of device addressed, def = definition string
#
#########################################################################################
sub Babble_Define ($$) {
my ($hash, $def) = @_;
my $now = time();
my $name = $hash->{NAME};
$hash->{VERSION} = $babbleversion;
#-- readjust language
my $lang = AttrVal("global","language","EN");
if( $lang eq "DE"){
$babble_tt = \%babble_transtable_DE;
}else{
$babble_tt = \%babble_transtable_EN;
}
readingsSingleUpdate( $hash, "state", "Initialized", 1 );
$babblehiddenroom = defined($attr{$name}{"hiddenroom"}) ? $attr{$name}{"hiddenroom"} : $babblehiddenroom;
$babblepublicroom = defined($attr{$name}{"publicroom"}) ? $attr{$name}{"publicroom"} : $babblepublicroom;
$data{FWEXT}{babblex}{LINK} = "?room=".$babblehiddenroom;
$data{FWEXT}{babblex}{NAME} = $babblelinkname;
$attr{$name}{"room"} = $babblehiddenroom;;
my $date = Babble_restore($hash,0);
#-- data seems to be ok, restore
if( defined($date) ){
Babble_restore($hash,1);
Log3 $name,1,"[Babble_Define] data hash restored from save file with date $date";
#-- intialization
}else{
$hash->{DATA}{"devs"}=();
$hash->{DATA}{"devcontacts"}=();
$hash->{DATA}{"rooms"}=();
$hash->{DATA}{"splaces"}=();
$hash->{DATA}{"places"}=();
$hash->{DATA}{"commands"}=();
$hash->{DATA}{"help"}=();
$hash->{DATA}{"status"}=();
$hash->{DATA}{"writes"}=();
$hash->{DATA}{"times"}=();
Babble_checkattrs($hash);
Log3 $name,1,"[Babble_Define] data hash is initialized";
}
#-- Create a new RiveScript interpreter
Babble_createRive($hash)
if( $rive==1 && !defined($hash->{Rive})) ;
$modules{babble}{defptr}{$name} = $hash;
RemoveInternalTimer($hash);
InternalTimer ($now + 5, 'Babble_CreateEntry', $hash, 0);
return;
}
#########################################################################################
#
# Babble_Undef - Implements Undef function
#
# Parameter hash = hash of device addressed, def = definition string
#
#########################################################################################
sub Babble_Undef ($$) {
my ($hash,$arg) = @_;
my $name = $hash->{NAME};
RemoveInternalTimer($hash);
delete $data{FWEXT}{babblex};
if (defined $defs{$name."_weblink"}) {
FW_fC("delete ".$name."_weblink");
Log3 $hash, 3, "[".$name. " V".$babbleversion."]"." Weblink ".$name."_weblink deleted";
}
return undef;
}
#########################################################################################
#
# Babble_Attr - Implements Attr function
#
# Parameter hash = hash of device addressed, ???
#
#########################################################################################
sub Babble_Attr($$$) {
my ($cmd, $name, $attrName, $attrVal) = @_;
my $hash = $defs{"$name"};
#-- in any attribute redefinition readjust language
my $lang = AttrVal("global","language","EN");
if( $lang eq "DE"){
$babble_tt = \%babble_transtable_DE;
}else{
$babble_tt = \%babble_transtable_EN;
}
return;
}
#########################################################################################
#
# Babble_CreateEntry - Puts the babble entry into the FHEM menu
#
# Parameter hash = hash of device addressed
#
#########################################################################################
sub Babble_CreateEntry($) {
my ($hash) = @_;
my $name = $hash->{NAME};
if (!defined $defs{$name."_weblink"}) {
FW_fC("define ".$name."_weblink weblink htmlCode {Babble_Html(\"".$name."\")}");
Log3 $hash, 3, "[".$name. " V".$babbleversion."]"." Weblink ".$name."_weblink created";
}
FW_fC("attr ".$name."_weblink room ".$babblehiddenroom);
foreach my $dn (sort keys %defs) {
if ($defs{$dn}{TYPE} eq "FHEMWEB" && $defs{$dn}{NAME} !~ /FHEMWEB:/) {
my $hr = AttrVal($defs{$dn}{NAME}, "hiddenroom", "");
if (index($hr,$babblehiddenroom) == -1){
if ($hr eq "") {
FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$babblehiddenroom);
}else {
FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$hr.",".$babblehiddenroom);
}
Log3 $hash, 3, "[".$name. " V".$babbleversion."]"." Added hidden room '".$babblehiddenroom."' to ".$defs{$dn}{NAME};
}
}
}
#-- recover state from stored readings
readingsBeginUpdate($hash);
#readingsBulkUpdate( $hash, "state", $mga);
readingsEndUpdate( $hash,1 );
}
#########################################################################################
#
# Babble_Set - Implements the Set function
#
# Parameter hash = hash of device addressed
#
#########################################################################################
sub Babble_Set($@) {
my ( $hash, $name, $cmd, @args ) = @_;
if ( $cmd =~ /^((talk)|(doit))/ ) {
#-- put args together, then split again at comma boundaries
my ($str,@newargs) = split(',',join(' ',@args));
return Babble_DoIt($name,$str,@newargs);
#-----------------------------------------------------------
}elsif ( $cmd =~ /^lock(ed)?$/ ) {
readingsSingleUpdate( $hash, "lockstate", "locked", 0 );
return;
#-----------------------------------------------------------
} elsif ( $cmd =~ /^unlock(ed)?$/ ) {
readingsSingleUpdate( $hash, "lockstate", "unlocked", 0 );
return;
#-----------------------------------------------------------
} elsif ( $cmd =~ /^rivereload/ ) {
delete $hash->{Rive};
return Babble_createRive($hash);
#-----------------------------------------------------------
} elsif ( $cmd =~ /^test/ ) {
return Babble_Test($hash);
#-----------------------------------------------------------
} elsif ( $cmd =~ /^save/ ) {
return Babble_save($hash);
#-----------------------------------------------------------
} elsif ( $cmd =~ /^restore/ ) {
return Babble_restore($hash,1);
} else {
my $str = "[babble] Unknown argument " . $cmd . ", choose one of talk doit locked:noArg unlocked:noArg save:noArg restore:noArg test:noArg ";
$str .= "rivereload:noArg"
if($rive == 1 && AttrVal($name,"noChatBot",0) != 1);
return $str;
}
}
#########################################################################################
#
# Babble_Get - Implements the Get function
#
# Parameter hash = hash of device addressed
#
#########################################################################################
sub Babble_Get($@) {
my ($hash, @a) = @_;
my $res = "";
my $ip;
my $name = $hash->{NAME};
my $arg = (defined($a[1]) ? $a[1] : "");
if ($arg eq "version") {
return "babble.version => $babbleversion";
}elsif ($arg eq "tokens") {
for( my $i=0;$i<=3;$i++ ){
$ip = AttrVal($name,"remoteFHEM$i",undef);
if( $ip ){
Babble_getcsrf($name,$ip,$i);
}
}
} else {
return "Unknown argument $arg choose one of version:noArg tokens:noArg";
}
}
#########################################################################################
#
# Babble_save
#
# Parameter hash = hash of the babble device
#
#########################################################################################
sub Babble_save($) {
my ($hash) = @_;
my $date = TimeNow();
my $name = $hash->{NAME};
$hash->{DATA}{"savedate"} = $date;
readingsSingleUpdate( $hash, "savedate", $date, 1 );
my $jhash0 = toJSON($hash->{DATA});
#$jhash0 = decode_utf8( $jhash0 );
if( ReadingsVal($name,"lockstate","locked") ne "locked" ){
my $error = FileWrite("babbleFILE",$jhash0);
Log3 $name,1,"[Babble_save]";
}else{
Log3 $name, 1, "[Babble] attempt to save data failed due to lockstate";
Log3 $name, 5, " ".Dumper($jhash0);
}
return;
}
sub Babble_savename($){
my ($name) = @_;
my $hash = $defs{$name};
Babble_save($hash);
}
#########################################################################################
#
# Babble_restore
#
# Parameter hash = hash of the babble device
#
#########################################################################################
sub Babble_restore($$) {
my ($hash,$doit) = @_;
my $name = $hash->{NAME};
my ($error,@lines) = FileRead("babbleFILE");
if( defined($error) && $error ne "" ){
Log3 $name,1,"[Babble_restore] read error=$error";
return undef;
}
my $json = JSON->new->utf8;
my $jhash0 = join('',@lines);
$jhash0 = encode_utf8( $jhash0 );
my $jhash1 = eval{ $json->decode( $jhash0 ) };
my $date = $jhash1->{"savedate"};
#-- just for the first time, reading an old savefile
$date = localtime(time)
if( !defined($date));
readingsSingleUpdate( $hash, "savedate", $date, 0 );
if( $doit==1 ){
$hash->{DATA} = {%{$jhash1}};
Log3 $name,1,"[Babble_restore] Data hash restored from save file with date ".$date;
return 1;
}else{
return $date;
}
}
#########################################################################################
#
# Babble_Test - Implements a variety of tests
#
# Parameter hash = hash of device addressed
#
#########################################################################################
sub Babble_Test($) {
my ($hash) = @_;
my $name = $hash->{NAME};
my $str = "";
$str .= "\nA.1:".Babble_DoIt($name,"guten morgen","testit",0);
$str .= "\nA.2:".Babble_DoIt($name,"gute nacht","testit",0);
$str .= "\nA.3:".Babble_DoIt($name,"guten morgen jeannie","testit",0);
$str .= "\nA.4:".Babble_DoIt($name,"gute nacht jeannie","testit",0);
$str .= "\n";
$str .= "\nB.1:".Babble_DoIt($name,"schalte das gerät an","testit",0);
$str .= "\nB.2:".Babble_DoIt($name,"schalte gerät an","testit",0);
$str .= "\nB.3:".Babble_DoIt($name,"mach das gerät an","testit",0);
$str .= "\nB.4:".Babble_DoIt($name,"das gerät ausschalten","testit",0);
$str .= "\nB.5:".Babble_DoIt($name,"gerät ausschalten","testit",0);
$str .= "\nB.6:".Babble_DoIt($name,"das gerät ausmachen","testit",0);
$str .= "\nB.7:".Babble_DoIt($name,"gerät anmachen","testit",0);
$str .= "\nB.8:".Babble_DoIt($name,"schalte beleuchtung an","testit",0);
$str .= "\nB.9:".Babble_DoIt($name,"licht anschalten","testit",0);
$str .= "\n";
$str .= "\nC.1:".Babble_DoIt($name,"wie ist der wert von gerät","testit",0);
$str .= "\nC.2:".Babble_DoIt($name,"wie ist wert von gerät","testit",0);
$str .= "\nC.3:".Babble_DoIt($name,"wie ist der wert gerät","testit",0);
$str .= "\nC.4:".Babble_DoIt($name,"wie ist wert gerät","testit",0);
$str .= "\nC.4:".Babble_DoIt($name,"sage den status von gerät","testit",0);
$str .= "\nC.5:".Babble_DoIt($name,"sage status von gerät","testit",0);
$str .= "\nC.6:".Babble_DoIt($name,"sage status gerät","testit",0);
$str .= "\n";
$str .= "\nD.1:".Babble_DoIt($name,"wie ist das wetter von morgen","testit",0);
$str .= "\nD.2:".Babble_DoIt($name,"wie ist wetter von morgen","testit",0);
$str .= "\nD.3:".Babble_DoIt($name,"wie ist das wetter morgen","testit",0);
$str .= "\nD.4:".Babble_DoIt($name,"wie ist wetter morgen","testit",0);
$str .= "\nD.5:".Babble_DoIt($name,"wie ist morgen das wetter","testit",0);
$str .= "\nD.6:".Babble_DoIt($name,"wie ist morgen wetter","testit",0);
$str .= "\nD.7:".Babble_DoIt($name,"wetter von morgen","testit",0);
$str .= "\nD.8:".Babble_DoIt($name,"wetter morgen","testit",0);
$str .= "\n";
$str .= "\nF.1:".Babble_DoIt($name,"schalte den wecker aus","testit",0);
$str .= "\nF.2:".Babble_DoIt($name,"schalte wecker aus","testit",0);
$str .= "\nF.3:".Babble_DoIt($name,"den wecker ausschalten","testit",0);
$str .= "\nF.4:".Babble_DoIt($name,"wecker ausschalten","testit",0);
$str .= "\nF.5:".Babble_DoIt($name,"wie ist die weckzeit","testit",0);
$str .= "\nF.6:".Babble_DoIt($name,"wie ist der status des weckers","testit",0);
$str .= "\nF.7:".Babble_DoIt($name,"weckzeit ansagen","testit",0);
$str .= "\nF.8:".Babble_DoIt($name,"weckzeit","testit",0);
$str .= "\nF.9:".Babble_DoIt($name,"wecken um 4 uhr 3","testit",0);
$str .= "\nF.10:".Babble_DoIt($name,"stelle den wecker auf 17:00","testit",0);
$str .= "\nF.11:".Babble_DoIt($name,"wecken um 13:12 Uhr","testit",0);
$str .= "\n";
$str .= "\nG.1:".Babble_DoIt($name,"das haus ansagen","testit",0);
$str .= "\nG.2:".Babble_DoIt($name,"haus ansagen","testit",0);
$str .= "\nG.3:".Babble_DoIt($name,"haus status","testit",0);
$str .= "\nG.4:".Babble_DoIt($name,"wie ist der status des hauses","testit",0);
$str .= "\nG.5:".Babble_DoIt($name,"wie ist der status vom haus","testit",0);
$str .= "\nG.6:".Babble_DoIt($name,"das haus sichern","testit",0);
$str .= "\nG.7:".Babble_DoIt($name,"sichere das haus","testit",0);
$str .= "\nG.8:".Babble_DoIt($name,"haus sichern","testit",0);
$str .= "\nG.9:".Babble_DoIt($name,"das haus entsichern","testit",0);
$str .= "\nG.10:".Babble_DoIt($name,"haus entsichern","testit",0);
$str .= "\nG.11:".Babble_DoIt($name,"haustür öffnen","testit",0);
$str .= "\nG.12:".Babble_DoIt($name,"die haustür öffnen","testit",0);
$str .= "\nG.13:".Babble_DoIt($name,"öffne die haustür","testit",0);
$str .= "\nG.14:".Babble_DoIt($name,"schließe die haustür zu","testit",0);
$str .= "\nG.15:".Babble_DoIt($name,"schließe die haustür auf","testit",0);
$str .= "\n";
$str .= "\nH.1:".Babble_DoIt($name,"alarmanlage einschalten","testit",0);
$str .= "\nH.1:".Babble_DoIt($name,"alarmanlage ein schalten","testit",0);
$str .= "\nH.1:".Babble_DoIt($name,"die alarmanlage scharfschalten","testit",0);
$str .= "\nH.2:".Babble_DoIt($name,"alarmanlage unscharf schalten","testit",0);
$str .= "\nH.2:".Babble_DoIt($name,"die alarmanlage ausschalten","testit",0);
$str .= "\nH.3:".Babble_DoIt($name,"schalte die alarmanlage scharf","testit",0);
$str .= "\nH.4:".Babble_DoIt($name,"schalte den alarm an","testit",0);
$str .= "\nH.5:".Babble_DoIt($name,"alarm wider rufen","testit",0);
$str .= "\nH.6:".Babble_DoIt($name,"alarm widerrufen","testit",0);
$str .= "\n";
$str .= "\nI.1:".Babble_DoIt($name,"schalte beleuchtung in sitzgruppe an","testit",0);
$str .= "\nI.2:".Babble_DoIt($name,"schalte beleuchtung in der sitzgruppe an","testit",0);
$str .= "\nI.3:".Babble_DoIt($name,"mach die beleuchtung auf terrasse an","testit",0);
$str .= "\nI.4:".Babble_DoIt($name,"mache außen die beleuchtung aus","testit",0);
$str .= "\nI.5:".Babble_DoIt($name,"wie ist die temperatur im badezimmer","testit",0);
$str .= "\nI.6:".Babble_DoIt($name,"wie ist die feuchte in dominics zimmer","testit",0);
$str .= "\nI.7:".Babble_DoIt($name,"wie ist die feuchte in dem schlafzimmer","testit",0);
$str .= "\nI.8:".Babble_DoIt($name,"wie ist der status der tür im schlafzimmer","testit",0);
$str .= "\nI.9:".Babble_DoIt($name,"status tür schlafzimmer","testit",0);
$str .= "\nI.10:".Babble_DoIt($name,"status der tür schlafzimmer","testit",0);
$str .= "\nI.11:".Babble_DoIt($name,"status tür im schlafzimmer","testit",0);
$str .= "\nI.12:".Babble_DoIt($name,"status der tür im schlafzimmer","testit",0);
$str .= "\n";
$str .= "\nJ.1:".Babble_DoIt($name,"stelle bei gerät den wert auf 8","testit",0);
$str .= "\nJ.2:".Babble_DoIt($name,"stelle am gerät wert auf 9","testit",0);
$str .= "\nJ.3:".Babble_DoIt($name,"stelle bei harmony den kanal auf 10","testit",0);
$str .= "\nJ.4:".Babble_DoIt($name,"stelle am fernseher die lautstärke auf 11","testit",0);
$str .= "\n";
$str .= "\nK.1:".Babble_DoIt($name,"zur einkaufsliste hinzufügen bratheringe","testit",0);
$str .= "\nK.2:".Babble_DoIt($name,"zu peters liste hinzufügen ticket münchen besorgen","testit",0);
$str .= "\nK.3:".Babble_DoIt($name,"von dominics liste entfernen schmieröl","testit",0);
$str .= "\nK.4:".Babble_DoIt($name,"baumarktliste löschen","testit",0);
$str .= "\nK.5:".Babble_DoIt($name,"einkaufsliste senden","testit",0);
return $str;
}
##############################################################################
#
# Babble_Normalize
#
# Parameter hash = hash of the babble device
#
##############################################################################
sub Babble_Normalize($$){
my ($name,$sentence) = @_;
my $hash = $defs{$name};
$sentence = lc $sentence;
$sentence =~ s/[,.]//g;
my $cat = 0;
my $subcat = 0;
my $subsubcat = 0;
my ($device,$verb,$reading,$value,$article,$reserve,$place,$state,$prepo)=("","","","","","","","","","");
#-- normalize special phrases
my $sentmod = $sentence;
my $pairs = AttrVal($name,"babblePreSubs",undef);
if( $pairs ){
my @subs=split(' ',$pairs);
for( my $i=0; $i<int(@subs); $i++ ){
my ($t,$r) = split( ':',$subs[$i],2 );
$t =~ s/\\s/ /g;
$r =~ s/\\s/ /g;
$sentmod =~ s/$t/$r/;
}
}
my @word = split(' ',$sentmod,15);
my $len = int(@word);
#-- emergency: only one word given
return ($word[0],"none","none","none","none","none","none","X.X.X")
if( $len==1 );
############################# POS tagging ###################
#-- isolate place - take out (prepo) [arti] PLACE
# (verb) (prepo) [arti] PLACE [arti] (device)
# (verb) [arti] (device) (prepo) [arti] PLACE
# wie ist [arti] (device) (prepo) [arti] PLACE
# wie ist (prepo) [arti] PLACE [arti] (device)
$place = "none";
for( my $i=0;$i<$len;$i++){
if( $word[$i] =~ /^$hash->{DATA}{"re_places"}/ ){
$place = $word[$i];
my $to = 1;
$to++
if( ($i-1)>=0 && $word[$i-1] =~ /^$hash->{DATA}{"re_articles"}/ );
$to++
if( ($i-$to)>=0 && $word[$i-$to] =~ /^$hash->{DATA}{"re_prepos"}/ );
for( my $j=$i+1-$to;$j<$len;$j++){
$word[$j]=($word[$j+$to])?$word[$j+$to]:"";
}
last;
}
}
#-- backup without place for reserve
my @xord = @word;
#-- leer
if( int(@word) == 0){
return ("","","","","","","");
#-- Kategorie 1: Verb zuerst ----------------------------------------------------------
# schalte das gerät an
# schalte gerät an
# sage den status von gerät
# sage status von gerät
# sage status gerät
# schalte den wecker aus ;
# schalte wecker aus
}elsif( ($word[0] =~ /^$hash->{DATA}{"re_verbsc"}/) && ($word[1])){
$cat = 1;
#-- get infinitive
$verb = $hash->{DATA}{"verbs"}{$word[0]};
if( $word[1] =~ /^$hash->{DATA}{"re_articles"}/){
$subcat = 1;
$article = $word[1];
$device = $word[2];
$reading = $word[3];
$reserve = $word[4];
}elsif( $word[1] =~ /^$hash->{DATA}{"re_prepos"}/){
$subcat = 2;
$article = $word[1];
$device = $word[2];
}else{
$subcat = 3;
$device = $word[1];
$reading = $word[2];
$reserve = $word[3];
}
#-- device=state => verb="sage" => reading
if( $hash->{DATA}{"re_status"} && $device =~ /^$hash->{DATA}{"re_status"}/ ){
if( $reading =~ /^$hash->{DATA}{"re_prepos"}/ ){
$subsubcat = 1;
$reading = $device;
$device = $reserve;
}else{
$subsubcat = 2;
$reserve = $reading;
$reading = $device;
$device = $reserve;
}
#-- reading of device => target
}elsif( $subcat==2 ){
if( $word[3] =~ /^$hash->{DATA}{"re_articles"}/ ){
$subsubcat = 3;
$reading = $word[4];
$reserve = $word[5];
}else{
$subsubcat = 4;
$reading = $word[3];
$reserve = $word[4];
}
}
#-- Kategorie 2 ----------------------------------------------------------
# wie ist der wert von gerät
# wie ist wert von gerät
# wie ist der wert gerät
# wie ist wert gerät
# wie ist das wetter morgen
# wie ist wetter morgen
# wie ist morgen das wetter
# wie ist morgen wetter
# wie ist die weckzeit
# wie ist der status des weckers
# (quest) ist (time) [arti1] (reading) [prepo] [arti2] ($device)
}elsif( $word[0] =~ /^$hash->{DATA}{"re_quests"}/){
$cat = 2;
$verb = "sagen";
my $inext;
#-- check time
if( $word[2] =~ /^$hash->{DATA}{"re_times"}/){
$value = $word[2];
$inext = 3;
}else{
$inext = 2;
}
#-- take out article
if( $word[$inext] =~ /^$hash->{DATA}{"re_articles"}/){
$subcat=1;
$article = $word[$inext];
$reading = $word[$inext+1];
#-- check time => device is reading
if( $word[$inext+2] =~ /^$hash->{DATA}{"re_times"}/){
$subsubcat = 1;
$value = $word[$inext+2];
$device = $reading;
#-- check time => device is reading
}elsif( $word[$inext+2] =~ /^$hash->{DATA}{"re_prepos"}/ && $word[$inext+3] =~ /^$hash->{DATA}{"re_times"}/){
$subsubcat = 2;
$value = $word[$inext+3];
$device = $reading;
#--take out preposition
}elsif( $word[$inext+2] =~ /^$hash->{DATA}{"re_prepos"}/ ){
if( $word[$inext+3] =~ /^$hash->{DATA}{"re_articles"}/){
$subsubcat = 3;
$article = $word[$inext+3];
$device = $word[$inext+4];
}else{
$subsubcat = 4;
$device = $word[$inext+3];
}
#-- no preposition
}else{
if( $word[$inext+2] =~ /^$hash->{DATA}{"re_articles"}/){
$subsubcat = 5;
$article = $word[$inext+2];
$device = $word[$inext+3];
}else{
$subsubcat = 6;
$device = $word[$inext+2];
}
}
#-- no article
}else{
$subcat=2;
$reading = $word[$inext];
#-- check time => device is reading
if( $word[$inext+1] =~ /^$hash->{DATA}{"re_times"}/){
$subsubcat = 1;
$value = $word[$inext+1];
$device = $reading;
#-- check time => device is reading
}elsif( $word[$inext+1] =~ /^$hash->{DATA}{"re_prepos"}/ && $word[$inext+2] =~ /^$hash->{DATA}{"re_times"}/){
$subsubcat = 2;
$value = $word[$inext+2];
$device = $reading;
#--take out preposition
}elsif( $word[$inext+1] =~ /^$hash->{DATA}{"re_prepos"}/ ){
if( $word[$inext+2] =~ /^$hash->{DATA}{"re_articles"}/){
$subsubcat = 3;
$article = $word[$inext+2];
$device = $word[$inext+3];
}else{
$subsubcat = 4;
$device = $word[$inext+2];
}
#-- no preposition
}else{
if( $word[$inext+1] =~ /^$hash->{DATA}{"re_articles"}/){
$subsubcat = 5;
$article = $word[$inext+1];
$device = $word[$inext+2];
}else{
$subsubcat = 6;
$device = $word[$inext+1];
}
}
}
if( $device eq ""){
$subsubcat = 7;
$device = $reading;
$reading = "status";
}
#-- Kategorie 3 ----------------------------------------------------------
# das gerät anschalten
# gerät anschalten
# das wetter von morgen
# wetter von morgen
# das wetter morgen
# wetter morgen
# guten morgen
# gute nacht
# den wecker ausschalten
# wecker ausschalten
# wecker
# status
}else{
$cat = 3;
my $rex = $hash->{DATA}{"re_verbparts"}." ?".$hash->{DATA}{"re_verbsi"};
#-- guten morgen / gute nacht
if( $word[0] =~ /^gut.*/){
$subcat = 1;
$device="zeit";
$reading="status";
$value=$word[1];
$reserve=$word[2]
if( $word[2] );
$verb="schalten";
#-- (arti) (device) something
}elsif( $word[0] =~ /^$hash->{DATA}{"re_articles"}/){
$subcat = 2;
$article = $word[0];
$device = $word[1];
shift(@xord);
shift(@xord);
#--take out preposition
if( $word[2] =~ /^$hash->{DATA}{"re_prepos"}/ ){
$subsubcat = 1;
shift(@xord);
$reserve = join(" ",@xord);
}else{
$subsubcat = 2;
$reserve = join(" ",@xord);
}
#-- (arti) (device) [prepo] (time)
if( $reserve =~ /^$hash->{DATA}{"re_times"}/ ){
$subsubcat = 3;
#$reading = $reserve;
$value = $reserve;
$verb = "sagen";
#-- (arti) (device) [prepo] verb
}elsif( $reserve =~ s/^$hash->{DATA}{"re_verbsi"}\s?// ){
$subsubcat = 4;
$verb = $1;
$reading = $reserve;
#-- (arti) (device) [prepo] (reading) (verb) (value)
}else{
$subsubcat = 5;
$reserve =~ /^$rex/;
#-- named group
$verb = $+{verbsi};
$reading = $1;
}
#-- status [prepo] (device)
}elsif( $word[0] =~ /^status/){
$subcat = 3;
#--take out preposition
if( $word[1] =~ /^$hash->{DATA}{"re_prepos"}/ ){
$subsubcat = 1;
$device = $word[2];
}else{
$subsubcat = 2;
$device = $word[1];
}
$verb = "sagen";
$reading = "status";
#-- (device) something
}elsif($word[1] ne ""){
$subcat = 4;
$device = $word[0];
shift(@xord);
#--take out preposition
if( $word[1] =~ /^$hash->{DATA}{"re_prepos"}/ ){
$subsubcat = 1;
shift(@xord);
$reserve = join(" ",@xord);
}else{
$subsubcat = 2;
$reserve = join(" ",@xord);
}
#-- (device) [prepo] (time)
if( $reserve =~ /^$hash->{DATA}{"re_times"}/ ){
$subsubcat = 3;
$reading = "status";
$value = $reserve;
$verb = "sagen";
#-- (device) [prepo] status
}elsif( $reserve =~ /^status/ ){
$subsubcat = 4;
$reading = "status";
$verb = "sagen";
#-- (device) (write)
}elsif( $word[1] =~ /^$hash->{DATA}{"re_writes"}/ ){
$subsubcat = 5;
$verb = $word[1];
shift(@xord);
$reading = join(" ",@xord);
#-- (arti) (device) [prepo] verb
}elsif( $reserve =~ s/^$hash->{DATA}{"re_verbsi"}\s?// ){
$subsubcat = 6;
$verb = $1;
$reading = $reserve;
#-- (device) [prepo] (reading) (verb) (value)
}else{
$subsubcat = 7;
$reserve =~ /^$rex/;
#-- named group
$verb = $+{verbsi};
$reading = $1;
}
#-- (device)
}else{
$subcat = 5;
$device = $word[0];
$reading = "status";
$verb = "sagen";
}
}
#-- normalize devices
$device = "haus"
if( $device =~/hauses/);
$device = "wecker"
if( $device =~/we((ck)|g).*/);
$place = "wohnzimmer"
if( ($device eq "licht") && ($place eq ""));
if( $device eq "außenlicht" ){
$place="aussen"
if( $place eq "" );
$device="licht";
}
#-- machen
$verb = "schalten"
if( $verb && $verb eq "machen");
#-- sichern
$reading = "zu"
if(( $verb && $verb eq "sichern") && ($reading eq ""));
#-- an
$reading = "status"
if( (( $verb && $verb eq "sagen") || ( $verb && $verb eq "zeigen")) && ($reading eq "an"));
$reading = "an"
if( $reading && $reading eq "ein");
#-- value
$value=substr($sentmod,index($sentmod,"auf")+4)
if( ($reading && $reading eq "auf") || ($reserve && $reserve eq "auf") );
$value=substr($sentmod,index($sentmod,"hinzufügen")+11)
if( $reserve && $reserve =~ /hinzufügen (.*)/ );
if( $verb && $verb eq "entfernen" ){
$value = $reading;
$reading = "ent";
}
if( $value =~ /.*uhr.*/ ){
$value = Babble_timecorrector($value);
}
return ($device,$verb,$reading,$value,$article,$reserve,$place,"$cat.$subcat.$subsubcat");
}
#########################################################################################
#
# Babble_timecorrector - to correct for weird answers from Google
#
#########################################################################################
sub Babble_timecorrector($){
my ($value) = @_;
my ($h,$m1,$m2);
#-- xx:yy uhr und zz uhr
if( $value =~/(\d?\d):(\d\d) uhr und (\d\d)( uhr)?/ ){
$h = $1*1;
$m1 = $2*1;
$m2 = $3*1;
return(sprintf("%2d\:%02d uhr",$h,$m1+$m2));
#-- xx uhr zz uhr
}elsif( $value =~/(\d?\d) uhr (\d\d)( uhr)?/ ){
$h = $1*1;
$m1 = $2*1;
return(sprintf("%2d\:%02d uhr",$h,$m1));
#-- xx:yy - no correction
}elsif( $value =~/(\d?\d)(:(\d\d))?( uhr)?$/ ){
$h = $1*1;
$m1 = $3*1;
if( $m1 eq "" ){
$m1 = 0;
}
return(sprintf("%2d\:%02d uhr",$h,$m1));
}else{
return "xx";
}
}
#########################################################################################
#
# Babble_createRive
#
#########################################################################################
sub Babble_createRive($){
my ($hash) = @_;
my $name = $hash->{NAME};
my $rs = $hash->{Rive};
if( !defined($rs) ){
$rs = new RiveScript(utf8=>1);
$hash->{Rive} = $rs;
Log3 $name, 1, "[Babble] new RiveScript interpreter generated";
}
#--load a directory of replies
eval{$rs->loadDirectory ("./rivescript")};
#-- sort all the loaded replies
$rs->sortReplies;
}
#########################################################################################
#
# Babble_getcsrf
#
# Parameter ip = ip address of remote FHEM
#
#########################################################################################
sub Babble_getcsrf($$$){
my ($name,$ip,$i) = @_;
my $url = "http://".$ip."/fhem";
HttpUtils_NonblockingGet({
url => $url,
callback => sub($$$){
my ($rhash,$err,$data) = @_;
my $res = $rhash->{httpheader};
$res =~ /X-FHEM-csrfToken\:\s(csrf_\d+).*/;
CommandAttr(undef,$name." remoteToken$i ".$1);
}
});
}
########################################################################################
#
# Babble_DoIt
#
# Parameter name = name of the babble definition
#
#########################################################################################
sub Babble_DoIt{
my ($name,$sentence,@parms) = @_;
my $hash = $defs{$name};
chomp ($sentence);
my $testit = 0;
my $exflag = 0;
my $confirm= 0;
my $res = "";
my $str = "";
my $star = "";
my $reply = "";
readingsSingleUpdate( $hash, "text", $sentence, 0 );
#-- semantic analysis
my ($device,$verb,$reading,$value,$article,$reserve,$place,$cat) = Babble_Normalize($name,$sentence);
$verb = "none"
if( !$verb );
$reading = "none"
if( !$reading );
if( @parms && $parms[0] eq "testit"){
$testit = 1;
shift @parms;
$exflag = $parms[0];
shift @parms;
for( my $i=0;$i<4;$i++){
$parms[$i] = AttrVal($name,"testParm".$i,undef)
if( !defined($parms[$i]) && AttrVal($name,"testParm".$i,undef));
}
$str="[Babble_Normalize] ".$babble_tt->{"input"}.": $sentence\n".
" ".$babble_tt->{"result"}.": Category=$cat: ".
$babble_tt->{"device"}."=$device ".$babble_tt->{"place"}."=$place ".
$babble_tt->{"verb"}."=$verb ".$babble_tt->{"target"}."=$reading / $value";
}
#-- find command directly
my $cmd = $hash->{DATA}{"command"}{$device}{$place}{$verb}{$reading};
#Log3 $name,5,"[Babble_DoIt] Result after first access of Cmd-Hash =".((defined $cmd)?$cmd:"none");
#-- not directly - but maybe we have an alias device ?
if( !defined($cmd) || $cmd eq "" ){
my $alidev = $device;
$alidev =~s/_\d+$//g;
my $numalias = (defined($hash->{DATA}{"devsalias"}{$alidev})) ? int(@{$hash->{DATA}{"devsalias"}{$alidev}}) : 0;
for (my $i=0;$i<$numalias ;$i++){
my $ig = $hash->{DATA}{"devsalias"}{$alidev}[$i];
my $bdev = $hash->{DATA}{"devs"}[$ig];
my $lbdev = lc($bdev);
next
if( $lbdev eq $device );
$cmd = $hash->{DATA}{"command"}{$lbdev}{$place}{$verb}{$reading};
if( defined($cmd) && $cmd ne "" ){
$device = $lbdev;
last;
}
}
#Log3 $name,5,"[Babble_DoIt] Result after second access of Cmd-Hash =".((defined $cmd)?$cmd:"none");
}
#-- not directly - but maybe we have a device which is an extension of an alias device
if( (!defined($cmd) || $cmd eq "") && defined($device) ){
my $realdev = $device;
foreach my $stardev (keys %{$hash->{DATA}{"devsalias"}}){
if(index($stardev,'*')!=-1){
my $starrexp = $stardev;
$starrexp =~ s/\*/\(\.\*\)/;
if( $realdev =~ m/$starrexp/ ){
$star = $1;
$cmd = $hash->{DATA}{"command"}{$stardev}{$place}{$verb}{$reading};
$cmd =~ s/\$STAR/$star/g;
if( defined($cmd) && $cmd ne "" ){
$device = $stardev;
last;
}
}
}
}
#Log3 $name,5,"[Babble_DoIt] Result after third access of Cmd-Hash =".((defined $cmd)?$cmd:"none");
}
#-- command found after all
if( defined($cmd) && $cmd ne "" ){
#-- confirmation ?
if( index($cmd,"\$CONFIRM") != -1 ){
$confirm=1;
$cmd =~ s/;;\$CONFIRM$//;
}
#-- substitution
$cmd =~ s/\$DEV/$device/g;
$cmd =~ s/\$VALUE/$value/g;
for(my $i=0;$i<int(@parms);$i++){
$cmd =~ s/\$PARM$i/$parms[$i]/g;
}
if( $testit==0 || ($testit==1 && $exflag==1 )){
Log3 $name,5,"[Babble_DoIt] Executing from hash: $device.$place.$verb.$reading/$value ";
my $contact = $hash->{DATA}{"devcontacts"}{$device}[2];
my $fhemdev = $hash->{DATA}{"devcontacts"}{$device}[1];
if( $contact == 0 ){
$res = fhem($cmd);
readingsSingleUpdate( $hash, "cmd", $cmd, 0 );
}else{
my $ip = AttrVal($name,"remoteFHEM".$contact,undef);
my $token = AttrVal($name,"remoteToken".$contact,undef);
my $func = AttrVal($name,"remoteFunc".$contact,undef);
if( $func && $func ne "" ){
$res = eval($func."(\"".$cmd."\")");
readingsSingleUpdate( $hash, "cmd", $res, 0 );
}else{
readingsSingleUpdate( $hash, "cmd", "remoteFHEM".$contact." ".$cmd, 0 );
$cmd =~ s/\s/\%20/g;
my $url = "http://".$ip."/fhem?XHR=1&amp;fwcsrf=".$token."&amp;cmd.$fhemdev=$cmd";
HttpUtils_NonblockingGet({
url => $url,
callback => sub($$$){}
});
}
}
#-- confirm execution
my $func = AttrVal($name,"confirmFunc",undef);
if( $confirm ){
if ($func && $func ne "" ){
#-- substitution
$func =~ s/\$DEV/$device/g;
$func =~ s/\$VALUE/$value/g;
for(my $i=0;$i<int(@parms);$i++){
$func =~ s/\$PARM$i/$parms[$i]/g;
}
$res = fhem($func);
}else{
Log3 $name,1,"[Babble_DoIt] Warning: requesting confirmation, but no attribute confirmFunc defined";
}
}
}
#-- what to do in conclusion
if( $testit==1 ){
$str .= "==> $cmd";
return $str;
}
return undef;
#-- no command found, acquire alternate text
}else{
#-- ChatBot available
if( $rive==1 && AttrVal($name,"noChatBot",0) != 1){
#-- Create a new RiveScript interpreter
Babble_createRive($hash)
if( !defined($hash->{Rive}) );
my $rs = $hash->{Rive};
$reply = $rs->reply ('localuser',$sentence);
if ($reply eq "ERR: No Reply Matched"){
$reply = $babble_tt->{dnu};
my $dnufile = AttrVal($name,"dnuFile",undef);
if( $dnufile ){
open(my $fh, '>>', $dnufile);
print $fh $sentence." => Category=$cat: ".
$babble_tt->{"device"}."=$device ".$babble_tt->{"place"}."=$place ".
$babble_tt->{"verb"}."=$verb ".$babble_tt->{"target"}."=$reading / $value\n";
close $fh;
}
}
readingsSingleUpdate( $hash, "cmd", "none => ChatBot text = $reply", 0 );
#-- no chatbot, use help text directly
}else{
$reply = defined($hash->{DATA}{"help"}{$device}) ? $hash->{DATA}{"help"}{$device} : "";
readingsSingleUpdate( $hash, "cmd", "none => Help text = $reply", 0 );
}
#-- get help function - embed reply of chatbot here
my $func = AttrVal($name,"helpFunc",undef);
if( $func && $func ne "" ){
#-- substitution
$func =~ s/\$HELP/$reply/g;
$func =~ s/\$DEV/$device/g;
$func =~ s/\$VALUE/$value/g;
for(my $i=0;$i<int(@parms);$i++){
$func =~ s/\$PARM$i/$parms[$i]/g;
}
#-- parameter is missing
if( $func =~ /.*PARM\d.*/){
return $str." ".$reply;
#-- test
}elsif( $testit==1 ){
$res = eval($func)
if( $exflag==1 );
return $str." ".$func;
}else{
$res = eval($func);
return "";
}
#-- no command, testing, no execution
}elsif( $testit==1 ){
Log 1,"[Babble_DoIt] Command $device.$place.$verb.$reading/$value undefined, reply = $reply";
return $reply;
}else{
return ""
}
}
}
########################################################################################
#
# Babble_checkattrs
#
# Parameter name = name of the babble definition
#
########################################################################################
sub Babble_checkattrs($){
my ($hash) = @_;
my $name = $hash->{NAME};
CommandAttr (undef,$name." babbleVerbs schalt,schalte:schalten")
if( AttrVal($name,"babbleVerbs","") eq "" );
CommandAttr (undef,$name." babbleVerbParts zu auf ent wider ein an aus ab um")
if( AttrVal($name,"babbleVerbParts","") eq "" );
CommandAttr (undef,$name." babblePrepos von vom des der in im auf bei am")
if( AttrVal($name,"babblePrepos","") eq "" );
CommandAttr (undef,$name." babbleQuests wie wo wann")
if( AttrVal($name,"babbleQuests","") eq "" );
CommandAttr (undef,$name." babbleArticles der die das den des dem zur")
if( AttrVal($name,"babbleArticles","") eq "" );
CommandAttr (undef,$name." babbleStatus Status Wert Wetter Zeit")
if( AttrVal($name,"babbleStatus","") eq "" );
CommandAttr (undef,$name." babbleWrites setzen ändern löschen")
if( AttrVal($name,"babbleWrites","") eq "" );
CommandAttr (undef,$name." babbleTimes heute morgen übermorgen nacht")
if( AttrVal($name,"babbleTimes","") eq "" );
#}else{
# $hash->{DATA}{"verbsi"}[0]="switching";
# $hash->{DATA}{"verbsicc"}[0][0]="switch";
# CommandAttr (undef,$name." babbleVerbParts re un");
# CommandAttr (undef,$name." babbleQuests by of in on at");
# CommandAttr (undef,$name." babbleAdverb how where when");
# CommandAttr (undef,$name." babbleArticles the to");
# CommandAttr (undef,$name." babbleStatus status value weather time");
#}
}
#########################################################################################
#
# Babble_ModPlace
#
# Parameter name = name of the babble definition
#
#########################################################################################
sub Babble_ModPlace($$$){
my ($name,$place,$cmd) = @_;
my $hash = $defs{$name};
#-- remove a place (parameter is just a number)
if( $cmd == 0){
splice(@{$hash->{DATA}{"splaces"}},$place,1);
#-- add a place
}else{
push(@{$hash->{DATA}{"splaces"}},$place);
}
CommandAttr (undef,$name." babblePlaces ".join(" ",@{$hash->{DATA}{"splaces"}}));
Babble_getplaces($hash,"new",undef);
Babble_save($hash);
}
#########################################################################################
#
# Babble_ModVerb
#
# Parameter name = name of the babble definition
#
#########################################################################################
sub Babble_ModVerb($$$$){
my ($name,$verbi,$verbc,$cmd) = @_;
my $hash = $defs{$name};
my $verbi2 = $verbi;
my $verbc2 = $verbc;
# %{$hash->{DATA}{"verbs"}} = hash of all verb => infinitive_verb pairs
# @{$hash->{DATA}{"verbsi"}} = array of all infinite verbs
# @{$hash->{DATA}{"verbsicc"}} = array of all arrays of conjugated verbs
#-- remove a verb - verbi is only a number,verbc is empty
if( $cmd == 0){
$verbi2 = $hash->{DATA}{"verbsi"}[$verbi];
$verbc2 = join(',',$hash->{DATA}{"verbsicc"}[$verbi]);
splice(@{ $hash->{DATA}{"verbsi"}},$verbi,1);
splice(@{ $hash->{DATA}{"verbsicc"}},$verbi,1);
#-- add a verb
}elsif( $cmd==1) {
push(@{$hash->{DATA}{"verbsi"}},$verbi);
my @cc=split(',',$verbc);
push(@{$hash->{DATA}{"verbsicc"}},\@cc);
#-- modify a verb - verbi is only a number,verbc is a list of conjugations
}else{
$verbi2 = $hash->{DATA}{"verbsi"}[$verbi];
my @cc=split(',',$verbc);
$hash->{DATA}{"verbsicc"}[$verbi]=\@cc;
}
#-- recreate attribute
my $att = "";
for(my $i=0;$i<int(@{ $hash->{DATA}{"verbsi"}});$i++){
$att .= join(',',@{ $hash->{DATA}{"verbsicc"}[$i]}).":".$hash->{DATA}{"verbsi"}[$i]." ";
}
CommandAttr (undef,$name." babbleVerbs ".$att);
Babble_getverbs($hash,"new",undef);
Babble_save($hash);
}
########################################################################################
#
# Babble_ModHlp
#
# Parameter name = name of the babble definition
#
#########################################################################################
sub Babble_ModHlp($$$){
my ($name,$bdev,$txt) = @_;
my $hash = $defs{$name};
#-- lower case characters
$bdev = lc($bdev);
$hash->{DATA}{"help"}{$bdev}=$txt;
}
########################################################################################
#
# Babble_ModCmd
#
# Parameter name = name of the babble definition
#
#########################################################################################
sub Babble_ModCmd($$$$$$){
my ($name,$bdev,$place,$verb,$target,$cmd) = @_;
my $hash = $defs{$name};
#-- lower case characters
$bdev = lc($bdev);
if( defined($target) && $target ne "" ){
$target = lc($target);
delete($hash->{DATA}{"command"}{$bdev}{"none"}{"none"}{"none"})
}else{
$target="none"
};
if( defined($verb) && $verb ne "" ){
$verb = lc($verb);
delete($hash->{DATA}{"command"}{$bdev}{"none"}{"none"})
}else{
$verb="none"
};
if( defined($place) && $place ne "" ){
$place = lc($place);
delete($hash->{DATA}{"command"}{$bdev}{"none"})
}else{
$place="none"
};
#Log 1,"[Babble_ModCmd] Setting in hash: $bdev.$place.$verb.$target";
$hash->{DATA}{"command"}{$bdev}{$place}{$verb}{$target}=$cmd;
}
########################################################################################
#
# Babble_RemCmd
#
# Parameter name = name of the babble definition
#
#########################################################################################
sub Babble_RemCmd($$$$$$){
my ($name,$bdev,$place,$verb,$target,$fallback) = @_;
my $hash = $defs{$name};
#-- lower case characters
$bdev = lc($bdev);
$place = lc($place);
$verb = lc($verb);
$target = lc($target);
$place="none"
if( $place eq "");
$verb="none"
if( $verb eq "");
$target="none"
if( $target eq "");
#-- trying to delete from data obtained via web
if( defined($hash->{DATA}{"command"}{$bdev}{$place}{$verb}{$target}) ){
Log3 $name, 5,"[Babble_RemCmd] Deleting from hash: $bdev.$place.$verb.$target => ".$hash->{DATA}{"command"}{$bdev}{$place}{$verb}{$target};
delete($hash->{DATA}{"command"}{$bdev}{$place}{$verb}{$target});
return
#-- try to figure out data from index (fallback strategy)
}else{
my $cmdstr = $babblerows[$fallback-1];
($bdev,$place,$verb,$target)=split('\+\|\+',$cmdstr);
Log3 $name, 5,"[Babble_RemCmd] Deleting in fallback strategy from hash: $bdev.$place.$verb.$target => ".$hash->{DATA}{"command"}{$bdev}{$place}{$verb}{$target};
delete($hash->{DATA}{"command"}{$bdev}{$place}{$verb}{$target});
return
}
}
#########################################################################################
#
# Babble_getids - Helper function to assemble id list
#
# Parameter hash = hash of device addressed
#
#########################################################################################
sub Babble_getids($$) {
my ($hash,$type) = @_;
my $name = $hash->{NAME};
my $res = "";
# @{$hash->{DATA}{"ids"}} = array of all ids
my @ids;
#--generate a new list
if( $type eq "new" ){
push(@ids,$babble_tt->{"hallo"});
#-- get ids from attribute
push(@ids,split(' ',AttrVal($name, "babbleIds", "")));
$hash->{DATA}{"re_ids"} = lc("((".join(")|(",@ids)."))");
return;
#-- just do something with the current list
}else{
return undef;
}
}
#########################################################################################
#
# Babble_getdevs - Helper function to assemble devices list
#
# Parameter hash = hash of device addressed
#
#########################################################################################
sub Babble_getdevs($$) {
my ($hash,$type) = @_;
my $name = $hash->{NAME};
# @{$hash->{DATA}{"devs"}} = array of all Babble devices
# %{$hash->{DATA}{"devcontacts"}} = hash of all arrays of contact data (Babble Device, FHEM Device, remote type)
my @remotes = (); # intermediate array of all remote groups of Babble device/FHEM device/contact data
my @devs = (); # intermediate array of all Babble devices with _number appendix
my %devshash = (); # intermediate hash of all Babble devices with _number appendix (for checking existence of name)
my %devsalias= (); # hash of arrays of all Babble device aliases without _number appendix
my @devcs = (); # intermediate array of all contact data for a certain device
my ($bdev,$lbdev,$sbdev,$fhemdev,$contact);
#--generate a new list
if( $type eq "new" ){
my $ig = 0;
$hash->{DATA}{"devs"}=();
$hash->{DATA}{"devcontacts"}=();
#-- local Babble devices raw data
foreach my $fhemdev (sort keys %defs ) {
$bdev = AttrVal($fhemdev, "babbleDevice",undef);
if( defined($bdev) ) {
Log3 $name,5,"[Babble_getdevs] finds local FHEM device $fhemdev with babbleDevice=$bdev";
$lbdev = lc($bdev);
$sbdev = $lbdev;
if(exists($devshash{$lbdev})) {
Log3 $name,1,"[Babble_getdevs] Warning: local FHEM device $fhemdev has duplicate babbleDevice=$bdev, is ignored. You need to specifiy ".$bdev."_<number> instead.";
}else{
Log3 $name,5,"[Babble_getdevs] local FHEM device $fhemdev with babbleDevice=$bdev entered into hashes with ig=$ig";
$devs[$ig] = $bdev;
#-- take away trailing _<num>
$sbdev =~ s/_\d+$//;
#-- put into hash
$hash->{DATA}{"devs"}[$ig] = $bdev;
$hash->{DATA}{"devcontacts"}{$lbdev}[0] = $bdev;
$hash->{DATA}{"devcontacts"}{$lbdev}[1] = $fhemdev;
$hash->{DATA}{"devcontacts"}{$lbdev}[2] = 0;
$devshash{$lbdev} = 1;
if( !defined($devsalias{$sbdev}) ){
$devsalias{$sbdev}[0]=$ig;
}else{
push(@{$devsalias{$sbdev}},$ig);
}
$ig++;
#-- safeguard against empty device
if( !defined($hash->{DATA}{"command"}{$lbdev})){
Log3 $name,1,"[Babble_getdevs] No entry in command table under $lbdev for local FHEM device $fhemdev with attribute babbleDevice=$bdev";
Babble_ModCmd($name,$sbdev,undef,undef,undef,undef)
}
}
}
}
#-- get devices from attribute
push(@remotes,split(' ',AttrVal($name, "babbleDevices", "")));
for (my $i=0;$i<int(@remotes);$i++){
($bdev,$fhemdev,$contact) =split(':',$remotes[$i]);
$lbdev = lc($bdev);
$sbdev = $lbdev;
#-- take away trailing _<num>
$sbdev =~ s/_\d+$//;
if(exists($devshash{$lbdev})) {
Log3 $name,1,"[Babble_getdevs] Warning: remote FHEM device $fhemdev has duplicate babbleDevice=$bdev, is ignored. You need to specifiy ".$bdev."_<unique number> instead.";
}else{
Log3 $name,5,"[Babble_getdevs] remote FHEM device $fhemdev with babbleDevice=$bdev entered into hashes with ig=$ig";
$hash->{DATA}{"devs"}[$ig] = $bdev;
$hash->{DATA}{"devcontacts"}{$lbdev}[0] = $bdev;
$hash->{DATA}{"devcontacts"}{$lbdev}[1] = $fhemdev;
$hash->{DATA}{"devcontacts"}{$lbdev}[2] = $contact;
$devshash{$lbdev} = 1;
if( !defined($devsalias{$sbdev}) ){
$devsalias{$sbdev}[0]=$ig;
}else{
push(@{$devsalias{$sbdev}},$ig);
}
$ig++;
#-- safeguard against empty device
if( !defined($hash->{DATA}{"command"}{$lbdev})){
Log 1,"[Babble_getdevs] No entry in command table under $lbdev for remote FHEM device $fhemdev (remote $contact) with attribute babbleDevice=$bdev";
Babble_ModCmd($name,$sbdev,undef,undef,undef,undef)
}
}
}
#-- hash of devices without _<num>
%{$hash->{DATA}{"devsalias"}} = %devsalias;
#-- regex list for devices to check for validity
$hash->{DATA}{"re_devs"} = lc("((".join(")|(",@{$hash->{DATA}{"devs"}})."))")
if( defined($hash->{DATA}{"devs"}) );
#-- cleanup commands list for obsolete devices
if( defined( $hash->{DATA}{"command"} )){
foreach my $device (keys %{$hash->{DATA}{"command"}}){
if( !defined($hash->{DATA}{"devcontacts"}{$device}) ){
delete($hash->{DATA}{"command"}{$device});
}
}
}
}
}
#########################################################################################
#
# Babble_antistupidity - check for stupid naming of devices or rooms
# Parameter hash = hash of device addressed
#
#########################################################################################
sub Babble_antistupidity($) {
my ($hash) = @_;
my $name = $hash->{NAME};
my $regexp = $hash->{DATA}{"re_places"};
my $devs = $hash->{DATA}{"devs"};
return
if( !defined($regexp) || !defined($devs) );
my $imax = int(@{$hash->{DATA}{"devs"}});
for( my $i=0; $i<$imax; $i++){
my $dev = lc($hash->{DATA}{"devs"}[$i]);
Log 1,"[Babble] Baaaaah ! It is not a good idea to name a device $dev similar to a place in Babble"
if( $dev =~ /$regexp/ );
}
return undef;
}
#########################################################################################
#
# Babble_gethelp - Helper function
# Parameter hash = hash of device addressed
#
#########################################################################################
sub Babble_gethelp($$) {
my ($hash,$bdev) = @_;
my $name = $hash->{NAME};
my $lbdev = lc($bdev);
}
#########################################################################################
#
# Babble_getplaces - Helper function to assemble places list
#
# Parameter hash = hash of device addressed
#
#########################################################################################
sub Babble_getplaces($$$) {
my ($hash,$type,$sel) = @_;
my $name = $hash->{NAME};
# @{$hash->{DATA}{"rooms"}} = array of all rooms that are not hidden
# @{$hash->{DATA}{"splaces"}} = array of all special places for Babble
# @{$hash->{DATA}{"places"}} = array of all places for Babble = rooms + special
my %rooms; # intermediate hash of all rooms
my @special; # intermediate array of all special places for Babble
my @places; # intermediate array of rooms/all babble places
my $nop = AttrVal($name,"babbleNotPlaces","");
#--generate a new list
if( $type eq "new" ){
#-- code lifted from FHEMWEB
%rooms = (); # Make a room hash
my $hre = AttrVal($FW_wname, "hiddenroomRegexp", "");
foreach my $d (keys %defs ) {
#next if(IsIgnored($d));
foreach my $r (split(",", AttrVal($d, "room", "Unsorted"))) {
next if($hre && $r =~ m/$hre/);
next if($r eq "Unsorted" || $r eq "hidden" || $r eq $babblehiddenroom || $r eq $babblepublicroom );
next if (index($nop, $r) != -1);
$rooms{$r}{$d} = 1;
}
}
if(AttrVal($FW_wname, "sortRooms", "")) { # Slow!
my @sortBy = split( " ", AttrVal( $FW_wname, "sortRooms", "" ) );
my %sHash;
map { $sHash{$_} = FW_roomIdx(\@sortBy,$_) } keys %rooms;
@places = sort { $sHash{$a} cmp $sHash{$b} } keys %rooms;
} else {
@places = sort keys %rooms;
}
@{$hash->{DATA}{"rooms"}}=@places;
#-- append special places from attribute
@special = split(' ',AttrVal($name, "babblePlaces", ""));
@{$hash->{DATA}{"splaces"}} = @special;
push(@places, @special);
@{$hash->{DATA}{"places"}} = @places;
$hash->{DATA}{"re_places"} = lc("((".join(")|(",@places)."))");
#Babble_save($hash);
return;
#-- just do something with the current list
}elsif( $type eq "html" ){
@places=@{$hash->{DATA}{"places"}};
#-- output
if( !defined($sel) ){
return "<option></option><option>".join("</option><option>",@places)."</option>";
}else{
$sel = lc($sel);
#-- todo: geht das einfacher ?
$sel =~ s/\xe3\xbc/ü/g;
$sel =~ s/\xe3\xb6/ö/g;
$sel =~ s/\xe3\xa4/ä/g;
$sel =~ s/\xe3\x9f/ß/g;
my $ret = ($sel eq "none") ? '<option selected="selected">' : '<option>';
$ret .= '</option>';
for( my $i=0;$i<int(@places);$i++){
$ret .= ( $sel eq lc($places[$i]) ) ? '<option selected="selected">' : '<option>';
$ret .= $places[$i].'</option>';
}
return $ret;
}
}else{
return undef;
}
}
#########################################################################################
#
# Babble_getverbs - Helper function to assemble verbs list
#
# Parameter hash = hash of device addressed
#
#########################################################################################
sub Babble_getverbs($$$) {
my ($hash,$type,$sel) = @_;
my $name = $hash->{NAME};
my $res = "";
# %{$hash->{DATA}{"verbs"}} = hash of all verb => infinitive_verb pairs
# @{$hash->{DATA}{"verbsi"}} = array of all infinite verbs
# @{$hash->{DATA}{"verbsicc"}} = array of all arrays of conjugated verbs
my @groups; # intermediate array of all conjugated_verb/infinitive_verb groups
my @verbsic; # intermediate array of all conjugations for a certain verb
#--generate a new list
if( $type eq "new" ){
#-- get verbs from attribute
push(@groups,split(' ',AttrVal($name, "babbleVerbs", "")));
for (my $i=0;$i<int(@groups);$i++){
my ($vc,$vi) =split(':',$groups[$i]);
$hash->{DATA}{"verbs"}{$vi} = $vi;
$hash->{DATA}{"verbsi"}[$i] = $vi;
@verbsic=split(',',$vc);
for (my $j=0;$j< int(@verbsic);$j++){
my $vcc = $verbsic[$j];
$hash->{DATA}{"verbs"}{$vcc} = $vi;
$hash->{DATA}{"verbsicc"}[$i][$j] = $vcc
}
}
$hash->{DATA}{"re_verbsi"} = "(?P<verbsi>(".lc( join(")|(",@{$hash->{DATA}{"verbsi"}}))."))";
#$hash->{DATA}{"re_verbsc"} = lc("((".join(")|(",(keys %{$hash->{DATA}{"verbs"}}))."))");
my $verbsc="((";
while (my ($key, $value) = each %{$hash->{DATA}{"verbs"}}){
$verbsc.=lc($key).")|(";
}
$verbsc =~ s/\)\|\($/))/;
$hash->{DATA}{"re_verbsc"}=$verbsc;
return;
#-- just do something with the current list
}elsif( $type eq "html" ){
my @verbsi=@{$hash->{DATA}{"verbsi"}};
my $fnd = 0;
#-- output
if( !defined($sel) ){
return "<option></option><option>".join("</option><option>",@verbsi)."</option>";
}else{
$sel = lc($sel);
#-- todo: geht das einfacher ?
$sel =~ s/\xe3\xbc/ü/g;
$sel =~ s/\xe3\xb6/ö/g;
$sel =~ s/\xe3\xa4/ä/g;
$sel =~ s/\xe3\x9f/ß/g;
#my $sel1 = encode_utf8($sel);
#my $sel2 = decode_utf8($sel);
my $ret = ($sel eq "none") ? '<option selected="selected">' : '<option>';
$ret .= '</option>';
for( my $i=0;$i<int(@verbsi);$i++){
if( $sel eq lc($verbsi[$i]) ) {
$ret .= '<option selected="selected">';
$fnd = 1;
}else{
$ret .= '<option>';
}
$ret .= $verbsi[$i].'</option>';
}
#if( $fnd==0 ){
# $ret .= '<option selected="selected" value="unknown">'.$babble_tt->{"unknown"}.'</option>';
#}
return $ret;
}
}else{
return undef;
}
}
#########################################################################################
#
# Babble_getwords - Helper function to assemble list of other word classes
#
# Parameter hash = hash of device addressed
#
#########################################################################################
sub Babble_getwords($$$$) {
my ($hash,$class,$type,$sel) = @_;
my $name = $hash->{NAME};
my $res = "";
my @words;
if( $type eq "new" ){
if( $class eq "verbparts" || $class eq "all" ) {
@words=split(' ',AttrVal($name, "babbleVerbParts", ""));
@{$hash->{DATA}{"verbparts"}} = @words;
$hash->{DATA}{"re_verbparts"} = lc("((".join(")|(",@words)."))");
}
if( $class eq "prepos" || $class eq "all" ) {
@words=split(' ',AttrVal($name, "babblePrepos", ""));
@{$hash->{DATA}{"prepos"}} = @words;
$hash->{DATA}{"re_prepos"} = lc("((".join(")|(",@words)."))");
}
if( $class eq "articles" || $class eq "all" ) {
@words=split(' ',AttrVal($name, "babbleArticles", ""));
@{$hash->{DATA}{"articles"}} = @words;
$hash->{DATA}{"re_articles"} = lc("((".join(")|(",@words)."))");
}
if( $class eq "status" || $class eq "all" ) {
@words=split(' ',AttrVal($name, "babbleStatus", ""));
@{$hash->{DATA}{"status"}} = @words;
$hash->{DATA}{"re_status"} = lc("((".join(")|(",@words)."))");
}
if( $class eq "times" || $class eq "all" ) {
@words=split(' ',AttrVal($name, "babbleTimes", ""));
@{$hash->{DATA}{"times"}} = @words;
$hash->{DATA}{"re_times"} = lc("((".join(")|(",@words)."))");
}
if( $class eq "quests" || $class eq "all" ) {
@words=split(' ',AttrVal($name, "babbleQuests", ""));
@{$hash->{DATA}{"quests"}} = @words;
$hash->{DATA}{"re_quests"} = lc("((".join(")|(",@words)."))");
}
if( $class eq "writes" || $class eq "all" ) {
@words=split(' ',AttrVal($name, "babbleStatus", ""));
@{$hash->{DATA}{"writes"}} = @words;
$hash->{DATA}{"re_writes"} = lc("((".join(")|(",@words)."))");
}
delete($hash->{DATA}{"pronouns"});
#Babble_save($hash);
return;
#-- just do something with the current list
}elsif( $class eq "targets" && $type eq "html" ){
my @targets=@{$hash->{DATA}{"status"}};
push(@targets,"----");
push(@targets,@{$hash->{DATA}{"verbparts"}});
#-- output
if( !defined($sel) ){
return "<option></option><option>".join("</option><option>",@targets)."</option>";
}else{
$sel = lc($sel);
#-- todo: geht das einfacher ?
$sel =~ s/\xe3\xbc/ü/g;
$sel =~ s/\xe3\xb6/ö/g;
$sel =~ s/\xe3\xa4/ä/g;
$sel =~ s/\xe3\x9f/ß/g;
my $ret = ($sel eq "none") ? '<option selected="selected">' : '<option>';
$ret .= '</option>';
for( my $i=0;$i<int(@targets);$i++){
$ret .= (lc($sel) eq lc($targets[$i]) ) ? '<option selected="selected">' : '<option>';
$ret .= $targets[$i].'</option>';
}
return $ret;
}
}else{
return undef;
}
}
#########################################################################################
#
# Babble_Html - returns HTML code for the babble page
#
# Parameter name = name of the babble definition
#
#########################################################################################
sub Babble_Html($)
{
my ($name) = @_;
my $ret = "";
my $rot = "";
my $hash = $defs{$name};
my $id = $defs{$name}{NR};
if( !defined($babble_tt) ){
#-- readjust language
my $lang = AttrVal("global","language","EN");
if( $lang eq "DE"){
$babble_tt = \%babble_transtable_DE;
}else{
$babble_tt = \%babble_transtable_EN;
}
}
Babble_checkattrs($hash);
Babble_getids($hash,"new");
Babble_getdevs($hash,"new");
my $pllist = Babble_getplaces($hash,"new",undef);
Babble_antistupidity($hash);
my $pmlist="";
for(my $i=0;$i<int(@{$hash->{DATA}{"splaces"}});$i++){
$pmlist .= "<a onclick=\"babble_modplace('$name','".$hash->{DATA}{"splaces"}[$i]."',$i)\">".$hash->{DATA}{"splaces"}[$i]."</a> ";
}
my $vblist = Babble_getverbs($hash,"new",undef);
my $vmlist="";
for(my $i=0;$i<int(@{$hash->{DATA}{"verbsi"}});$i++){
my $vi = $hash->{DATA}{"verbsi"}[$i];
my $vmilist = join(',',@{$hash->{DATA}{"verbsicc"}[$i]});
$vmlist .= "<a onclick=\"babble_modverb('$name','".$vi."','".$vmilist."',$i)\">".$vi."</a> ";
}
my $vpmlist = Babble_getwords($hash,"all","new",undef);
#-- update state display
#readingsSingleUpdate( $hash, "state", Babble_getstate($hash)." ".$hash->{READINGS}{"short"}{VAL}, 1 );
#--
my $lockstate = ($hash->{READINGS}{lockstate}{VAL}) ? $hash->{READINGS}{lockstate}{VAL} : "unlocked";
my $showhelper = ($lockstate eq "unlocked") ? 1 : 0;
#--
$ret .= "<script type=\"text/javascript\" src=\"$FW_ME/pgm2/babble.js\"></script><script type=\"text/javascript\">\n";
$ret .= "var tt_add='".$babble_tt->{"add"}."';\n";
$ret .= "var tt_added='".$babble_tt->{"added"}."';\n";
$ret .= "var tt_remove='".$babble_tt->{"remove"}."';\n";
$ret .= "var tt_removed='".$babble_tt->{"removed"}."';\n";
$ret .= "var tt_modify='".$babble_tt->{"modify"}."';\n";
$ret .= "var tt_modified='".$babble_tt->{"modified"}."';\n";
$ret .= "var tt_cancel='".$babble_tt->{"cancel"}."';\n";
$ret .= "var tt_place='".$babble_tt->{"place"}."';\n";
$ret .= "var tt_verb='".$babble_tt->{"verb"}."';\n";
$ret .= "var newplace = '<select name=\"d_place\">".Babble_getplaces($hash,"html","none")."</select>';\n";
$ret .= "var newverbs = '<select name=\"d_verb\">".Babble_getverbs($hash, "html","none")."</select>';\n";
$ret .= "var newtargs = '<select name=\"d_verbpart\">".Babble_getwords($hash,"targets","html","none")."</select>';\n";
$ret .= "var newfield = '<input type=\"text\" name=\"d_command\" size=\"30\" maxlength=\"512\" value=\"FHEM command\">';\n";
$ret .= "var newcheck = '<input type=\"checkbox\" name=\"d_confirm\">';\n";
$rot .= "</script>\n";
$rot .= "<table class=\"roomoverview\">\n";
#-- test table
$rot .= "<tr><td colspan=\"3\"><div class=\"devType\">".$babble_tt->{"babbletest"}."</div></td></tr>";
$rot .= "<tr><td colspan=\"3\"><table class=\"block wide\" id=\"testtable\">\n";
$rot .= "<tr class=\"odd\" ><td class=\"col1\">".$babble_tt->{"input"}.": <input type=\"text\" id=\"d_testcommand\" size=\"60\" maxlength=\"512\"/></td>\n".
"<td class=\"col1\" style=\"text-align:left\"><input type=\"button\" id=\"b_testit\" onclick=\"babble_testit('".$name."')\" value=\"".$babble_tt->{"test"}."\" style=\"width:100px;\"/</td></tr>\n".
"<tr class=\"even\"><td class=\"col1\">".$babble_tt->{"result"}.": <div id=\"d_testresult\"></div></td>\n".
"<td class=\"col1\" style=\"text-align:left\"><input type=\"checkbox\" id=\"b_execit\">".$babble_tt->{"exec"}."</td></tr>\n";
$rot .= "</table></td></tr>";
#-- places table
my $tblrow=1;
$rot .= "<tr><td colspan=\"3\"><div class=\"devType\">".$babble_tt->{"babbleplaces"}."</div></td></tr>";
$rot .= "<tr><td colspan=\"3\"><table class=\"block wide\" id=\"placestable\">\n";
$rot .= "<tr class=\"odd\"><td class=\"col1\">".$babble_tt->{"rooms"}."</td><td class=\"col1\" colspan=\"2\" style=\"horizontal-align:left\">".join(" ",@{$hash->{DATA}{"rooms"}})."</td></tr>\n".
"<tr class=\"even\"><td class=\"col1\">".$babble_tt->{"places"}."</td><td class=\"col1\" colspan=\"2\" style=\"align:left\">".$pmlist."</td></tr>\n".
"<tr class=\"odd\"><td class=\"col1\"><input type=\"button\" id=\"b_addplace\" onclick=\"babble_addplace('".$name."')\" value=\"".$babble_tt->{"add"}."\" style=\"width:100px;\"/>".
"<div id=\"b_chgplacediv\" style=\"width:100px\"></div></td>".
"<td class=\"col3\" colspan=\"2\"><input type=\"text\" id=\"b_newplace\" size=\"40\" maxlength=\"120\" ></td></tr>\n";
$rot .= "</table></td></tr>";
#-- verbs table
$tblrow=1;
$rot .= "<tr><td colspan=\"3\"><div class=\"devType\">".$babble_tt->{"babbleverbs"}."</div></td></tr>";
$rot .= "<tr><td colspan=\"3\"><table class=\"block wide\" id=\"verbstable\">\n";
$rot .= "<tr class=\"odd\"><td class=\"col1\">".$babble_tt->{"verbs"}."</td><td class=\"col1\" colspan=\"2\" style=\"align:left\">".$vmlist."</td></tr>\n".
"<tr class=\"even\"><td class=\"col1\"></td>".
"<td class=\"col3\">".$babble_tt->{"conjugations"}."</td><td class=\"col3\">".$babble_tt->{"infinitive"}."</td></tr>\n".
"<tr class=\"odd\"><td class=\"col1\"><input type=\"button\" id=\"b_addverb\" onclick=\"babble_addverb('".$name."')\" value=\"".$babble_tt->{"add"}.
"\" style=\"width:100px;\"/><div id=\"b_chgverbdiv\" style=\"width:100px\"></div></td>".
"<td class=\"col3\"><input type=\"text\" id=\"b_newverbc\" size=\"20\" maxlength=\"120\" ></td><td class=\"col3\"><input type=\"text\" id=\"b_newverbi\" size=\"20\" maxlength=\"120\" ></td></tr>\n";
$rot .= "</table></td></tr>";
#-- devices table
$tblrow = 0;
my $ig = 0;
my $devcount = 0;
my @devrows = ();
my $indrow = 0;
@babblerows = ();
my($devrow,$ip,$ipp);
$rot .= "<tr><td colspan=\"3\"><div class=\"devType\">".$babble_tt->{"babbledev"}."</div></td></tr>";
$rot .= "<tr><td colspan=\"3\"><table class=\"block wide\" id=\"devstable\">\n";
$rot .= "<tr class=\"odd\"><td class=\"col1\" style=\"text-align:left;padding-right:10px;\">".$babble_tt->{"fhemname"}."</td><td class=\"col2\" style=\"text-align:left\">".$babble_tt->{"device"}."</td>\n".
"<td class=\"col3\">".$babble_tt->{"place"}."</td><td class=\"col3\">".$babble_tt->{"verb"}."</td><td class=\"col3\">".$babble_tt->{"target"}."</td>\n".
"<td class=\"col3\">".$babble_tt->{"action"}."</td><td class=\"col3\">".$babble_tt->{"confirm"}."</td><td class=\"col3\"><input type=\"button\" id=\"d_save\" onclick=\"babble_savedevs('".$name."')\" value=\"".$babble_tt->{"save"}.
"\" style=\"width:100px;\"/></td></tr>\n";
#-- loop over all unique devices to get some sorting
if( defined($hash->{DATA}{"devsalias"}) ){
for my $alidev (sort keys %{$hash->{DATA}{"devsalias"}}) {
#-- number of devices with this unique
my $numalias = int(@{$hash->{DATA}{"devsalias"}{$alidev}});
for (my $i=0;$i<$numalias ;$i++){
$ig = $hash->{DATA}{"devsalias"}{$alidev}[$i];
my $bdev = $hash->{DATA}{"devs"}[$ig];
my $lbdev = lc($bdev);
my $sbdev = $bdev;
$sbdev =~s/_\d+$//g;
my $lsbdev = $lbdev;
$lsbdev =~s/_\d+$//g;
my $hlp = $hash->{DATA}{"help"}{$lbdev};
if( !defined($hlp) ){
$hlp = $babble_tt->{"speak"}.": ".$sbdev.", ".$babble_tt->{"followedby"}." ";
#-- places ?
if( join('_',(keys %{$hash->{DATA}{"command"}{$lbdev}})) ne "none"){;
$hlp .= $babble_tt->{"placespec"}.", ".$babble_tt->{"followedby"}." ";
}
}
my $checked;
my $fhemdev = $hash->{DATA}{"devcontacts"}{$lbdev}[1];
my $contact = $hash->{DATA}{"devcontacts"}{$lbdev}[2];
$devcount++;
$tblrow++;
$ig++;
$devrow=1;
#-- headline for device
$rot .= sprintf("<tr class=\"%s\" style=\"padding-right:25px;\">", ($tblrow&1)?"odd":"even");
$rot .= "<td width=\"240\" class=\"col1\" style=\"text-align:left;padding-right:10px; border-top:1px solid gray\">";
#-- local link to device
if( $contact == 0 ){
$rot .= "<a href=\"$FW_ME?detail=$fhemdev\">$fhemdev</a>";
#-- remote link to device
}else{
$ip = AttrVal($name,"remoteFHEM".$contact,undef);
$ipp = $ip =~ s/:.*//sr;
if( $ip ){
$rot .= "<a href=\"http://".$ip."/fhem?detail=$fhemdev\">$fhemdev</a> ($ipp)";
}else{
$rot .= $fhemdev." (R$contact)";
}
}
$rot .= "</td>\n<td class=\"col2\" style=\"text-align:left; border-top:1px solid gray;padding:2px\">$bdev</td>\n";
$rot .= "</td>\n<td class=\"col2\" style=\"text-align:right; border-left:1px dotted gray; border-bottom: 1px dotted gray;border-top:1px solid gray;border-bottom-left-radius:10px; padding:2px\">".$babble_tt->{"helptext"}."&rarr;</td>";
#-- helptext
$rot .= "<td class=\"col3\" colspan=\"4\" style=\"text-align:left;border-right:1px dotted gray;border-bottom: 1px dotted gray;border-top:1px solid gray;border-bottom-right-radius:10px; padding:2px;\">";
$rot .= "<input type=\"text\" name=\"d_help\" size=\"51\" maxlength=\"1024\" value=\"".$hlp."\"/></td>";
$rot .= "<td style=\"text-align:left;padding-right:10px; border-top:1px solid gray\">".
"<input type=\"button\" id=\"d_addrow\" onclick=\"babble_addrow('".$name."',$devcount,$tblrow)\" value=\"".$babble_tt->{"add"}."\" style=\"width:100px;\"/></td></tr>\n";#$tblrow-$devcount.$devrow
foreach my $place (keys %{$hash->{DATA}{"command"}{$lbdev}}){
foreach my $verb (keys %{$hash->{DATA}{"command"}{$lbdev}{$place}}){
foreach my $target (keys %{$hash->{DATA}{"command"}{$lbdev}{$place}{$verb}}){
my $cmd = $hash->{DATA}{"command"}{$lbdev}{$place}{$verb}{$target};
if( !defined($cmd) ){
Log3 $name,1,"[Babble] Warning: Entry \$hash->{DATA}{\"command\"}{\"".$lbdev."\"}{\"".$place."\"}{\"".$verb."\"}{\"".$target."\"} is undefined";
$cmd = "undefined"
}
if( index($cmd,"\$CONFIRM") != -1 ){
$checked = "checked=\"checked\" ";
$cmd =~ s/;;\$CONFIRM$//;
}else{
$checked="";
}
push(@babblerows,$lbdev."+|+".$place."+|+".$verb."+|+".$target);
$indrow++;
$tblrow++;
$devrow++;
$rot .= sprintf("<tr class=\"%s\" style=\"padding-right:25px;\"><td></td><td></td>\n", ($tblrow&1)?"odd":"even");
$pllist = Babble_getplaces($hash,"html",$place);
$vblist = Babble_getverbs($hash, "html",$verb);
$vpmlist = Babble_getwords($hash,"targets","html",$target);
$rot .= "<td class=\"col3\"><select name=\"d_place\">".$pllist."</select></td>".
"<td class=\"col3\"><select name=\"d_verb\">".$vblist."</select></td>".
"<td class=\"col3\"><select name=\"d_verbpart\">".$vpmlist."</select></td>\n";
$rot .= "<td class=\"col3\" style=\"text-align:left;padding:2px\"><input type=\"text\" name=\"d_command\" size=\"30\" maxlength=\"512\" value=\"".$cmd."\"/></td>";
$rot .= "<td class=\"col3\"><input type=\"checkbox\" name=\"d_confirm\"$checked</td>";
$rot .= "<td><input type=\"button\" id=\"d_remrow\" onclick=\"babble_remrow('".$name."',$devcount,$tblrow,$indrow)\" value=\"".$babble_tt->{"remove"}."\" style=\"width:100px;\"/></td></tr>\n";#$tblrow-$devcount.$devrow
}
}
}
push(@devrows,$devrow)
}
}
$rot .= "</table></td></tr>";
}
$rot .= "</table>";
$ret .= "var devrows=[".( (@devrows) ? join(",",@devrows) : "")."];\n";
$ret .= "var devrowstart=devrows;\n";
return $ret.$rot;
}
1;
=pod
=item helper
=item summary for speech control of FHEM devices
=begin html
<a name="Babble"></a>
<h3>Babble</h3>
<ul>
<p> FHEM module for speech control of FHEM devices</p>
<a name="babbleusage"></a>
<h4>Usage</h4>
See <a href="http://www.fhemwiki.de/wiki/Modul_babble">German Wiki page</a>
<a name="babbledefine"></a>
<br/>
<h4>Define</h4>
<p>
<code>define &lt;name&gt; Babble</code>
<br />Defines the Babble device. </p>
<a name="babbleset"></a>
Notes: <ul>
<li>This module uses the global attribute <code>language</code> to determine its output data<br />
(default: EN=english). For German output set <code>attr global language DE</code>.</li>
<li>This module needs the JSON package.</li>
<li>Only when the chatbot functionality of RiveScript is required, the RiveScript module must be installed as well, see https://github.com/aichaos/rivescript-perl</li>
</ul>
<h4>Usage</h4>
To use this module,
<ol><li>call the Perl function <code>Babble_DoIt("&lt;name&gt;","&lt;sentence&gt;"[,&lt;parm0&gt;,&lt;parm1&gt;,...])</code>.</li>
<li>execute the FHEM command <code>set &lt;name&gt; talk|doit &lt;sentence&gt;[,&lt;parm0&gt;,&lt;parm1&gt;,...]</code>.</li>
</ol>
&lt;name&gt; is the name of the Babble device, &lt;parm0&gt; &lt;parm1&gt; are arbitrary parameters passed to the executed command.
The module will analyze the sentence passed an isolate a device to be addressed, a place identifier,
a verb, a target and its value from the sentence passed. <b>Attention: in case the FHEM command is used, &lt;sentence&gt must not contain commas.</b>
If a proper command has been stored with device, place, verb and target, it will be subject to substitutions and then will be executed.
In these substitutions, a string $VALUE will be replaced by the value for the target reading, a string $DEV will be replaced by the device name identified by Babble,
and strings $PARM[0|1|2...] will be replaced by the
corresponding parameters passed to the function <code>Babble_DoIt</code>
<ul>
<li>If no stored command ist found, the sentence is passed to the local RiveScript interpreter if present</li>
<li>To have a FHEM register itself as a Babble Device, it must get an attribute value <code>babbleDevice=&lt;name&gt;</code>. The <i>name</i> parameter must either be
unique to the Babble system, or it muts be of the form <code>&lt;name&gt;_&lt;digits&gt;</code></li>
<li>Devices on remote FHEM installations are defined in the <code>babbleDevices</code> attribute, see below</li>
</ul>
<h4>Set</h4>
<ul>
<li><a name="babble_talk">
<code>set &lt;name&gt; talk|doit &lt;sentence&gt;[,&lt;parm0&gt;,&lt;parm1&gt;,...]</code><br />
</a>
<br />execute the command given in the sentence.</li>
<li><a name="babble_lock">
<code>set &lt;name&gt; locked</code><br />
<code>set &lt;name&gt; unlocked</code>
</a>
<br />sets the lockstate of the babble module to <i>locked</i> (i.e., babble setups
may not be changed) resp. <i>unlocked</i> (i.e., babble setups may be changed>)</li>
<li><a name="babble_save">
<code>set &lt;name&gt; save|restore</code>
</a>
<br />Manually save/restore the babble to/from the external file babbleFILE (save done automatically at each state modification, restore at FHEM start)</li>
<li><a name="babble_rivereload">
<code>set &lt;name&gt; rivereload</code>
</a>
<br />Reload data for RiveScript Interpreter</li>
<li><a name="babble_test">
<code>set &lt;name&gt; test</code>
</a>
<br />Run a few test cases for normalization</li>
</ul>
</ul>
<a name="babbleget"></a>
<h4>Get</h4>
<ul>
<li><a name="babble_version"></a>
<code>get &lt;name&gt; version</code>
<br />Display the version of the module</li>
<li><a name="babble_tokens"></a>
<code>get &lt;name&gt; tokens</code>
<br />Obtain fresh csrfToken from remote FHEM installations (needed after restart of remote FHEM)</li>
</ul>
<a name="babbleattr"></a>
<h4>Attributes</h4>
<ul>
<li><a name="babbleDevices"><code>attr &lt;name&gt; babbleDevices [&lt;babble devname&gt;:&lt;FHEM devname&gt;:1|2|3]* </code></a>
<br />space separated list of <i>remote</i> FHEM devices, each as a group separated by ':' consisting of
<ul><li>a Babble device name</li>
<li>a FHEM Device name</li>
<li>an integer 1..3, indication which of the <i>remoteFHEM</i> functions to be called</li>
</ul>
The Babble device name may contain a <b>*</b>-character. If this is the case, it will be considered a regular expression, with the star replaced by <b>(.*)</b>.
When using Babble with a natural language sentence whose device part matches this regular expression, the character group addressed by the star sequence is placed in the variable
<code>$STAR</code>, and used to replace this value in the command sequence.
</li>
<li><a name="helpFunc"><code>attr &lt;name&gt; helpFunc &lt;function name&rt;</code></a>
<br/>name of a help function which is used in case no command is found for a certain device. When this function is called, the strings $DEV, $HELP, $PARM[0|1|2...]
will be replaced by the devicename identified by Babble, the help text for this device and parameters passed to the Babble_DoIt function</li>
<li><a name="testParm"><code>attr &lt;name&gt; testParm(0|1|2|3) &lt;string&rt;</code></a>
<br/>if a command is not really excuted, but only tested, the values of these attributes will be used to substitute the strings $PARM[0|1|2...]
in the tested command</li>
<li><a name="dnuFile"><code>attr &lt;name&gt; dnuFile &lt;filename&rt;</code></a>
<br/>if this filename is given, every sentence that could not be analyzed is stored in this file</li>
<li><a name="confirmFunc"><code>attr &lt;name&gt; confirmFunc &lt;function name&rt;</code></a>
<br/>name of a confirmation function which is used in case a command is exceuted. When this function is called, the strings $DEV, $HELP, $PARM[0|1|2...]
will be replaced by the devicename identified by Babble, the help text for this device and parameters passed to the Babble_DoIt function</li>
<li><a name="noChatBot"><code>attr &lt;name&gt; noChatBot 0|1</code></a>
<br/>if this attribute is set to 1, a local RiveScript interpreter will be ignored even though it is present in the system</li>
<li><a name="remoteFHEM"><code>attr &lt;name&gt; remoteFHEM(0|1|2|3) [&lt;user&gt;:&lt;password&gt;@]&lt;IP address:port&rt;</code></a>
<br/>IP address and port for a remote FHEM installation</li>
<li><a name="remoteFunc"><code>attr &lt;name&gt; remoteFunc(0|1|2|3) &lt;function name&rt;</code></a>
<br/>name of a Perl function that is called for addressing a certain remote FHEM device</li>
<li><a name="remoteToken"><code>attr &lt;name&gt; remoteToken(0|1|2|3) &lt;csrfToken&rt;</code></a>
<br/>csrfToken for addressing a certain remote FHEM device</li>
<li><a name="babbleIds"><code>attr &lt;name&gt; babbleIds <id_1> <id_2> ...</code></a>
<br />space separated list of identities by which babble may be addressed</li>
<li><a name="babblePreSubs"><code>attr &lt;name&gt; babbleSubs <regexp1>:<replacement1>,<regexp2>:<replacement2>, ...</code></a>
<br/>space separated list of regular expressions and their replacements - this will be used on the initial sentence submitted to Babble
(Note: a space in the regexp must be replaced by \s). </li>
<li><a name="babblePlaces"><code>attr &lt;name&gt; babblePlaces <place_1> <place_2> ...</code></a>
<br />space separated list of special places to be identified in speech</li>
<li><a name="babbleNotPlaces"><code>attr &lt;name&gt; babbleNoPlaces <place_1> <place_2> ...</code></a>
<br />space separated list of rooms (in the local FHEM device) that should <i>not</i> appear in the list of place</li>
<li><a name="babbleStatus"><code>attr &lt;name&gt; babbleStatus <status_1> <status_2> ...</code></a>
<br />space separated list of status identifiers to be identified in speech. Example: <code>Status Value Weather Time</code></li>
<li><a name="babblePrepos"><code>attr &lt;name&gt; babblePrepos <prepo_1> <prepo_2> ...</code></a>
<br />space separated list of prepositions to be identified in speech. Example: <code>by in at on</code></li>
<li><a name="babbleTimes"><code>attr &lt;name&gt; babbleTimes <time_1> <time_2> ...</code></a>
<br />space separated list of temporal adverbs. Example: <code>today tomorrow</code></li>
<li><a name="babbleQuests"><code>attr &lt;name&gt; babbleQuests <pron_1> <pron_2> ...</code></a>
<br />space separated list of questioning adverbs. Example: <code>how when where</code></li>
<li><a name="babbleArticles"><code>attr &lt;name&gt; babbleArticles <art_1> <art_2> ...</code></a>
<br />space separated list of articles to be identified in speech. Example: <code>the</code></li>
<li><a name="babbleVerbs"><code>attr &lt;name&gt; babbleVerbs <form1a>,<form1b>...:<infinitive1> <form2a>,<form2b>...:<infinitive2></code></a>
<br />space separated list of verb groups to be identified in speech. Each group consists of comma separated verb forms (conjugations as well as variations),
followed by a ':' and then the infinitive form of the verb. Example: <code>speak,speaks,say,says:speaking</code></li>
<li><a name="babbleWrites"><code>attr &lt;name&gt; babbleWrites <write_1> <write_2> ...</code></a>
<br />space separated list of write verbs to be identified in speech. Example: <code>send add remove</code></li>
<li><a name="babbleVerbParts"><code>attr &lt;name&gt; babbleVerbParts <vp_1> <vp_2> ...</code></a>
<br />space separated list of verb parts to be identified in speech. Example: <code>un re</code></li>
<li><a name="babble_linkname"><code>attr &lt;name&gt; linkname
&lt;string&gt;</code></a>
<br />Name for babble web link, default:
babbles</li>
<li><a name="babble_hiddenroom"><code>attr &lt;name&gt; hiddenroom
&lt;string&gt;</code></a>
<br />Room name for hidden babble room (containing only the Babble device), default:
babbleRoom</li>
<li><a name="babble_publicroom"><code>attr &lt;name&gt; publicroom
&lt;string&gt;</code></a>
<br />Room name for public babble room (containing sensor/actor devices), default:
babble</li>
<li><a name="babble_lockstate"><code>attr &lt;name&gt; lockstate
locked|unlocked</code></a>
<br /><i>locked</i> means that babble setups may not be changed, <i>unlocked</i>
means that babble setups may be changed></li>
</ul>
=end html
=begin html_DE
<a name="Babble"></a>
<h3>Babble</h3>
<ul>
<a href="https://wiki.fhem.de/wiki/Modul_Babble">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="commandref.html#Babble">Babble</a>
</ul>
=end html_DE
=cut