Xpl

De MicElectroLinGenMet.

Sommaire

Protopole xPL

Source Wikipedia: http://en.wikipedia.org/wiki/XPL_Protocol

XPL (eXtremely Simple Protocole) est un protocole ouvert destiné à permettre le contrôle et la surveillance des appareils de domotique. Les principaux buts du protocol XPL est de fournir un ensemble riche de fonctionnalités, tout en maintenant une structure de message simple. Le protocole permet une auto-découverte et une auto-configuration des capacités des dispositifs se qui le rend "plug-n-play".

Site du projet xPL, en anglais, http://wiki.xplproject.org.uk/


xPL Linux C library

Lien: http://www.xpl4java.org/xPL4Linux/

Voir le programme C Xpl-vmc.c comme exemple (à améliorer) d'utilisation de la xPLLib C ainsi que de la lib OWfs.


xPL Perl API

Lien: https://github.com/beanz/xpl-perl/

Installation sous Debian, rajouter ce dépot dans /etc/apt/sources.list:

deb http://debian.temporalanomaly.com/ squeeze main


Programmes fournis avec l'API

xpl-bridge          xpl-hub             xpl-packet-trace    xpl-sms-send
xpl-clock           xpl-hub.origine     xpl-perl-setup      xpl-tty-tcp
xpl-ctx35           xpl-lcdproc         xpl-prog.sh         xpl-udin
xpl-currentcost     xpl-linux-cpu       xpl-rfxcom          xpl-viom
xpl-dawndusk        xpl-lirc            xpl-rfxcom.origine  xpl-w800
xpl-dg834           xpl-logger          xpl-rfxcom-trans    xpl-x10
xpl-dmx             xpl-monitor         xpl-rrd             xpl-xosd
xpl-hddtemp         xpl-mpd             xpl-rrd-graphs      xpl-xvkbd
xpl-heyu-helper     xpl-owfs            xpl-sender

Dans la version 0.09-1, un leger bug empêche le lancement des programmes xpl.

$ xpl-hub -i eth0 -v
xPL::Hub->new: Unable to detect interface eth0
 at /usr/share/perl5/xPL/Hub.pm line 87. 

Le problème est du à la version française de Linux, la commande ifconfig utilisée par l'API xpl ne retrouve pas les paramètres voulus si la sortie est francisée.

La solution temporaire consiste à lancé un export LANG=C avant toute commande xpl-perl.

Le bug ne semble plus être présent dans la version SVN.

Exemples d'utilisation

  • xpl-sender
/usr/bin/xpl-sender -c osd.basic command=write text="$(date)" row=1

Permet d'envoyer des messages xPL.


  • xpl-lirc
/usr/bin/xpl-lirc -i eth0

Ce met à l'écoute du daemon Lirc et envoie un message xPL lors de la réception de la télécommande.


Messages xPL reçus avec xPL_Logger:

09/06/27 12:25:56 xpl-trig {bnz-lirc.vesta *} remote.basic { keys='ok' device='terratec_cinergy_1400' }
09/06/27 12:28:49 xpl-trig {bnz-lirc.vesta *} remote.basic { keys='left' device='terratec_cinergy_1400' }
09/06/27 12:28:50 xpl-trig {bnz-lirc.vesta *} remote.basic { keys='ok' device='terratec_cinergy_1400' }


  • xpl-rrd
/usr/bin/xpl-rrd -i eth0 /home/xpl/rrd

Ce script est un client XPL qui 'log' les messages connus (température, humidité, x10 ...) dans une base RRD. Il sera possible de générer des graphes avec le script xpl-rrd-graphs par intervalles réguliers.

$ /usr/bin/xpl-rrd-graphs  /home/xpl/rrd /home/xpl/rrd/graphs


  • xpl-owfs
/usr/bin/xpl-owfs -i eth0 -v /mnt/owfs

Ce script est un client XPL qui s'interface avec le système de fichiers owfs du bus 1-wire. Il supporte l'utilisation de messages control.basic avec les valeurs 'high', 'low' or 'pulse' pour les circuits PIO. Il envoie également des messages sensor.basic pour les sondes température, humidité, ou compteurs A ou B.


  • xpl-rfxcom
/usr/bin/xpl-rfxcom -v -i eth0 -B 4800 /dev/rfxcom

Le rfxcom connecté sur un port USB permet la réception de différent protocol RF sur la fréquence 433.92MHz tel que les sondes Oregon, X10-RF, HomeEasy ...


Messages xPL reçus avec xPL_Logger:

Réception messages sondes Oregon

09/06/27 15:40:18 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thgr228n.ab' type='temp' current='24' }
09/06/27 15:40:18 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thgr228n.ab' type='humidity' current='52' string='comfortable' }
09/06/27 15:40:18 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thgr228n.ab' type='battery' current='90' units='%' }
09/06/27 15:40:54 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thwr288a.de' type='temp' current='24.6' }
09/06/27 15:40:54 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thwr288a.de' type='battery' current='90' units='%' }


Réception messages X10 avec la sonnette IDK ML-8300

09/06/27 15:50:33 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='on' device='o8' }
09/06/27 15:50:33 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='off' device='o8' }


Réception messages X10 (par exemple avec le montage à micro-controleur AVR)

09/06/27 10:04:51 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='on' device='a3' }
09/06/27 10:20:03 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='on' device='a4' }
09/06/27 10:49:33 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='on' device='a4' }
09/06/27 11:19:03 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='on' device='a4' }


Messages de xpl-rfxcom en mode verbeux pour la télécommande ATI Remote qui n'est pas interprétée par le programme (pas de décodage de la trame ni message xPL)

Processed: 14dc17f0     <= ATI Remote (code touches 0 à 9)
Processed: 14d20df0
Processed: 14d30ef0
Processed: 14d40ff0
Processed: 14d510f0
Processed: 14d611f0
Processed: 14d712f0
Processed: 14d813f0
Processed: 14d914f0
Processed: 14da15f0


Module ATIRemote.pm

En prenant exemple sur le module X10.pm fourni avec la lib xpl-perl, j'en ai écrit un pour décoder les touches de la télécommande ATI Remote.

Il suffira de le copier dans le répertoire /usr/share/perl5/xPL/RF pour Debian et de relancer le programme xpl-rfxcom.

Le source est fourni "brut de fonderie" car je ne suis pas expert en Perl mais le module fonctionne pour moi.


Messages xPL reçus avec xPL_Logger:

09/06/27 11:16:50 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='5' device='ATI_Remote' }
09/06/27 11:16:53 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='6' device='ATI_Remote' }
09/06/27 11:16:55 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='8' device='ATI_Remote' }
09/06/27 11:16:56 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='9' device='ATI_Remote' }
09/06/27 11:16:57 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='0' device='ATI_Remote' }
09/06/27 11:16:58 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='check' device='ATI_Remote' }
09/06/27 11:16:59 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='menu' device='ATI_Remote' }
09/06/27 11:17:00 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='ok' device='ATI_Remote' }
09/06/27 11:17:09 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='clic_right_on' device='ATI_Remote' }
09/06/27 11:17:09 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='clic_right_off' device='ATI_Remote' }
09/06/27 11:17:11 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='clic_left_on' device='ATI_Remote' }
09/06/27 11:17:11 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='clic_left_off' device='ATI_Remote' }
09/06/27 11:17:13 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='hand' device='ATI_Remote' }
09/06/27 11:17:14 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='book' device='ATI_Remote' }
09/06/27 11:17:15 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='web' device='ATI_Remote' }
09/06/27 11:17:16 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='dvd' device='ATI_Remote' }
09/06/27 11:17:17 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='tv' device='ATI_Remote' }
09/06/27 11:17:20 xpl-trig {bnz-rfxcom.vesta *} remote.basic { keys='power' device='ATI_Remote' }


Source du module

package xPL::RF::ATIRemote;
 
# $Id$
# Dan, /usr/share/perl5/xPL/RF/ATIRemote.pm
 
=head1 NAME
 
xPL::RF::ATIRemote - Perl extension for decoding ATIRemote device RF messages
 
=head1 SYNOPSIS
 
  use xPL::RF::ATIRemote;
 
=head1 DESCRIPTION
 
This is a module for decoding RF messages from ATIRemote devices.
 
=head1 METHODS
 
=cut
 
use 5.006;
use strict;
use warnings;
use English qw/-no_match_vars/;
use xPL::Message;
use Exporter;
#use AutoLoader qw(AUTOLOAD);
 
our @ISA = qw(Exporter);
our %EXPORT_TAGS = ( 'all' => [ qw() ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw();
our $VERSION = '0.01';
our $SVNVERSION = qw/$Revision: 519 $/[1];
 
my $SPACE = q{ };
 
 
my %code_to_key =
(
	'dc17' => '0',
	'd20d' => '1',
	'd30e' => '2',
	'd40f' => '3',
	'd510' => '4',
	'd611' => '5',
	'd712' => '6',
	'd813' => '7',
	'd914' => '8',
	'da15' => '9',
        '3d78' => 'clic_left_on',
        '3e79' => 'clic_left_off',
        '417c' => 'clic_right_on',
        '427d' => 'clic_right_off',
        'c803' => 'tv',
        'c904' => 'dvd',
        'ca05' => 'web',
        'cb06' => 'book',
        'cc07' => 'hand',
        'c500' => 'A',
        'c601' => 'B',
        'c702' => 'power',
        '3772' => 'mouse_up',
        '3873' => 'mouse_down',
        '3671' => 'mouse_right',
        '3570' => 'mouse_left',
	'3974' => 'mouse_right_up',
	'3671' => 'mouse_left_up',
	'3b76' => 'mouse_right_down',
	'3c77' => 'mouse_left_down',
        'ce09' => 'vol-',
        'cd08' => 'vol+',
        'cf0a' => 'mute',
        'd10c' => 'ch-',
        'd00b' => 'ch+',
        'db16' => 'menu',
        'dd18' => 'check',
        'de19' => 'C',
        'e01b' => 'D',
        'df1a' => 'up',
        'e722' => 'down',
        'e41f' => 'right',
        'e21d' => 'left',
	'e31e' => 'ok',
        'e621' => 'E',
        'e823' => 'F',
        'e11c' => 'timer',
        'e520' => 'max',
        'ea25' => 'play',
        'ed28' => 'stop',
        'e924' => 'rew',
        'eb26' => 'fwd',
        'ee29' => 'pause',
        'ec27' => 'rec',
);
 
=head2 C<parse( $parent, $message, $bytes, $bits )>
 
This method attempts to recognize and parse RF messages corresponding
to ATI messages.  If messages are identified a reference to a list of
xPL::Message objects is returned.  If the message is not recognized,
undef is returned.
 
29/05/2009 23:25:28= 14D20DF0 ATI[15]C Remote type= ATI Remote Wonder Channel= 15  Command= 1 bits=20
29/05/2009 23:25:43= 14D30EF0 ATI[15]C Remote type= ATI Remote Wonder Channel= 15  Command= 2 bits=20
29/05/2009 23:25:45= 14D40FF0 ATI[15]C Remote type= ATI Remote Wonder Channel= 15  Command= 3 bits=20 
29/05/2009 23:25:48= 14D510F0 ATI[15]C Remote type= ATI Remote Wonder Channel= 15  Command= 4 bits=20 
29/05/2009 23:25:48= 14D611F0 ATI[15]C Remote type= ATI Remote Wonder Channel= 15  Command= 5 bits=20 
29/05/2009 23:25:49= 14D712F0 ATI[15]C Remote type= ATI Remote Wonder Channel= 15  Command= 6 bits=20 
29/05/2009 23:25:51= 14D813F0 ATI[15]C Remote type= ATI Remote Wonder Channel= 15  Command= 7 bits=20 
29/05/2009 23:25:52= 14D914F0 ATI[15]C Remote type= ATI Remote Wonder Channel= 15  Command= 8 bits=20 
29/05/2009 23:25:52= 14DA15F0 ATI[15]C Remote type= ATI Remote Wonder Channel= 15  Command= 9 bits=20 
29/05/2009 23:25:55= 14DC17F0 ATI[15]C Remote type= ATI Remote Wonder Channel= 15  Command= 0 bits=20 
 
XPL-TRIG Structure
remote.basic
{
keys=<button names or keycodes> - comma seperated values
[device=<name or devicecode of device>]
[zone=<ir zone>]
}
 
=cut
 
sub parse
{
  my $self = shift;
  my $parent = shift;
  my $message = shift;
  my $bytes = shift;
  my $bits = shift;
 
  $bits == 20 or return ;			# 20 bits pour ATI Remote !
#  printf("ATI bits %d, codes: %02x %02x %02x\n", $bits, $bytes->[0], $bytes->[1], $bytes->[2]) ; 
  my $key =  sprintf("%02x%02x", $bytes->[0], $bytes->[1]) ; 
#  print "ATI Key: ", $key, " => ", $code_to_key{$key}, "\n" ;
 
  my %body = (
              device => "ATI_Remote",
              keys => $code_to_key{$key},
             );
 
  return [xPL::Message->new(
                            message_type => 'xpl-trig',
                            class => 'remote.basic',
                            head => { source => $parent->source, },
                            body => \%body,
                           )];
}
 
1;
__END__
 
=head1 EXPORT
 
None by default.
 
=head1 SEE ALSO
 
Project website: http://www.xpl-perl.org.uk/
 
=head1 AUTHOR
 
Domos, E<lt>domos78 at free point frE<gt>
 
=head1 COPYRIGHT
 
Copyright (C) 2009 by Domos
 
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.7 or,
at your option, any later version of Perl 5 you may have available.
 
=cut


xpl et lcdproc

  • Autre programme personnel utilisant la librairie xpl-perl.

Affichage de températures remontées par xPL sur afficheur LCD comme celui-ci avec lcdproc.
Deux sondes Oregon remontent les températures salon/extérieure et une sonde DS18B20 1-wire est lu par le Wrt54gl.


Températures salon/extérieure et congélateur


  • source programme perl: xpl-lcdsensors.pl


#!/usr/bin/perl -w
 
=head1 NAME
 
xpl-lcdsensors - Perl script for display temp. sensors en lcd with lcdproc
 
=head1 SYNOPSIS
 
  xpl-lcdsensors [flags] [options] [filter...]
  where valid flags are:
    -h - show this help text
    -head - dump the head of all xPL messages
    -body - dump the body of all xPL messages
    -v - verbose client mode
  and valid options are (default shown in brackets):
    -i if0 - the interface for xPL messages (first non-loopback or loopback)
 
=head1 DESCRIPTION
 
Display temp. sensors on lcd with lcdproc.
 
=cut
 
#use strict;
use warnings;
use Fcntl;
use Getopt::Long;
use Pod::Usage;
use POSIX qw/strftime/;
use Time::Local;
use IO::Socket;
use xPL::Client;
$|=1; # autoflush helps debugging
 
my %args = ( vendor_id => 'domos', device_id => 'lcdtemp', );
my %opt = ();
my $verbose;
my $interface;
my $help;
my $man;
my $verbose_head;
my $verbose_body;
 
# Paramètres lcdproc
my $lcdhost = "localhost";
my $lcdport = "13666";
 
# Paramètres application
my $lastsalontemp = 0 ;
my $lastexttemp = 0 ;
my $lastcongtemp = 0 ;
 
my $minsalontemp = 99 ;
my $maxsalontemp = 99 ;
my $minexttemp = 99 ;
my $maxexttemp = 99 ;
my $mincongtemp = 99 ;
my $maxcongtemp = 99 ;
 
GetOptions('verbose+' => \$verbose,
           'interface=s' => \$interface,
           'define=s' => \%opt,
           'help|?|h' => \$help,
           'man' => \$man,
           'head' => \$verbose_head,
           'body' => \$verbose_body,
           ) or pod2usage(2);
pod2usage(1) if ($help);
pod2usage(-exitstatus => 0, -verbose => 2) if ($man);
 
$args{'interface'} = $interface if ($interface);
$args{'verbose'} = $verbose if ($verbose);
 
# Connect to the lcdproc server -----------------------------------------------
my $lcd = IO::Socket::INET->new(
                Proto     => "tcp",
                PeerAddr  => $lcdhost,
                PeerPort  => $lcdport,
        )
        || die "Cannot connect to LCDproc port\n";
 
# Make sure our messages get there right away
$lcd->autoflush(1);
 
sleep 1;        # Give server plenty of time to notice us...
 
print $lcd "hello\n";
my $lcdresponse = <$lcd>;
 
# Turn off blocking mode...
fcntl($lcd, F_SETFL, O_NONBLOCK);
 
# Set up some screen widgets...
print $lcd "client_set name {sensors}\n";
$lcdresponse = <$lcd>;
print $lcd "screen_add sensors\n";
$lcdresponse = <$lcd>;
print $lcd "widget_add sensors title title\n";
$lcdresponse = <$lcd>;
print $lcd "widget_set sensors title {Senseurs temp.}\n";
$lcdresponse = <$lcd>;
print $lcd "widget_add sensors l1 string\n";
$lcdresponse = <$lcd>;
print $lcd "widget_add sensors l2 string\n";
$lcdresponse = <$lcd>;
print $lcd "widget_add sensors l3 string\n";
$lcdresponse = <$lcd>;
print $lcd "backlight on\n";
$lcdresponse = <$lcd>;
#print $lcd "screen_set sensors priority foreground\n"; # S'affiche devant les autres clients lcdproc.
#$lcdresponse = <$lcd>;
 
# Create an xPL Client object  ------------------------------------------------
my $xpl = xPL::Client->new(%args, %opt) or die "Failed to create xPL::Client\n";
 
# Add a callback to receive filtered incoming xPL messages
$xpl->add_xpl_callback(id => 'sensorsmsg', self_skip => 0, targetted => 0, callback => \&sensorsmsg,
                        filter => {
                                  message_type => 'xpl-trig',
                                  class => 'sensor',
                                  class_type => 'basic',
                                  } );
 
# Add a timer 180s to the xPL Client event loop ...
$xpl->add_timer(id => 'testtimer',
                timeout => 50,
                callback => \&tick,
                arguments => [$xpl]);
 
$SIG{INT} = \&end;
$SIG{TERM} = \&end;
$SIG{QUIT} = \&end;
 
# Run the main loop
$xpl->main_loop();
 
# Traite message xPL sensor.basic.
sub sensorsmsg
{
        my %p = @_;
        my $msg = $p{message};
        # 10/03/03 12:22:43 xpl-trig sensor.basic device='thn132n.54' type='temp' current='21'
        # 10/03/03 10:30:16 xpl-trig sensor.basic device='thwr288a.de' type='temp' current='6.2'
        # 10/03/03 12:29:55 xpl-trig sensor.basic device='28.FC1D80010000' type='temp' current='-18.4'
 
        if ($msg->device eq "thn132n.54" && $msg->type eq "temp") 
        {
                $lastsalontemp = time() ;
                my $temp = sprintf("%.1f", $msg->current) ;
                if ( $temp < $minsalontemp || $minsalontemp==99) { $minsalontemp = $temp } ;
                if ( $temp > $maxsalontemp || $maxsalontemp==99) { $maxsalontemp = $temp } ;
                print $lcd "widget_set sensors l1 1 2 {S: ${temp}° $minsalontemp/$maxsalontemp }\n";
                $lcdresponse = <$lcd>;
        }
        if ($msg->device eq "thwr288a.de" && $msg->type eq "temp")
        {
                $lastexttemp = time() ;
                my $temp = sprintf("%.1f", $msg->current) ;
                if ( $temp < $minexttemp || $minexttemp==99) { $minexttemp = $temp } ;
                if ( $temp > $maxexttemp || $maxexttemp==99) { $maxexttemp = $temp } ;
                print $lcd "widget_set sensors l2 1 3 {X: ${temp}° $minexttemp/$maxexttemp }\n";
                $lcdresponse = <$lcd>;
        }
        if ($msg->device eq "28.FC1D80010000" && $msg->type eq "temp") 
        {
                $lastcongtemp = time() ;
                my $temp = sprintf("%.1f", $msg->current) ;
                if ( $temp < $mincongtemp || $mincongtemp==99) { $mincongtemp = $temp } ;
                if ( $temp > $maxcongtemp || $maxcongtemp==99) { $maxcongtemp = $temp } ;
                print $lcd "widget_set sensors l3 1 4 {C:${temp}° $mincongtemp/$maxcongtemp }\n";
                $lcdresponse = <$lcd>;
        }
 
        return 1;
}
 
# The callback to send the "clock.update" messages
sub tick {
  my %p = @_;
  my $timetick = time() ;
  my $heure = strftime "%H:%M", localtime ;
  # Si minuit, réinit. valeur min/max.
  if ($heure eq '00:00')
  { 
        $minsalontemp = 99 ;
        $maxsalontemp = 99 ;
        $minexttemp = 99 ;
        $maxexttemp = 99 ;
        $mincongtemp = 99 ;
        $maxcongtemp = 99 ;
  }
  if ( ($timetick - $lastsalontemp) > 300 )
  {
        # Si dernière info. temp. salon > 5mn affiche message.
        print $lcd "widget_set sensors l1 1 2 { S:  Plus à jour !   }\n";
        $lcdresponse = <$lcd>;  
  }
  if ( ($timetick - $lastexttemp) > 300 )
  {
        # Si dernière info. temp. salon > 5mn affiche message.
        print $lcd "widget_set sensors l2 1 3 { X:  Plus à jour !   }\n";
        $lcdresponse = <$lcd>;  
  }
  if ( ($timetick - $lastcongtemp) > 300 )
  {
        # Si dernière info. temp. salon > 5mn affiche message.
        print $lcd "widget_set sensors l3 1 4 { C:  Plus à jour !   }\n";
        $lcdresponse = <$lcd>;  
  }
  return 1;
};
 
# send a "hbeat.end" message on exit
sub end 
{ 
        defined $xpl && $xpl->send_hbeat_end() ;
        close($lcd);
        exit;
}
 
=head1 SEE ALSO
 
xPL::Client(3), xPL::Listener(3)
 
Project website: http://www.xpl-perl.org.uk/
 
=head1 AUTHOR
 
Domos, E<lt>domos78@free.frE<gt>
 
=head1 COPYRIGHT
 
Copyright (C) 2005, 2008 by Mark Hindess
 
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.7 or,
at your option, any later version of Perl 5 you may have available.
 
=cut




26/6/2009