mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-09 20:57:11 +00:00
88_xs1Bridge.pm: new Modul to read xs1 EZcontrol (Forum #81122.0) (Forum #81500.0)
git-svn-id: https://svn.fhem.de/fhem/trunk@15674 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
0732cf8600
commit
9fb9e4cb34
@ -1,5 +1,6 @@
|
||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
||||
# Do not insert empty lines here, update check depends on it.
|
||||
- new: 88_xs1Bridge: New modul to read xs1 EZcontrol
|
||||
- change: 93_DbRep: V7.0.0, changelog:
|
||||
- faster exec if no time attribut/aggregation is set
|
||||
- bugfix in fetchrows if data contain chars like "'
|
||||
|
401
fhem/FHEM/88_xs1Bridge.pm
Normal file
401
fhem/FHEM/88_xs1Bridge.pm
Normal file
@ -0,0 +1,401 @@
|
||||
#############################################################
|
||||
# $Id$
|
||||
#############################################################
|
||||
# ToDo´s: (physisches Modul - Verbindung zur Hardware)
|
||||
#
|
||||
# ...
|
||||
#############################################################
|
||||
|
||||
package main;
|
||||
|
||||
# Laden evtl. abhängiger Perl- bzw. FHEM-Module
|
||||
use HttpUtils; # um Daten via HTTP auszutauschen https://wiki.fhem.de/wiki/HttpUtils
|
||||
use strict;
|
||||
use warnings; # Warnings
|
||||
use POSIX;
|
||||
use Time::Local;
|
||||
|
||||
use JSON qw( decode_json ); # JSON
|
||||
use LWP::Simple; # JSON
|
||||
use utf8; # UTF-8
|
||||
use Encode qw(encode_utf8); # UTF-8
|
||||
use Net::Ping; # Ping Test Verbindung
|
||||
|
||||
sub xs1Bridge_Initialize($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{WriteFn} = "xs1Bridge_Write";
|
||||
$hash->{Clients} = ":xs1Device:";
|
||||
$hash->{MatchList} = { "1:xs1Device" => '.*' }; ## zum testen lt. Forum - https://regex101.com/
|
||||
|
||||
#$hash->{MatchList} = { "1:xs1Device" => '{\n..version.:.+}' }; ## zum testen lt. Forum - https://regex101.com/
|
||||
#$hash->{MatchList} = { "1:xs1Device" => '^{"id":".*' };
|
||||
|
||||
$hash->{DefFn} = "xs1Bridge_Define";
|
||||
$hash->{AttrFn} = "xs1Bridge_Attr";
|
||||
$hash->{AttrList} = "debug:0,1 ".
|
||||
"disable:0,1 ".
|
||||
"ignore:0,1 ".
|
||||
"interval:10,30,60,180,360 ";
|
||||
##$readingFnAttributes; ## die Standardattribute von FHEM
|
||||
|
||||
foreach my $d(sort keys %{$modules{xs1Bridge}{defptr}}) {
|
||||
my $hash = $modules{xs1Bridge}{defptr}{$d};
|
||||
}
|
||||
}
|
||||
|
||||
sub xs1Bridge_Define($$) {
|
||||
my ($hash, $def) = @_;
|
||||
my @arg = split("[ \t][ \t]*", $def);
|
||||
my $name = $hash->{NAME}; ## Der Definitionsname, mit dem das Gerät angelegt wurde.
|
||||
my $typ = $hash->{TYPE}; ## Der Modulname, mit welchem die Definition angelegt wurde.
|
||||
my $debug = AttrVal($hash->{NAME},"debug",0);
|
||||
|
||||
return "Usage: define <name> $name <ip>" if(@arg != 3);
|
||||
|
||||
# Parameter Define
|
||||
my $xs1_ip = $arg[2]; ## Zusatzparameter 1 bei Define - ggf. nur in Sub
|
||||
$hash->{xs1_ip} = $xs1_ip;
|
||||
|
||||
if (&xs1Bridge_Ping == 1) { ## IP - Check
|
||||
$hash->{STATE} = "Initialized"; ## Der Status des Modules nach Initialisierung.
|
||||
$hash->{TIME} = time(); ## Zeitstempel, derzeit vom anlegen des Moduls
|
||||
$hash->{VERSION} = "1.04"; ## Version
|
||||
$hash->{BRIDGE} = 1;
|
||||
|
||||
# Attribut gesetzt
|
||||
$attr{$name}{disable} = "0";
|
||||
$attr{$name}{interval} = "60";
|
||||
$attr{$name}{room} = "xs1" if( not defined( $attr{$name}{room} ) );
|
||||
|
||||
$modules{xs1Bridge}{defptr}{BRIDGE} = $hash;
|
||||
|
||||
InternalTimer(gettimeofday()+$attr{$name}{interval}, "xs1Bridge_GetUpDate", $hash); ## set Timer
|
||||
|
||||
Log3 $name, 3, "$typ: Module defined with xs1_ip: $xs1_ip";
|
||||
return undef;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "ERROR - Host IP $xs1_ip is not reachable. Please check!";
|
||||
}
|
||||
}
|
||||
|
||||
sub xs1Bridge_Attr(@) {
|
||||
my ($cmd,$name,$attrName,$attrValue) = @_;
|
||||
my $hash = $defs{$name};
|
||||
my $typ = $hash->{TYPE};
|
||||
my $interval = 0;
|
||||
my $debug = AttrVal($hash->{NAME},"debug",0);
|
||||
|
||||
# $cmd - Vorgangsart - kann die Werte "del" (löschen) oder "set" (setzen) annehmen
|
||||
# $name - Gerätename
|
||||
# $attrName/$attrValue sind Attribut-Name und Attribut-Wert
|
||||
|
||||
Debug " $typ: xs1_Attr | Attributes $attrName = $attrValue" if($debug);
|
||||
|
||||
if ($cmd eq "set") { ## Handling bei set .. attribute
|
||||
RemoveInternalTimer($hash); ## Timer löschen
|
||||
Debug " $typ: xs1_Attr | Cmd:$cmd | RemoveInternalTimer" if($debug);
|
||||
|
||||
if ($attrName eq "interval") { ## Abfrage Attribute
|
||||
if (($attrValue !~ /^\d*$/) || ($attrValue < 5)) ## Bildschirmausgabe - Hinweis Wert zu klein
|
||||
{
|
||||
return "$typ: Interval is too small. Please define new Interval | (at least: 5 seconds)";
|
||||
}
|
||||
my $interval = $attrValue;
|
||||
}
|
||||
elsif ($attrName eq "disable") {
|
||||
if ($attrValue eq "1") { ## Handling bei attribute disable 1
|
||||
readingsSingleUpdate($hash, "state", "deactive", 1);
|
||||
}
|
||||
elsif ($attrValue eq "0") { ## Handling bei attribute disable 0
|
||||
readingsSingleUpdate($hash, "state", "active", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($cmd eq "del") { ## Handling bei del ... attribute
|
||||
if ($attrName eq "disable" && !defined $attrValue) {
|
||||
readingsSingleUpdate($hash, "state", "active", 1);
|
||||
Debug " $typ: xs1_Attr | Cmd:$cmd | $attrName=$attrValue" if($debug);
|
||||
}
|
||||
if ($attrName eq "interval") {
|
||||
RemoveInternalTimer($hash);
|
||||
Debug " $typ: xs1_Attr | Cmd:$cmd | $attrName" if($debug);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($hash->{STATE} eq "active") {
|
||||
RemoveInternalTimer($hash);
|
||||
InternalTimer(gettimeofday()+$interval, "xs1Bridge_GetUpDate", $hash);
|
||||
Debug " $typ: xs1_Attr | RemoveInternalTimer + InternalTimer" if($debug);
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub xs1Bridge_Delete($$) {
|
||||
my ( $hash, $name ) = @_;
|
||||
RemoveInternalTimer($hash);
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub xs1Bridge_Ping() { ## Check before Define
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my $typ = $hash->{TYPE};
|
||||
my $xs1_ip = $hash->{xs1_ip};
|
||||
|
||||
my $timeout = "3";
|
||||
my $connection;
|
||||
my $p = Net::Ping->new;
|
||||
my $isAlive = $p->ping($xs1_ip , $timeout);
|
||||
$p->close;
|
||||
if ($isAlive) {
|
||||
$connection = 1;
|
||||
} else {
|
||||
$connection = 0;
|
||||
}
|
||||
return ($connection);
|
||||
}
|
||||
|
||||
sub xs1Bridge_GetUpDate() {
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my $typ = $hash->{TYPE};
|
||||
my $state = $hash->{STATE};
|
||||
my $def;
|
||||
|
||||
#http://xxx.xxx.xxx.xxx/control?callback=cname&cmd=...
|
||||
#get_list_actuators - list all actuators
|
||||
#get_list_sensors - list all sensors
|
||||
#get_list_timers - list all timers
|
||||
#get_config_info - list all device info´s
|
||||
#get_protocol_info - list protocol info´s
|
||||
|
||||
my $cmd = "/control?callback=cname&cmd=";
|
||||
my @cmdtyp = ("get_list_actuators","get_list_sensors","get_config_info","get_list_timers","get_list_actuators");
|
||||
my @arrayname = ("actuator","sensor","info","timer","function");
|
||||
my @readingsname = ("Aktor","Sensor","","Timer","");
|
||||
|
||||
my $debug = AttrVal($hash->{NAME},"debug",0);
|
||||
my $disable = AttrVal($name, "disable", 0);
|
||||
my $interval = AttrVal($name, "interval", 60);
|
||||
|
||||
if (AttrVal($hash->{NAME},"disable",0) == 0) {
|
||||
RemoveInternalTimer($hash);
|
||||
InternalTimer(gettimeofday()+$interval, "xs1Bridge_GetUpDate", $hash);
|
||||
Debug "\n ------------- ERROR CHECK - START -------------" if($debug);
|
||||
Debug " $typ: xs1Bridge_GetUpDate | RemoveInternalTimer + InternalTimer" if($debug);
|
||||
#Log3 $name, 3, "$typ: xs1Bridge_GetUpDate | RemoveInternalTimer + InternalTimer";
|
||||
|
||||
if ($state eq "Initialized") {
|
||||
readingsSingleUpdate($hash, "state", "active", 1);
|
||||
}
|
||||
|
||||
### JSON Abfrage - Schleife
|
||||
my $decoded;
|
||||
for my $i (0..4) {
|
||||
my $adress = "http://".$hash->{xs1_ip}.$cmd.$cmdtyp[$i];
|
||||
Debug " $typ: xs1Bridge_GetUpDate | Adresse: $adress" if($debug);
|
||||
|
||||
my $ua = LWP::UserAgent->new; ## CHECK JSON Adresse -> JSON-query, sonst FHEM shutdown
|
||||
my $resp = $ua->request(HTTP::Request->new(GET => $adress));
|
||||
if ($resp->code != "200") { ## http://search.cpan.org/~oalders/HTTP-Message-6.13/lib/HTTP/Status.pm
|
||||
#print "HTTP GET error code: ", $resp->code, "\n";
|
||||
#print "HTTP GET error message: ", $resp->message, "\n";
|
||||
Log3 $name, 3, "$typ: xs1Bridge_GetUpDate | HTTP GET error code ".$resp->code." -> no JSON-query";
|
||||
if ($i == 0 || $i == 1 || $i == 2 || $i == 3) {last}; ## ERROR JSON-query -> Abbruch schleife
|
||||
}
|
||||
|
||||
my ($json) = get($adress) =~ /[^(]*[}]/g; ## cut cname( + ) am Ende von Ausgabe -> ARRAY Struktur
|
||||
my $json_utf8 = encode_utf8( $json ); ## UTF-8 character Bearbeitung, da xs1 TempSensoren ERROR
|
||||
$decoded = decode_json($json_utf8);
|
||||
|
||||
Dispatch($hash,$json_utf8,undef); ## Übergabe an anderes Modul, NUR KOMPLETTES JSON !!!
|
||||
|
||||
if ($i <= 1 ) { ### Aktoren / Sensoren
|
||||
my @array = @{ $decoded->{$arrayname[$i]} };
|
||||
foreach my $f ( @array ) {
|
||||
if ($f->{"type"} ne "disabled") {
|
||||
readingsSingleUpdate($hash, $readingsname[$i]."_".sprintf("%02d", $f->{"id"}) , $f->{"value"}, 1);
|
||||
Debug " $typ: ".$readingsname[$i]."_".sprintf("%02d", $f->{"id"})." | ".$f->{"type"}." | ".$f->{"name"}." | ". $f->{"value"} if($debug);
|
||||
#Log3 $name, 3, $f->{"id"}." | ".$f->{"type"}." | ".$f->{"name"}." | ". $f->{"value"};
|
||||
}
|
||||
}
|
||||
} elsif ($i == 2) { ### Info´s
|
||||
my $features;
|
||||
my $features_i=0;
|
||||
while (defined $decoded->{'info'}{'features'}->[$features_i]) {
|
||||
$features.= $decoded->{'info'}{'features'}->[$features_i]." ";
|
||||
$features_i++;
|
||||
}
|
||||
readingsBeginUpdate($hash);
|
||||
readingsBulkUpdate($hash, "xs1_devicename" , $decoded->{'info'}{'devicename'});
|
||||
readingsBulkUpdate($hash, "xs1_bootloader" , $decoded->{'info'}{'bootloader'});
|
||||
readingsBulkUpdate($hash, "xs1_hardware" , $decoded->{'info'}{'hardware'});
|
||||
readingsBulkUpdate($hash, "xs1_features" , $features);
|
||||
readingsBulkUpdate($hash, "xs1_firmware" , $decoded->{'info'}{'firmware'});
|
||||
readingsBulkUpdate($hash, "xs1_mac" , $decoded->{'info'}{'mac'});
|
||||
readingsEndUpdate($hash, 1);
|
||||
|
||||
Debug " $typ: xs1_devicename: ".$decoded->{'info'}{'devicename'} if($debug);
|
||||
Debug " $typ: xs1_bootloader: ".$decoded->{'info'}{'bootloader'} if($debug);
|
||||
Debug " $typ: xs1_hardware: ".$decoded->{'info'}{'hardware'} if($debug);
|
||||
Debug " $typ: xs1_features: ".$features if($debug);
|
||||
Debug " $typ: xs1_firmware: ".$decoded->{'info'}{'firmware'} if($debug);
|
||||
Debug " $typ: xs1_mac: ".$decoded->{'info'}{'mac'} if($debug);
|
||||
|
||||
} elsif ($i == 3) { ### Timers noch BUG !!!
|
||||
my @array = @{ $decoded->{$arrayname[$i]} };
|
||||
foreach my $f ( @array ) {
|
||||
if ($f->{"type"} ne "disabled") {
|
||||
readingsSingleUpdate($hash, $readingsname[$i]."_".sprintf("%02d", $f->{"id"}) , FmtDateTime($f->{"next"}), 1);
|
||||
Debug " $typ: ".$readingsname[$i]."_".sprintf("%02d", $f->{"id"})." | ".$f->{"name"}." | ".$f->{"type"}." | ". $f->{"next"} if($debug);
|
||||
}
|
||||
}
|
||||
} elsif ($i == 4) { ### Aktoren / Funktion != disable
|
||||
my @array2 = @{ $decoded->{$arrayname[0]} };
|
||||
foreach my $f2 ( @array2 ) {
|
||||
|
||||
if ($f2->{"type"} ne "disabled") { ## Funktion != actuator -> type disable
|
||||
my @array = @{ $decoded->{'actuator'}->[($f2->{"id"})-1]->{$arrayname[$i]} };
|
||||
my $i2 = 0; ## Funktionscounter
|
||||
|
||||
foreach my $f3 ( @array ) {
|
||||
$i2 = $i2+1;
|
||||
if ($f3->{"type"} ne "disabled") { ## Funktion != function -> type disable
|
||||
Debug " $typ: ".$readingsname[0]."_".sprintf("%02d", $f2->{"id"})." | ".$f2->{"type"}." | ".$arrayname[$i]."_".$i2." | ".$f3->{"type"} if($debug);
|
||||
#readingsSingleUpdate($hash, $readingsname[0]."_".sprintf("%02d", $f2->{"id"})."_".$arrayname[$i]."_".$i2 , $f3->{"type"} , 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($i < 4) {
|
||||
Debug "\n ------------- ERROR CHECK - SUB -------------" if($debug);
|
||||
}
|
||||
### Schleifen Ende ###
|
||||
}
|
||||
|
||||
Debug "\n ------------- ERROR CHECK - ALL END -------------\n\n " if($debug);
|
||||
}
|
||||
}
|
||||
|
||||
sub xs1Bridge_Write($) ## Zustellen von Daten via IOWrite() vom logischen zum physischen Modul um diese an die Hardware weiterzureichen.
|
||||
{
|
||||
my ($hash, $Aktor_ID, $cmd) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my $typ = $hash->{TYPE};
|
||||
|
||||
Log3 $name, 3, "$typ: xs1Bridge_Write | Aktor_ID=$Aktor_ID, cmd=$cmd";
|
||||
}
|
||||
|
||||
# Eval-Rückgabewert für erfolgreiches
|
||||
# Laden des Moduls
|
||||
1;
|
||||
|
||||
|
||||
# Beginn der Commandref
|
||||
|
||||
=pod
|
||||
=item summary Connection of the device xs1 from EZControl
|
||||
=item summary_DE Anbindung des Gerates xs1 der Firma EZControl
|
||||
=begin html
|
||||
|
||||
<a name="xs1Bridge"></a>
|
||||
<h3>xs1Bridge</h3>
|
||||
<ul>
|
||||
With this module you can read out the device xs1 from EZcontrol. There will be actors | Sensors | Timer | Information read from xs1 and written in readings. With each read only readings are created or updated, which are also defined and active in xs1. Actor | Sensor or timer definitions which are deactivated in xs1 are NOT read.
|
||||
<br><br>
|
||||
|
||||
The module was developed based on the firmware version v4-Beta of the xs1. There may be errors due to different adjustments within the manufacturer's firmware.<br><br>
|
||||
|
||||
<a name="xs1Bridge_define"></a>
|
||||
<b>Define</b><br>
|
||||
<ul>
|
||||
<code>define <name> xs1Bridge <IP> </code>
|
||||
<br><br>
|
||||
|
||||
The module can not create without the IP of the xs1. If the IP can not be reached during module definition, the Define process is aborted.
|
||||
<ul>
|
||||
<li><code><IP></code> is IP address in the local network.</li>
|
||||
</ul><br>
|
||||
example:
|
||||
<ul>
|
||||
define EZcontrol_xs1 xs1Bridge 192.168.1.45
|
||||
</ul>
|
||||
</ul><br>
|
||||
<b>Set</b>
|
||||
<ul>N/A</ul><br>
|
||||
<b>Get</b><br>
|
||||
<ul>N/A</ul><br>
|
||||
<a name="xs1_attr"></a>
|
||||
<b>Attributes</b>
|
||||
<ul>
|
||||
<li>debug (0,1)<br>
|
||||
This brings the module into a very detailed debug output in the logfile. Program parts can be checked and errors checked.<br>
|
||||
(Default, debug 0)
|
||||
</li><br>
|
||||
<li>disable (0,1)<br>
|
||||
This function deactivates the interval. With disable 1 no readings are updated.<br>
|
||||
(Default, disable 0)
|
||||
</li><br>
|
||||
<li>interval (10,30,60,180,360)<br>
|
||||
This is the interval in seconds at which readings are read from xs1<br>
|
||||
(Default, interval 60)
|
||||
</li><br>
|
||||
</ul>
|
||||
</ul>
|
||||
=end html
|
||||
=begin html_DE
|
||||
|
||||
<a name="xs1Bridge"></a>
|
||||
<h3>xs1Bridge</h3>
|
||||
<ul>
|
||||
Mit diesem Modul können Sie das Gerät xs1 der Firma <a href="http://www.ezcontrol.de/">EZcontrol</a> auslesen. Das Modul ruft die Daten des xs1 via der Kommunikationsschnittstelle ab. Mit einem HTTP GET Requests erhält man die Antworten in Textform welche im Datenformat JSON (JavaScript Object Notation) ausgegeben werden.
|
||||
Es werden Aktoren | Sensoren | Timer | Informationen vom xs1 ausgelesen und in Readings geschrieben. Bei jedem Auslesen werden nur Readings angelegt bzw. aktualisiert, welche auch im xs1 definiert und aktiv sind. Aktor | Sensor bzw. Timer Definitionen welche deaktiviert sind im xs1, werden NICHT ausgelesen.
|
||||
<br><br>
|
||||
|
||||
Das Modul wurde entwickelt basierend auf dem Firmwarestand v4-Beta des xs1. Es kann aufgrund von unterschiedlichen Anpassungen innerhalb der Firmware des Herstellers zu Fehlern kommen.<br><br>
|
||||
|
||||
<a name="xs1Bridge_define"></a>
|
||||
<b>Define</b><br>
|
||||
<ul>
|
||||
<code>define <name> xs1Bridge <IP> </code>
|
||||
<br><br>
|
||||
|
||||
Ein anlegen des Modules ohne Angabe der IP vom xs1 ist nicht möglich. Sollte die IP bei der Moduldefinierung nicht erreichbar sein, so bricht der Define Vorgang ab.
|
||||
<ul>
|
||||
<li><code><IP></code> ist IP-Adresse im lokalen Netzwerk.</li>
|
||||
</ul><br>
|
||||
Beispiel:
|
||||
<ul>
|
||||
define EZcontrol_xs1 xs1Bridge 192.168.1.45
|
||||
</ul>
|
||||
</ul><br>
|
||||
<b>Set</b>
|
||||
<ul>N/A</ul><br>
|
||||
<b>Get</b><br>
|
||||
<ul>N/A</ul><br>
|
||||
<a name="xs1_attr"></a>
|
||||
<b>Attribute</b>
|
||||
<ul>
|
||||
<li>debug (0,1)<br>
|
||||
Dies bringt das Modul in eine sehr ausführliche Debug-Ausgabe im Logfile. Somit lassen sich Programmteile kontrollieren und Fehler überprüfen.<br>
|
||||
(Default, debug 0)
|
||||
</li><br>
|
||||
<li>disable (0,1)<br>
|
||||
Diese Funktion deaktiviert den Interval. Mit <code>disable 1</code> werden keine Readings aktualisiert.<br>
|
||||
(Default, disable 0)
|
||||
</li><br>
|
||||
<li>interval (10,30,60,180,360)<br>
|
||||
Das ist der Intervall in Sekunden, in dem die Readings neu gelesen werden vom xs1.<br>
|
||||
(Default, interval 60)
|
||||
</li><br>
|
||||
</ul>
|
||||
</ul>
|
||||
=end html_DE
|
||||
=cut
|
@ -366,6 +366,7 @@ FHEM/88_Itach_Relay.pm sachag Automatisierung
|
||||
FHEM/88_Itach_IRDevice ulimaass Sonstige Systeme
|
||||
FHEM/88_VantagePro2.pm sachag Sonstiges
|
||||
FHEM/88_WEBCOUNT.pm sachag Sonstiges
|
||||
FHEM/88_xs1Bridge.pm HomeAuto_User Sonstige Systeme
|
||||
FHEM/89_FULLY.pm zap Frontends
|
||||
FHEM/89_HEATRONIC.pm heikoranft Sonstige Systeme
|
||||
FHEM/89_VCONTROL.pm adamwit Heizungssteuerung/Raumklima
|
||||
|
Loading…
x
Reference in New Issue
Block a user