From 7962529c09fa609be48e68bbd41209b0534261e6 Mon Sep 17 00:00:00 2001
From: ntruchsess <>
Date: Wed, 1 Oct 2014 10:53:51 +0000
Subject: [PATCH] MQTT: initial checkin of MQTT and MQTT_DEVICE
git-svn-id: https://svn.fhem.de/fhem/trunk@6648 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
fhem/FHEM/00_MQTT.pm | 352 ++++++++++++++++++
fhem/FHEM/10_MQTT_DEVICE.pm | 272 ++++++++++++++
fhem/FHEM/lib/Net/MQTT.pod | 59 +++
fhem/FHEM/lib/Net/MQTT/Constants.pm | 270 ++++++++++++++
fhem/FHEM/lib/Net/MQTT/Message.pm | 246 ++++++++++++
fhem/FHEM/lib/Net/MQTT/Message/ConnAck.pm | 92 +++++
fhem/FHEM/lib/Net/MQTT/Message/Connect.pm | 246 ++++++++++++
fhem/FHEM/lib/Net/MQTT/Message/Disconnect.pm | 51 +++
.../lib/Net/MQTT/Message/JustMessageId.pm | 74 ++++
fhem/FHEM/lib/Net/MQTT/Message/PingReq.pm | 51 +++
fhem/FHEM/lib/Net/MQTT/Message/PingResp.pm | 51 +++
fhem/FHEM/lib/Net/MQTT/Message/PubAck.pm | 59 +++
fhem/FHEM/lib/Net/MQTT/Message/PubComp.pm | 59 +++
fhem/FHEM/lib/Net/MQTT/Message/PubRec.pm | 59 +++
fhem/FHEM/lib/Net/MQTT/Message/PubRel.pm | 63 ++++
fhem/FHEM/lib/Net/MQTT/Message/Publish.pm | 104 ++++++
fhem/FHEM/lib/Net/MQTT/Message/SubAck.pm | 97 +++++
fhem/FHEM/lib/Net/MQTT/Message/Subscribe.pm | 106 ++++++
fhem/FHEM/lib/Net/MQTT/Message/UnsubAck.pm | 60 +++
fhem/FHEM/lib/Net/MQTT/Message/Unsubscribe.pm | 99 +++++
fhem/FHEM/lib/Net/MQTT/TopicStore.pm | 116 ++++++
21 files changed, 2586 insertions(+)
create mode 100644 fhem/FHEM/00_MQTT.pm
create mode 100644 fhem/FHEM/10_MQTT_DEVICE.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT.pod
create mode 100644 fhem/FHEM/lib/Net/MQTT/Constants.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/ConnAck.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/Connect.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/Disconnect.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/JustMessageId.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/PingReq.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/PingResp.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/PubAck.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/PubComp.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/PubRec.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/PubRel.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/Publish.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/SubAck.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/Subscribe.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/UnsubAck.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/Message/Unsubscribe.pm
create mode 100644 fhem/FHEM/lib/Net/MQTT/TopicStore.pm
diff --git a/fhem/FHEM/00_MQTT.pm b/fhem/FHEM/00_MQTT.pm
new file mode 100644
index 000000000..d8295ce55
--- /dev/null
+++ b/fhem/FHEM/00_MQTT.pm
@@ -0,0 +1,352 @@
+##############################################
+#
+# fhem bridge to mqtt (see http://mqtt.org)
+#
+# Copyright (C) 2014 Norbert Truchsess
+#
+# This file is part of fhem.
+#
+# Fhem 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.
+#
+# Fhem 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with fhem. If not, see .
+#
+# $Id$
+#
+##############################################
+
+use strict;
+use warnings;
+use GPUtils qw(:all);
+
+use Net::MQTT::Constants;
+use Net::MQTT::Message;
+
+require "10_MQTT_DEVICE.pm";
+
+my %sets = (
+ "connect" => "",
+ "disconnect" => "",
+);
+
+my %gets = (
+ "version" => ""
+);
+
+my @clients = qw(
+ MQTT_DEVICE
+);
+
+sub MQTT_Initialize($) {
+
+ my $hash = shift @_;
+
+ require "$main::attr{global}{modpath}/FHEM/DevIo.pm";
+
+ # Provider
+ $hash->{Clients} = join (':',@clients);
+ $hash->{ReadyFn} = "MQTT_Ready";
+ $hash->{ReadFn} = "MQTT_Read";
+
+ # Consumer
+ $hash->{DefFn} = "MQTT_Define";
+ $hash->{UndefFn} = "MQTT_Undef";
+ $hash->{SetFn} = "MQTT_Set";
+ $hash->{NotifyFn} = "MQTT_Notify";
+
+ $hash->{AttrList} = "keep-alive";
+}
+
+sub MQTT_Define($$) {
+ my ( $hash, $def ) = @_;
+
+ $hash->{NOTIFYDEV} = "global";
+ $hash->{msgid} = 1;
+
+ if ($main::init_done) {
+ return MQTT_Start($hash);
+ } else {
+ return undef;
+ }
+}
+
+sub MQTT_Undef($) {
+ MQTT_Stop(shift);
+}
+
+sub MQTT_Set($@) {
+ my ($hash, @a) = @_;
+ return "Need at least one parameters" if(@a < 2);
+ return "Unknown argument $a[1], choose one of " . join(" ", sort keys %sets)
+ if(!defined($sets{$a[1]}));
+ my $command = $a[1];
+ my $value = $a[2];
+
+ COMMAND_HANDLER: {
+ $command eq "connect" and do {
+ MQTT_Start($hash);
+ last;
+ };
+ $command eq "disconnect" and do {
+ MQTT_Stop($hash);
+ last;
+ };
+ };
+}
+
+sub MQTT_Notify($$) {
+ my ($hash,$dev) = @_;
+ if( grep(m/^(INITIALIZED|REREADCFG)$/, @{$dev->{CHANGED}}) ) {
+ MQTT_Start($hash);
+ } elsif( grep(m/^SAVE$/, @{$dev->{CHANGED}}) ) {
+ }
+}
+
+sub MQTT_Start($) {
+ my $hash = shift;
+ my ($dev) = split("[ \t]+", $hash->{DEF});
+ $hash->{DeviceName} = $dev;
+ DevIo_CloseDev($hash);
+ return DevIo_OpenDev($hash, 0, "MQTT_Init");
+}
+
+sub MQTT_Stop($) {
+ my $hash = shift;
+ MQTT_send_disconnect($hash);
+ DevIo_CloseDev($hash);
+ main::RemoveInternalTimer($hash);
+ main::readingsSingleUpdate($hash,"connection","disconnected",1);
+}
+
+sub MQTT_Ready($) {
+ my $hash = shift;
+ return DevIo_OpenDev($hash, 1, "MQTT_Init") if($hash->{STATE} eq "disconnected");
+}
+
+sub MQTT_Init($) {
+ my $hash = shift;
+ MQTT_send_connect($hash);
+ main::readingsSingleUpdate($hash,"connection","connecting",1);
+ $hash->{ping_received}=1;
+ MQTT_Timer($hash);
+ return undef;
+}
+
+sub MQTT_Timer($) {
+ my $hash = shift;
+ main::RemoveInternalTimer($hash);
+ main::readingsSingleUpdate($hash,"connection","timed-out",1) unless $hash->{ping_received};
+ $hash->{ping_received} = 0;
+ main::InternalTimer(gettimeofday()+main::AttrVal($hash-> {NAME},"keep-alive",60), "MQTT_Timer", $hash, 0);
+ MQTT_send_ping($hash);
+}
+
+sub MQTT_Read {
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+ my $buf = DevIo_SimpleRead($hash);
+ return undef unless $buf;
+ $hash->{buf} .= $buf;
+ while (my $mqtt = Net::MQTT::Message->new_from_bytes($hash->{buf},1)) {
+ my $message_type = $mqtt->message_type();
+
+ main::Log3($name,5,"MQTT $name message received: ".$mqtt->string());
+
+ MESSAGE_TYPE: {
+ $message_type == MQTT_CONNACK and do {
+ readingsSingleUpdate($hash,"connection","connected",1);
+ GP_ForallClients($hash,\&MQTT_DEVICE_Start);
+ last;
+ };
+
+ $message_type == MQTT_PUBLISH and do {
+ my $topic = $mqtt->topic();
+ GP_ForallClients($hash,sub {
+ my $client = shift;
+ main::Log3($client->{NAME},5,"publish received for $topic, ".$mqtt->message());
+ if (grep { $_ eq $topic } @{$client->{subscribe}}) {
+ readingsSingleUpdate($client,"transmission-state","publish received",1);
+ MQTT_DEVICE_onmessage($client,$topic,$mqtt->message());
+ };
+ },undef);
+ last;
+ };
+
+ $message_type == MQTT_PUBACK and do {
+ my $message_id = $mqtt->message_id();
+ GP_ForallClients($hash,sub {
+ my $client = shift;
+ if ($client->{message_ids}->{$message_id}) {
+ readingsSingleUpdate($client,"transmission-state","pubacknowledge received",1);
+ delete $client->{message_ids}->{$message_id};
+ };
+ },undef);
+ last;
+ };
+
+ $message_type == MQTT_PUBREC and do {
+ my $message_id = $mqtt->message_id();
+ GP_ForallClients($hash,sub {
+ my $client = shift;
+ if ($client->{message_ids}->{$message_id}) {
+ readingsSingleUpdate($client,"transmission-state","pubreceive received",1);
+ delete $client->{message_ids}->{$message_id};
+ };
+ },undef);
+ last;
+ };
+
+ $message_type == MQTT_PUBREL and do {
+ my $message_id = $mqtt->message_id();
+ GP_ForallClients($hash,sub {
+ my $client = shift;
+ if ($client->{message_ids}->{$message_id}) {
+ readingsSingleUpdate($client,"transmission-state","pubrelease received",1);
+ delete $client->{message_ids}->{$message_id};
+ };
+ },undef);
+ last;
+ };
+
+ $message_type == MQTT_PUBCOMP and do {
+ my $message_id = $mqtt->message_id();
+ GP_ForallClients($hash,sub {
+ my $client = shift;
+ if ($client->{message_ids}->{$message_id}) {
+ readingsSingleUpdate($client,"transmission-state","pubcomplete received",1);
+ delete $client->{message_ids}->{$message_id};
+ };
+ },undef);
+ last;
+ };
+
+ $message_type == MQTT_SUBACK and do {
+ my $message_id = $mqtt->message_id();
+ GP_ForallClients($hash,sub {
+ my $client = shift;
+ if ($client->{message_ids}->{$message_id}) {
+ readingsSingleUpdate($client,"transmission-state","subscription acknowledged",1);
+ delete $client->{message_ids}->{$message_id};
+ };
+ },undef);
+ last;
+ };
+
+ $message_type == MQTT_UNSUBACK and do {
+ my $message_id = $mqtt->message_id();
+ GP_ForallClients($hash,sub {
+ my $client = shift;
+ if ($client->{message_ids}->{$message_id}) {
+ readingsSingleUpdate($client,"transmission-state","unsubscription acknowledged",1);
+ delete $client->{message_ids}->{$message_id};
+ };
+ },undef);
+ last;
+ };
+
+ $message_type == MQTT_PINGRESP and do {
+ $hash->{ping_received} = 1;
+ main::readingsSingleUpdate($hash,"connection","active",1);
+ last;
+ };
+
+ main::Log3($hash->{NAME},4,"MQTT_Read '$hash->{NAME}' unexpected message type '".message_type_string($message_type)."'");
+ }
+ }
+ return undef;
+};
+
+sub MQTT_send_connect($) {
+ my $hash = shift;
+ return MQTT_send_message($hash, message_type => MQTT_CONNECT, keep_alive_timer => main::AttrVal($hash->{NAME},"keep-alive",60));
+};
+
+sub MQTT_send_publish($@) {
+ return MQTT_send_message(shift, message_type => MQTT_PUBLISH, @_);
+};
+
+sub MQTT_send_subscribe($@) {
+ my $hash = shift;
+ return MQTT_send_message($hash, message_type => MQTT_SUBSCRIBE, @_);
+};
+
+sub MQTT_send_unsubscribe($@) {
+ return MQTT_send_message(shift, message_type => MQTT_UNSUBSCRIBE, @_);
+};
+
+sub MQTT_send_ping($) {
+ return MQTT_send_message(shift, message_type => MQTT_PINGREQ);
+};
+
+sub MQTT_send_disconnect($) {
+ return MQTT_send_message(shift, message_type => MQTT_DISCONNECT);
+};
+
+sub MQTT_send_message($$$@) {
+ my $hash = shift;
+ my $name = $hash->{NAME};
+ my $msgid = $hash->{msgid}++;
+ my $msg = Net::MQTT::Message->new(message_id => $msgid,@_);
+ main::Log3($name,5,"MQTT $name message sent: ".$msg->string());
+ DevIo_SimpleWrite($hash,$msg->bytes);
+ return $msgid;
+};
+
+1;
+
+=pod
+=begin html
+
+
+
MQTT
+
+ connects fhem to mqtt
+
+ A single MQTT device can serve multiple MQTT_DEVICE clients.
+
+ Each MQTT_DEVICE acts as a bridge in between an fhem-device and mqtt.
+
+ Note: this module is based on module
+ Define
+
+ define <name> MQTT <ip:port>
+ Specifies the MQTT device.
+
+
+
+ Set
+
+ -
+
set <name> connect
+ (re-)connects the MQTT-device to the mqtt-broker
+
+ -
+
set <name> disconnect
+ disconnects the MQTT-device from the mqtt-broker
+
+
+
+
+ Attributes
+
+ - keep-alive
+ sets the keep-alive time (in seconds).
+
+
+
+
+
+
+=end html
+=cut
diff --git a/fhem/FHEM/10_MQTT_DEVICE.pm b/fhem/FHEM/10_MQTT_DEVICE.pm
new file mode 100644
index 000000000..80c633521
--- /dev/null
+++ b/fhem/FHEM/10_MQTT_DEVICE.pm
@@ -0,0 +1,272 @@
+##############################################
+#
+# fhem bridge to mqtt (see http://mqtt.org)
+#
+# Copyright (C) 2014 Norbert Truchsess
+#
+# This file is part of fhem.
+#
+# Fhem 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.
+#
+# Fhem 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with fhem. If not, see .
+#
+# $Id$
+#
+##############################################
+
+use strict;
+use warnings;
+use GPUtils qw(:all);
+
+use Net::MQTT::Constants;
+use Net::MQTT::Message;
+
+my %sets = (
+);
+
+my %gets = (
+ "version" => "",
+ "readings" => ""
+);
+
+my %qos = map {qos_string($_) => $_} (MQTT_QOS_AT_MOST_ONCE,MQTT_QOS_AT_LEAST_ONCE,MQTT_QOS_EXACTLY_ONCE);
+
+sub MQTT_DEVICE_Initialize($) {
+
+ my $hash = shift @_;
+
+ # Consumer
+ $hash->{DefFn} = "MQTT_DEVICE_Define";
+ $hash->{UndefFn} = "MQTT_DEVICE_Undef";
+ #$hash->{SetFn} = "MQTT_DEVICE_Set";
+ $hash->{GetFn} = "MQTT_DEVICE_Get";
+ $hash->{NotifyFn} = "MQTT_DEVICE_Notify";
+ $hash->{AttrFn} = "MQTT_DEVICE_Attr";
+
+ $hash->{AttrList} =
+ "IODev ".
+ "qos:".join(",",keys %qos)." ".
+ "publish-topic-base ".
+ "publishState ".
+ "publishReading_.* ".
+ "subscribeSet ".
+ "subscribeSet_.* ".
+ $main::readingFnAttributes;
+}
+
+sub MQTT_DEVICE_Define($$) {
+ my ( $hash, $def ) = @_;
+
+ $hash->{NOTIFYDEV} = $hash->{DEF};
+ $hash->{qos} = MQTT_QOS_AT_MOST_ONCE;
+ $hash->{subscribe} = [];
+ if ($main::init_done) {
+ return MQTT_DEVICE_Start($hash);
+ } else {
+ return undef;
+ }
+}
+
+sub MQTT_DEVICE_Undef($) {
+ MQTT_DEVICE_Stop(shift);
+}
+
+sub MQTT_DEVICE_Get($$@) {
+ my ($hash, $name, $command) = @_;
+ return "Need at least one parameters" unless (defined $command);
+ return "Unknown argument $command, choose one of " . join(" ", sort keys %gets)
+ unless (defined($gets{$command}));
+
+ COMMAND_HANDLER: {
+ # populate dynamically from keys %{$defs{$sdev}{READINGS}}
+ $command eq "readings" and do {
+ my $base = AttrVal($name,"publish-topic-base","/$hash->{DEF}/");
+ foreach my $reading (keys %{$main::defs{$hash->{DEF}}{READINGS}}) {
+ unless (defined main::AttrVal($name,"publishReading_$reading",undef)) {
+ main::CommandAttr($hash,"$name publishReading_$reading $base$reading");
+ }
+ };
+ last;
+ };
+ };
+}
+
+sub MQTT_DEVICE_Notify() {
+ my ($hash,$dev) = @_;
+
+ main::Log3($hash->{NAME},5,"Notify for $dev->{NAME}");
+ foreach my $event (@{$dev->{CHANGED}}) {
+ $event =~ /^([^:]+)(: )?(.*)$/;
+ main::Log3($hash->{NAME},5,"$event, '".((defined $1) ? $1 : "-undef-")."', '".((defined $3) ? $3 : "-undef-")."'");
+ if (defined $3 and $3 ne "") {
+ if (defined $hash->{publishReadings}->{$1}) {
+ MQTT_send_publish($hash->{IODev}, topic => $hash->{publishReadings}->{$1}, message => $3, qos => $hash->{qos});
+ readingsSingleUpdate($hash,"transmission-state","publish sent",1);
+ }
+ } else {
+ if (defined $hash->{publishState}) {
+ MQTT_send_publish($hash->{IODev}, topic => $hash->{publishState}, message => $1, qos => $hash->{qos});
+ readingsSingleUpdate($hash,"transmission-state","publish sent",1);
+ }
+ }
+ }
+}
+
+sub MQTT_DEVICE_Attr($$$$) {
+ my ($command,$name,$attribute,$value) = @_;
+
+ my $hash = $main::defs{$name};
+ ATTRIBUTE_HANDLER: {
+ $attribute =~ /^subscribeSet(_?)(.*)/ and do {
+ if ($command eq "set") {
+ $hash->{subscribeSets}->{$value} = $2;
+ push @{$hash->{subscribe}},$value unless grep {$_ eq $value} @{$hash->{subscribe}};
+ if ($main::init_done) {
+ if (my $mqtt = $hash->{IODev}) {;
+ my $msgid = MQTT_send_subscribe($mqtt,
+ topics => [[$value => $hash->{qos} || MQTT_QOS_AT_MOST_ONCE]],
+ );
+ $hash->{message_ids}->{$msgid}++;
+ readingsSingleUpdate($hash,"transmission-state","subscribe sent",1)
+ }
+ }
+ } else {
+ if ($main::init_done) {
+ if (my $mqtt = $hash->{IODev}) {;
+ my $msgid = MQTT_send_unsubscribe($mqtt,
+ topics => [$value],
+ );
+ $hash->{message_ids}->{$msgid}++;
+ }
+ }
+ delete $hash->{subscribeSets}->{$value};
+ $hash->{subscribe} = [grep { $_ != $value } @{$hash->{subscribe}}];
+ }
+ last;
+ };
+ $attribute eq "publishState" and do {
+ if ($command eq "set") {
+ $hash->{publishState} = $value;
+ } else {
+ delete $hash->{publishState};
+ }
+ last;
+ };
+ $attribute =~ /^publishReading_(.+)$/ and do {
+ if ($command eq "set") {
+ $hash->{publishReadings}->{$1} = $value;
+ } else {
+ delete $hash->{publishReadings}->{$1};
+ }
+ last;
+ };
+ $attribute eq "qos" and do {
+ if ($command eq "set") {
+ $hash->{qos} = $qos{$value};
+ } else {
+ $hash->{qos} = MQTT_QOS_AT_MOST_ONCE;
+ }
+ last;
+ };
+ $attribute eq "IODev" and do {
+ if ($command eq "set") {
+ } else {
+ }
+ last;
+ };
+ }
+}
+
+sub MQTT_DEVICE_Start($) {
+ my $hash = shift;
+ my $name = $hash->{NAME};
+ if (! (defined AttrVal($name,"stateFormat",undef))) {
+ $main::attr{$name}{stateFormat} = "transmission-state";
+ }
+ if (@{$hash->{subscribe}}) {
+ my $msgid = MQTT_send_subscribe($hash->{IODev},
+ topics => [map { [$_ => $hash->{qos} || MQTT_QOS_AT_MOST_ONCE] } @{$hash->{subscribe}}],
+ );
+ $hash->{message_ids}->{$msgid}++;
+ readingsSingleUpdate($hash,"transmission-state","subscribe sent",1)
+ }
+}
+
+sub MQTT_DEVICE_Stop($) {
+ my $hash = shift;
+ if (@{$hash->{subscribe}}) {
+ my $msgid = MQTT_send_unsubscribe($hash->{IODev},
+ topics => [@{$hash->{subscribe}}],
+ );
+ $hash->{message_ids}->{$msgid}++;
+ readingsSingleUpdate($hash,"transmission-state","unsubscribe sent",1)
+ }
+}
+
+sub MQTT_DEVICE_onmessage($$$) {
+ my ($hash,$topic,$message) = @_;
+ if (defined (my $command = $hash->{subscribeSets}->{$topic})) {
+ my @args = split ("[ \t]+",$message);
+ if ($command eq "") {
+ main::Log3($hash->{NAME},5,"calling DoSet($hash->{DEF}".(@args ? ",".join(",",@args) : ""));
+ main::DoSet($hash->{DEF},@args);
+ } else {
+ main::Log3($hash->{NAME},5,"calling DoSet($hash->{DEF},$command".(@args ? ",".join(",",@args) : ""));
+ main::DoSet($hash->{DEF},$command,@args);
+ }
+ }
+}
+1;
+
+=pod
+=begin html
+
+
+MQTT
+
+ acts as a bridge in between an fhem-device and mqtt-topics.
+
+ requires a MQTT-device as IODev
+
+ Note: this module is based on module
+ Define
+
+ define <name> MQTT_DEVICE <fhem-device-name>
+ Specifies the MQTT device.
+ <fhem-device-name> is the fhem-device this MQTT_DEVICE is linked to.
+
+
+ Get
+
+ -
+
get <name> readings
+ retrieves all existing readings from fhem-device and configures (default-)topics for them.
+
+
+
+
+
+ Attributes
+
+
+
+
+
+=end html
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT.pod b/fhem/FHEM/lib/Net/MQTT.pod
new file mode 100644
index 000000000..0a75b279b
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT.pod
@@ -0,0 +1,59 @@
+use strict;
+use warnings;
+package Net::MQTT;
+$Net::MQTT::VERSION = '1.142010';
+# ABSTRACT: Perl modules for MQTT Protocol (http://mqtt.org/)
+
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT - Perl modules for MQTT Protocol (http://mqtt.org/)
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # net-mqtt-sub /topic
+
+ # net-mqtt-pub /topic message
+
+ # net-mqtt-trace mqtt.tcp
+
+=head1 DESCRIPTION
+
+Low level API for the MQTT protocol described at L.
+
+B This is an early release and the API is still subject to
+change.
+
+=head1 DISCLAIMER
+
+This is B official IBM code. I work for IBM but I'm writing this
+in my spare time (with permission) for fun.
+
+=head1 SEE ALSO
+
+net-mqtt-sub(1), net-mqtt-pub(1), net-mqtt-trace(1), Net::MQTT::Message(3)
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Constants.pm b/fhem/FHEM/lib/Net/MQTT/Constants.pm
new file mode 100644
index 000000000..738b566e3
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Constants.pm
@@ -0,0 +1,270 @@
+use strict;
+use warnings;
+package Net::MQTT::Constants;
+$Net::MQTT::Constants::VERSION = '1.142010';
+# ABSTRACT: Module to export constants for MQTT protocol
+
+
+use Carp qw/croak/;
+
+my %constants =
+ (
+ MQTT_CONNECT => 0x1,
+ MQTT_CONNACK => 0x2,
+ MQTT_PUBLISH => 0x3,
+ MQTT_PUBACK => 0x4,
+ MQTT_PUBREC => 0x5,
+ MQTT_PUBREL => 0x6,
+ MQTT_PUBCOMP => 0x7,
+ MQTT_SUBSCRIBE => 0x8,
+ MQTT_SUBACK => 0x9,
+ MQTT_UNSUBSCRIBE => 0xa,
+ MQTT_UNSUBACK => 0xb,
+ MQTT_PINGREQ => 0xc,
+ MQTT_PINGRESP => 0xd,
+ MQTT_DISCONNECT => 0xe,
+
+ MQTT_QOS_AT_MOST_ONCE => 0x0,
+ MQTT_QOS_AT_LEAST_ONCE => 0x1,
+ MQTT_QOS_EXACTLY_ONCE => 0x2,
+
+ MQTT_CONNECT_ACCEPTED => 0,
+ MQTT_CONNECT_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION => 1,
+ MQTT_CONNECT_REFUSED_IDENTIFIER_REJECTED => 2,
+ MQTT_CONNECT_REFUSED_SERVER_UNAVAILABLE => 3,
+ MQTT_CONNECT_REFUSED_BAD_USER_NAME_OR_PASSWORD => 4,
+ MQTT_CONNECT_REFUSED_NOT_AUTHORIZED => 5,
+ );
+
+sub import {
+ no strict qw/refs/; ## no critic
+ my $pkg = caller(0);
+ foreach (keys %constants) {
+ my $v = $constants{$_};
+ *{$pkg.'::'.$_} = sub () { $v };
+ }
+ foreach (qw/decode_byte encode_byte
+ decode_short encode_short
+ decode_string encode_string
+ decode_remaining_length encode_remaining_length
+ qos_string
+ message_type_string
+ dump_string
+ connect_return_code_string
+ /) {
+ *{$pkg.'::'.$_} = \&{$_};
+ }
+}
+
+
+sub decode_remaining_length {
+ my ($data, $offset) = @_;
+ my $multiplier = 1;
+ my $v = 0;
+ my $d;
+ do {
+ $d = decode_byte($data, $offset);
+ $v += ($d&0x7f) * $multiplier;
+ $multiplier *= 128;
+ } while ($d&0x80);
+ $v
+}
+
+
+sub encode_remaining_length {
+ my $v = shift;
+ my $o;
+ my $d;
+ do {
+ $d = $v % 128;
+ $v = int($v/128);
+ if ($v) {
+ $d |= 0x80;
+ }
+ $o .= encode_byte($d);
+ } while ($d&0x80);
+ $o;
+}
+
+
+sub decode_byte {
+ my ($data, $offset) = @_;
+ croak 'decode_byte: insufficient data' unless (length $data >= $$offset+1);
+ my $res = unpack 'C', substr $data, $$offset, 1;
+ $$offset++;
+ $res
+}
+
+
+sub encode_byte {
+ pack 'C', $_[0];
+}
+
+
+sub decode_short {
+ my ($data, $offset) = @_;
+ croak 'decode_short: insufficient data' unless (length $data >= $$offset+2);
+ my $res = unpack 'n', substr $data, $$offset, 2;
+ $$offset += 2;
+ $res;
+}
+
+
+sub encode_short {
+ pack 'n', $_[0];
+}
+
+
+sub decode_string {
+ my ($data, $offset) = @_;
+ my $len = decode_short($data, $offset);
+ croak 'decode_string: insufficient data'
+ unless (length $data >= $$offset+$len);
+ my $res = substr $data, $$offset, $len;
+ $$offset += $len;
+ $res;
+}
+
+
+sub encode_string {
+ pack "n/a*", $_[0];
+}
+
+
+sub qos_string {
+ [qw/at-most-once at-least-once exactly-once reserved/]->[$_[0]]
+}
+
+
+sub message_type_string {
+ [qw/Reserved0 Connect ConnAck Publish PubAck PubRec PubRel PubComp
+ Subscribe SubAck Unsubscribe UnsubAck PingReq PingResp Disconnect
+ Reserved15/]->[$_[0]];
+}
+
+
+sub dump_string {
+ my $data = shift || '';
+ my $prefix = shift || '';
+ $prefix .= ' ';
+ my @lines;
+ while (length $data) {
+ my $d = substr $data, 0, 16, '';
+ my $line = unpack 'H*', $d;
+ $line =~ s/([A-F0-9]{2})/$1 /ig;
+ $d =~ s/[^ -~]/./g;
+ $line = sprintf "%-48s %s", $line, $d;
+ push @lines, $line
+ }
+ scalar @lines ? "\n".$prefix.(join "\n".$prefix, @lines) : ''
+}
+
+
+
+sub connect_return_code_string {
+ [
+ 'Connection Accepted',
+ 'Connection Refused: unacceptable protocol version',
+ 'Connection Refused: identifier rejected',
+ 'Connection Refused: server unavailable',
+ 'Connection Refused: bad user name or password',
+ 'Connection Refused: not authorized',
+ ]->[$_[0]] || 'Reserved'
+}
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Constants - Module to export constants for MQTT protocol
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ use Net::MQTT::Constants;
+
+=head1 DESCRIPTION
+
+Module to export constants for MQTT protocol.
+
+=head1 C
+
+=head2 C
+
+Calculates the C from the bytes in C<$data> starting
+at the offset read from the scalar reference. The offset reference is
+subsequently incremented by the number of bytes processed.
+
+=head2 C
+
+Calculates the C bytes from the length, C<$length>,
+and returns the packed bytes as a string.
+
+=head2 C
+
+Returns a byte by unpacking it from C<$data> starting at the offset
+read from the scalar reference. The offset reference is subsequently
+incremented by the number of bytes processed.
+
+=head2 C
+
+Returns a packed byte.
+
+=head2 C
+
+Returns a short (two bytes) by unpacking it from C<$data> starting at
+the offset read from the scalar reference. The offset reference is
+subsequently incremented by the number of bytes processed.
+
+=head2 C
+
+Returns a packed short (two bytes).
+
+=head2 C
+
+Returns a string (short length followed by length bytes) by unpacking
+it from C<$data> starting at the offset read from the scalar
+reference. The offset reference is subsequently incremented by the
+number of bytes processed.
+
+=head2 C
+
+Returns a packed string (length as a short and then the bytes of the
+string).
+
+=head2 C
+
+Returns a string describing the given QoS value.
+
+=head2 C
+
+Returns a string describing the given C value.
+
+=head2 C
+
+Returns a string representation of arbitrary data - as a string if it
+contains only printable characters or as a hex dump otherwise.
+
+=head2 C
+
+Returns a string describing the given C value.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message.pm b/fhem/FHEM/lib/Net/MQTT/Message.pm
new file mode 100644
index 000000000..842f95c70
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message.pm
@@ -0,0 +1,246 @@
+use strict;
+use warnings;
+package Net::MQTT::Message;
+$Net::MQTT::Message::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent MQTT messages
+
+
+use Net::MQTT::Constants qw/:all/;
+use Module::Pluggable search_path => __PACKAGE__, require => 1;
+
+our %types;
+foreach (plugins()) {
+ my $m = $_.'::message_type';
+ next unless (defined &{$m}); # avoid super classes
+ my $t = $_->message_type;
+ if (exists $types{$t}) {
+ die 'Duplicate message_type number ', $t, ":\n",
+ ' ', $_, " and\n",
+ ' ', $types{$t}, "\n";
+ }
+ $types{$t} = $_;
+}
+
+
+sub new {
+ my ($pkg, %p) = @_;
+ my $type_pkg =
+ exists $types{$p{message_type}} ? $types{$p{message_type}} : $pkg;
+ bless { %p }, $type_pkg;
+}
+
+
+sub new_from_bytes {
+ my ($pkg, $bytes, $splice) = @_;
+ my %p;
+ return if (length $bytes < 2);
+ my $offset = 0;
+ my $b = decode_byte($bytes, \$offset);
+ $p{message_type} = ($b&0xf0) >> 4;
+ $p{dup} = ($b&0x8)>>3;
+ $p{qos} = ($b&0x6)>>1;
+ $p{retain} = ($b&0x1);
+ my $length;
+ eval {
+ $length = decode_remaining_length($bytes, \$offset);
+ };
+ return if ($@);
+ if (length $bytes < $offset+$length) {
+ return
+ }
+ substr $_[1], 0, $offset+$length, '' if ($splice);
+ $p{remaining} = substr $bytes, $offset, $length;
+ my $self = $pkg->new(%p);
+ $self->_parse_remaining();
+ $self;
+}
+
+sub _parse_remaining {
+}
+
+
+sub message_type { shift->{message_type} }
+
+
+sub dup { shift->{dup} || 0 }
+
+
+sub qos {
+ my $self = shift;
+ defined $self->{qos} ? $self->{qos} : $self->_default_qos
+}
+
+sub _default_qos {
+ MQTT_QOS_AT_MOST_ONCE
+}
+
+
+sub retain { shift->{retain} || 0 }
+
+
+sub remaining { shift->{remaining} || '' }
+
+sub _remaining_string {
+ my ($self, $prefix) = @_;
+ dump_string($self->remaining, $prefix);
+}
+
+sub _remaining_bytes { shift->remaining }
+
+
+sub string {
+ my ($self, $prefix) = @_;
+ $prefix = '' unless (defined $prefix);
+ my @attr;
+ push @attr, qos_string($self->qos);
+ foreach (qw/dup retain/) {
+ my $bool = $self->$_;
+ push @attr, $_ if ($bool);
+ }
+ my $r = $self->_remaining_string($prefix);
+ $prefix.message_type_string($self->message_type).
+ '/'.(join ',', @attr).($r ? ' '.$r : '')
+}
+
+
+sub bytes {
+ my ($self) = shift;
+ my $o = '';
+ my $b =
+ ($self->message_type << 4) | ($self->dup << 3) |
+ ($self->qos << 1) | $self->retain;
+ $o .= encode_byte($b);
+ my $remaining = $self->_remaining_bytes;
+ $o .= encode_remaining_length(length $remaining);
+ $o .= $remaining;
+ $o;
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message - Perl module to represent MQTT messages
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ use Net::MQTT::Constants;
+ use Net::MQTT::Message;
+ use IO::Socket::INET;
+ my $socket = IO::Socket::INET->new(PeerAddr => '127.0.0.1:1883');
+ my $mqtt = Net::MQTT::Message->new(message_type => MQTT_CONNECT);
+ print $socket $mqtt->bytes;
+
+ my $tcp_payload = pack 'H*', '300d000774657374696e6774657374';
+ $mqtt = Net::MQTT::Message->new_from_bytes($tcp_payload);
+ print 'Received: ', $mqtt->string, "\n";
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT message. It uses subclasses to
+represent specific message types.
+
+=head1 METHODS
+
+=head2 C
+
+Constructs an L object based on the given
+parameters. The common parameter keys are:
+
+=over
+
+=item C
+
+The message type field of the MQTT message. This should be an integer
+between 0 and 15 inclusive. The module L
+provides constants that can be used for this value. This parameter
+is required.
+
+=item C
+
+The duplicate flag field of the MQTT message. This should be either 1
+or 0. The default is 0.
+
+=item C
+
+The QoS field of the MQTT message. This should be an integer between
+0 and 3 inclusive. The default is as specified in the spec or 0 ("at
+most once") otherwise. The module L provides
+constants that can be used for this value.
+
+=item C
+
+The retain flag field of the MQTT message. This should be either 1
+or 0. The default is 0.
+
+=back
+
+The remaining keys are dependent on the specific message type. The
+documentation for the subclasses for each message type list methods
+with the same name as the required keys.
+
+=head2 C
+
+Attempts to constructs an L object based on
+the given packed byte string. If there are insufficient bytes, then
+undef is returned. If the splice parameter is provided and true, then
+the processed bytes are removed from the scalar referenced by the
+$packed_bytes parameter.
+
+=head2 C
+
+Returns the message type field of the MQTT message. The module
+L provides a function, C,
+that can be used to convert this value to a human readable string.
+
+=head2 C
+
+The duplicate flag field of the MQTT message.
+
+=head2 C
+
+The QoS field of the MQTT message. The module
+L provides a function, C, that
+can be used to convert this value to a human readable string.
+
+=head2 C
+
+The retain field of the MQTT message.
+
+=head2 C
+
+This contains a packed string of bytes with any of the payload of the
+MQTT message that was not parsed by these modules. This should not
+be required for packets that strictly follow the standard.
+
+=head2 C
+
+Returns a summary of the message as a string suitable for logging.
+If provided, each line will be prefixed by the optional prefix.
+
+=head2 C
+
+Returns the bytes of the message suitable for writing to a socket.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/ConnAck.pm b/fhem/FHEM/lib/Net/MQTT/Message/ConnAck.pm
new file mode 100644
index 000000000..fc81ffe3b
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/ConnAck.pm
@@ -0,0 +1,92 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::ConnAck;
+$Net::MQTT::Message::ConnAck::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent an MQTT ConnAck message
+
+
+use base 'Net::MQTT::Message';
+use Net::MQTT::Constants qw/:all/;
+
+sub message_type {
+ 2
+}
+
+
+sub connack_reserved { shift->{connack_reserved} || 0 }
+
+
+sub return_code { shift->{return_code} || MQTT_CONNECT_ACCEPTED }
+
+sub _remaining_string {
+ my ($self, $prefix) = @_;
+ connect_return_code_string($self->return_code).
+ ' '.$self->SUPER::_remaining_string($prefix)
+}
+
+sub _parse_remaining {
+ my $self = shift;
+ my $offset = 0;
+ $self->{connack_reserved} = decode_byte($self->{remaining}, \$offset);
+ $self->{return_code} = decode_byte($self->{remaining}, \$offset);
+ substr $self->{remaining}, 0, $offset, '';
+}
+
+sub _remaining_bytes {
+ my $self = shift;
+ my $o = encode_byte($self->connack_reserved);
+ $o .= encode_byte($self->return_code);
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::ConnAck - Perl module to represent an MQTT ConnAck message
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # instantiated by Net::MQTT::Message
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT Connection Acknowledgement
+message. It is a specific subclass used by L
+and should not need to be instantiated directly.
+
+=head1 METHODS
+
+=head2 C
+
+Returns the reserved field of the MQTT Connection Acknowledgement
+message.
+
+=head2 C
+
+Returns the return code field of the MQTT Connection Acknowledgement
+message. The module L provides a function,
+C, that can be used to convert this value
+to a human readable string.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/Connect.pm b/fhem/FHEM/lib/Net/MQTT/Message/Connect.pm
new file mode 100644
index 000000000..bf27b8598
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/Connect.pm
@@ -0,0 +1,246 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::Connect;
+$Net::MQTT::Message::Connect::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent an MQTT Connect message
+
+
+use base 'Net::MQTT::Message';
+use Net::MQTT::Constants qw/:all/;
+
+sub message_type {
+ 1
+}
+
+
+sub protocol_name { shift->{protocol_name} || 'MQIsdp' }
+
+
+sub protocol_version { shift->{protocol_version} || 3 }
+
+
+sub user_name_flag {
+ my $self = shift;
+ $self->{user_name_flag} || defined $self->{user_name};
+}
+
+
+sub password_flag {
+ my $self = shift;
+ $self->{password_flag} || defined $self->{password};
+}
+
+
+sub will_retain { shift->{will_retain} || 0 }
+
+
+sub will_qos { shift->{will_qos} || 0 }
+
+
+sub will_flag {
+ my $self = shift;
+ $self->{will_flag} || defined $self->{will_topic}
+}
+
+
+sub clean_session {
+ my $self = shift;
+ defined $self->{clean_session} ? $self->{clean_session} : 1
+}
+
+
+sub connect_reserved_flag { shift->{connect_reserved_flag} || 0 }
+
+
+sub keep_alive_timer { shift->{keep_alive_timer} || 60 }
+
+
+sub client_id { shift->{client_id} || 'Net::MQTT::Message['.$$.']' }
+
+
+sub will_topic { shift->{will_topic} }
+
+
+sub will_message { shift->{will_message} }
+
+
+sub user_name { shift->{user_name} }
+
+
+sub password { shift->{password} }
+
+sub _remaining_string {
+ my ($self, $prefix) = @_;
+ $self->protocol_name.'/'.$self->protocol_version.'/'.$self->client_id.
+ ($self->user_name_flag ? ' user='.$self->user_name : '').
+ ($self->password_flag ? ' pass='.$self->password : '').
+ ($self->will_flag
+ ? ' will='.$self->will_topic.',"'.$self->will_message.'",'.
+ $self->will_retain.','.qos_string($self->will_qos) : '').
+ ' '.$self->SUPER::_remaining_string($prefix)
+}
+
+sub _parse_remaining {
+ my $self = shift;
+ my $offset = 0;
+ $self->{protocol_name} = decode_string($self->{remaining}, \$offset);
+ $self->{protocol_version} = decode_byte($self->{remaining}, \$offset);
+ my $b = decode_byte($self->{remaining}, \$offset);
+ $self->{user_name_flag} = ($b&0x80) >> 7;
+ $self->{password_flag} = ($b&0x40) >> 6;
+ $self->{will_retain} = ($b&0x20) >> 5;
+ $self->{will_qos} = ($b&0x18) >> 3;
+ $self->{will_flag} = ($b&0x4) >> 2;
+ $self->{clean_session} = ($b&0x2) >> 1;
+ $self->{connect_reserved_flag} = $b&0x1;
+ $self->{keep_alive_timer} = decode_short($self->{remaining}, \$offset);
+ $self->{client_id} = decode_string($self->{remaining}, \$offset);
+ if ($self->will_flag) {
+ $self->{will_topic} = decode_string($self->{remaining}, \$offset);
+ $self->{will_message} = decode_string($self->{remaining}, \$offset);
+ }
+ if ($self->user_name_flag) {
+ $self->{user_name} = decode_string($self->{remaining}, \$offset);
+ }
+ if ($self->password_flag) {
+ $self->{password} = decode_string($self->{remaining}, \$offset);
+ }
+ substr $self->{remaining}, 0, $offset, '';
+}
+
+sub _remaining_bytes {
+ my $self = shift;
+ my $o = encode_string($self->protocol_name);
+ $o .= encode_byte($self->protocol_version);
+ $o .= encode_byte(
+ ($self->user_name_flag << 7) |
+ ($self->password_flag << 6) |
+ ($self->will_retain << 5) | ($self->will_qos << 3) |
+ ($self->will_flag << 2) |
+ ($self->clean_session << 1) |
+ $self->connect_reserved_flag);
+ $o .= encode_short($self->keep_alive_timer);
+ $o .= encode_string($self->client_id);
+ $o .= encode_string($self->will_topic) if ($self->will_flag);
+ $o .= encode_string($self->will_message) if ($self->will_flag);
+ $o .= encode_string($self->user_name) if ($self->user_name_flag);
+ $o .= encode_string($self->password) if ($self->password_flag);
+ $o;
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::Connect - Perl module to represent an MQTT Connect message
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # instantiated by Net::MQTT::Message
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT Connection Request message. It
+is a specific subclass used by L and should
+not need to be instantiated directly.
+
+=head1 METHODS
+
+=head2 C
+
+Returns the protocol name field of the MQTT Connect message. The
+default is 'C'.
+
+=head2 C
+
+Returns the protocol version field of the MQTT Connect message. The
+default is 3.
+
+=head2 C
+
+Returns the user name flag field of the MQTT Connect message. The
+default is true if and only if a user name is defined.
+
+=head2 C
+
+Returns the password flag field of the MQTT Connect message. The
+default is true if and only if a password is defined.
+
+=head2 C
+
+Returns the will retain field of the MQTT Connect message. The
+default is 0.
+
+=head2 C
+
+Returns the will QoS field of the MQTT Connect message. The default
+is 0.
+
+=head2 C
+
+Returns the will flag field of the MQTT Connect message. The
+default is true if and only if a will topic is defined.
+
+=head2 C
+
+Returns the clean session flag field of the MQTT Connect message. The
+default is 1.
+
+=head2 C
+
+Returns the reserved flag field of the MQTT Connect message. The
+default is 0.
+
+=head2 C
+
+Returns the keep alive timer field of the MQTT Connect message. The
+units are seconds. The default is 60.
+
+=head2 C
+
+Returns the client identifier field of the MQTT Connect message. The
+default is 'C' where 'C<$$>' is the
+current process id.
+
+=head2 C
+
+Returns the will topic field of the MQTT Connect message. The default
+is undefined.
+
+=head2 C
+
+Returns the will message field of the MQTT Connect message. The
+default is undefined.
+
+=head2 C
+
+Returns the user name field of the MQTT Connect message. The default
+is undefined.
+
+=head2 C
+
+Returns the password field of the MQTT Connect message. The default
+is undefined.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/Disconnect.pm b/fhem/FHEM/lib/Net/MQTT/Message/Disconnect.pm
new file mode 100644
index 000000000..85d83b6f1
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/Disconnect.pm
@@ -0,0 +1,51 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::Disconnect;
+$Net::MQTT::Message::Disconnect::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent an MQTT Disconnect message
+
+
+use base 'Net::MQTT::Message';
+
+sub message_type {
+ 14
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::Disconnect - Perl module to represent an MQTT Disconnect message
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # instantiated by Net::MQTT::Message
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT Disconnection Notification
+message. It is a specific subclass used by L
+and should not need to be instantiated directly.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/JustMessageId.pm b/fhem/FHEM/lib/Net/MQTT/Message/JustMessageId.pm
new file mode 100644
index 000000000..fbde29765
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/JustMessageId.pm
@@ -0,0 +1,74 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::JustMessageId;
+$Net::MQTT::Message::JustMessageId::VERSION = '1.142010';
+# ABSTRACT: Perl module for an MQTT message w/message id only payload
+
+
+use base 'Net::MQTT::Message';
+use Net::MQTT::Constants qw/:all/;
+
+
+sub message_id { shift->{message_id} }
+
+sub _remaining_string {
+ my ($self, $prefix) = @_;
+ $self->message_id.' '.$self->SUPER::_remaining_string($prefix)
+}
+
+sub _parse_remaining {
+ my $self = shift;
+ my $offset = 0;
+ $self->{message_id} = decode_short($self->{remaining}, \$offset);
+ substr $self->{remaining}, 0, $offset, '';
+}
+
+sub _remaining_bytes {
+ my $self = shift;
+ encode_short($self->message_id)
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::JustMessageId - Perl module for an MQTT message w/message id only payload
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # abstract class not instantiated directly
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT message that has only a message id
+in its payload. This is an abstract class used to implement a number
+of other MQTT messages such as PubAck, PubComp, etc.
+
+=head1 METHODS
+
+=head2 C
+
+Returns the message id field of the MQTT message.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/PingReq.pm b/fhem/FHEM/lib/Net/MQTT/Message/PingReq.pm
new file mode 100644
index 000000000..774f78636
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/PingReq.pm
@@ -0,0 +1,51 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::PingReq;
+$Net::MQTT::Message::PingReq::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent an MQTT PingReq message
+
+
+use base 'Net::MQTT::Message';
+
+sub message_type {
+ 12
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::PingReq - Perl module to represent an MQTT PingReq message
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # instantiated by Net::MQTT::Message
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT Ping Request message. It is a
+specific subclass used by L and should not
+need to be instantiated directly.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/PingResp.pm b/fhem/FHEM/lib/Net/MQTT/Message/PingResp.pm
new file mode 100644
index 000000000..c4ea611b5
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/PingResp.pm
@@ -0,0 +1,51 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::PingResp;
+$Net::MQTT::Message::PingResp::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent an MQTT PingResp message
+
+
+use base 'Net::MQTT::Message';
+
+sub message_type {
+ 13
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::PingResp - Perl module to represent an MQTT PingResp message
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # instantiated by Net::MQTT::Message
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT Ping Response message. It is a
+specific subclass used by L and should not
+need to be instantiated directly.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/PubAck.pm b/fhem/FHEM/lib/Net/MQTT/Message/PubAck.pm
new file mode 100644
index 000000000..0c7acdc6f
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/PubAck.pm
@@ -0,0 +1,59 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::PubAck;
+$Net::MQTT::Message::PubAck::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent an MQTT PubAck message
+
+
+use base 'Net::MQTT::Message::JustMessageId';
+use Net::MQTT::Constants qw/:all/;
+
+sub message_type {
+ 4
+}
+
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::PubAck - Perl module to represent an MQTT PubAck message
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # instantiated by Net::MQTT::Message
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT Publish Acknowledgement
+message. It is a specific subclass used by L
+and should not need to be instantiated directly.
+
+=head1 METHODS
+
+=head2 C
+
+Returns the message id field of the MQTT Publish Acknowledgement message.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/PubComp.pm b/fhem/FHEM/lib/Net/MQTT/Message/PubComp.pm
new file mode 100644
index 000000000..db666f87a
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/PubComp.pm
@@ -0,0 +1,59 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::PubComp;
+$Net::MQTT::Message::PubComp::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent an MQTT PubComp message
+
+
+use base 'Net::MQTT::Message::JustMessageId';
+use Net::MQTT::Constants qw/:all/;
+
+sub message_type {
+ 7
+}
+
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::PubComp - Perl module to represent an MQTT PubComp message
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # instantiated by Net::MQTT::Message
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT Publish Complete message. It
+is a specific subclass used by L and should
+not need to be instantiated directly.
+
+=head1 METHODS
+
+=head2 C
+
+Returns the message id field of the MQTT Publish Complete message.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/PubRec.pm b/fhem/FHEM/lib/Net/MQTT/Message/PubRec.pm
new file mode 100644
index 000000000..512ceb4db
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/PubRec.pm
@@ -0,0 +1,59 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::PubRec;
+$Net::MQTT::Message::PubRec::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent an MQTT PubRec message
+
+
+use base 'Net::MQTT::Message::JustMessageId';
+use Net::MQTT::Constants qw/:all/;
+
+sub message_type {
+ 5
+}
+
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::PubRec - Perl module to represent an MQTT PubRec message
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # instantiated by Net::MQTT::Message
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT Publish Received message. It
+is a specific subclass used by L and should
+not need to be instantiated directly.
+
+=head1 METHODS
+
+=head2 C
+
+Returns the message id field of the MQTT Publish Received message.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/PubRel.pm b/fhem/FHEM/lib/Net/MQTT/Message/PubRel.pm
new file mode 100644
index 000000000..8af264143
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/PubRel.pm
@@ -0,0 +1,63 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::PubRel;
+$Net::MQTT::Message::PubRel::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent an MQTT PubRel message
+
+
+use base 'Net::MQTT::Message::JustMessageId';
+use Net::MQTT::Constants qw/:all/;
+
+sub message_type {
+ 6
+}
+
+sub _default_qos {
+ MQTT_QOS_AT_LEAST_ONCE
+}
+
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::PubRel - Perl module to represent an MQTT PubRel message
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # instantiated by Net::MQTT::Message
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT Publish Release message. It is
+a specific subclass used by L and should not
+need to be instantiated directly.
+
+=head1 METHODS
+
+=head2 C
+
+Returns the message id field of the MQTT Publish Release message.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/Publish.pm b/fhem/FHEM/lib/Net/MQTT/Message/Publish.pm
new file mode 100644
index 000000000..9f0e356aa
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/Publish.pm
@@ -0,0 +1,104 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::Publish;
+$Net::MQTT::Message::Publish::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent an MQTT Publish message
+
+
+use base 'Net::MQTT::Message';
+use Net::MQTT::Constants qw/:all/;
+
+sub message_type {
+ 3
+}
+
+
+sub topic { shift->{topic} }
+
+
+sub message_id { shift->{message_id} }
+
+
+sub message { shift->{message} }
+
+sub _message_string { shift->{message} }
+
+sub _remaining_string {
+ my $self = shift;
+ $self->topic.
+ ($self->qos ? '/'.$self->message_id : '').
+ ' '.dump_string($self->_message_string)
+}
+
+sub _parse_remaining {
+ my $self = shift;
+ my $offset = 0;
+ $self->{topic} = decode_string($self->{remaining}, \$offset);
+ $self->{message_id} = decode_short($self->{remaining}, \$offset)
+ if ($self->qos);
+ $self->{message} = substr $self->{remaining}, $offset;
+ $self->{remaining} = '';
+}
+
+sub _remaining_bytes {
+ my $self = shift;
+ my $o = encode_string($self->topic);
+ if ($self->qos) {
+ $o .= encode_short($self->message_id);
+ }
+ $o .= $self->message;
+ $o;
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::Publish - Perl module to represent an MQTT Publish message
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # instantiated by Net::MQTT::Message
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT Publish message. It
+is a specific subclass used by L and should
+not need to be instantiated directly.
+
+=head1 METHODS
+
+=head2 C
+
+Returns the topic field of the MQTT Publish message.
+
+=head2 C
+
+Returns the message id field of the MQTT Publish message.
+
+=head2 C
+
+Returns the message field of the MQTT Publish message.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/SubAck.pm b/fhem/FHEM/lib/Net/MQTT/Message/SubAck.pm
new file mode 100644
index 000000000..25b31a7c7
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/SubAck.pm
@@ -0,0 +1,97 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::SubAck;
+$Net::MQTT::Message::SubAck::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent an MQTT SubAck message
+
+
+use base 'Net::MQTT::Message';
+use Net::MQTT::Constants qw/:all/;
+
+sub message_type {
+ 9
+}
+
+
+sub message_id { shift->{message_id} }
+
+
+sub qos_levels { shift->{qos_levels} }
+
+sub _remaining_string {
+ my ($self, $prefix) = @_;
+ $self->message_id.'/'.
+ (join ',', map { qos_string($_) } @{$self->qos_levels}).
+ ' '.$self->SUPER::_remaining_string($prefix)
+}
+
+sub _parse_remaining {
+ my $self = shift;
+ my $offset = 0;
+ $self->{message_id} = decode_short($self->{remaining}, \$offset);
+ while ($offset < length $self->{remaining}) {
+ push @{$self->{qos_levels}}, decode_byte($self->{remaining}, \$offset)&0x3;
+ }
+ substr $self->{remaining}, 0, $offset, '';
+}
+
+sub _remaining_bytes {
+ my $self = shift;
+ my $o = encode_short($self->message_id);
+ foreach my $qos (@{$self->qos_levels}) {
+ $o .= encode_byte($qos);
+ }
+ $o
+}
+
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::SubAck - Perl module to represent an MQTT SubAck message
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # instantiated by Net::MQTT::Message
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT Subscription Acknowledgement
+message. It is a specific subclass used by L
+and should not need to be instantiated directly.
+
+=head1 METHODS
+
+=head2 C
+
+Returns the message id field of the MQTT Subscription Acknowledgement
+message.
+
+=head2 C
+
+Returns the list of granted QoS fields of the MQTT Subscription
+Acknowledgement message.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/Subscribe.pm b/fhem/FHEM/lib/Net/MQTT/Message/Subscribe.pm
new file mode 100644
index 000000000..ce632e427
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/Subscribe.pm
@@ -0,0 +1,106 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::Subscribe;
+$Net::MQTT::Message::Subscribe::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent an MQTT Subscribe message
+
+
+use base 'Net::MQTT::Message';
+use Net::MQTT::Constants qw/:all/;
+
+sub message_type {
+ 8
+}
+
+sub _default_qos {
+ MQTT_QOS_AT_LEAST_ONCE
+}
+
+
+sub message_id { shift->{message_id} }
+
+
+sub topics { shift->{topics} }
+
+sub _topics_string {
+ join ',', map { $_->[0].'/'.qos_string($_->[1]) } @{shift->{topics}}
+}
+
+sub _remaining_string {
+ my ($self, $prefix) = @_;
+ $self->message_id.' '.$self->_topics_string.' '.
+ $self->SUPER::_remaining_string($prefix)
+}
+
+sub _parse_remaining {
+ my $self = shift;
+ my $offset = 0;
+ $self->{message_id} = decode_short($self->{remaining}, \$offset);
+ while ($offset < length $self->{remaining}) {
+ push @{$self->{topics}}, [ decode_string($self->{remaining}, \$offset),
+ decode_byte($self->{remaining}, \$offset) ];
+ }
+ substr $self->{remaining}, 0, $offset, '';
+}
+
+sub _remaining_bytes {
+ my $self = shift;
+ my $o = encode_short($self->message_id);
+ foreach my $r (@{$self->topics}) {
+ my ($name, $qos) = @$r;
+ $o .= encode_string($name);
+ $o .= encode_byte($qos);
+ }
+ $o
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::Subscribe - Perl module to represent an MQTT Subscribe message
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # instantiated by Net::MQTT::Message
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT Subscribe message. It is a
+specific subclass used by L and should not
+need to be instantiated directly.
+
+=head1 METHODS
+
+=head2 C
+
+Returns the message id field of the MQTT Subscribe message.
+
+=head2 C
+
+Returns the list of topics of the MQTT Subscribe message. Each
+element of the list is a 2-ple containing the topic and its associated
+requested QoS level.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/UnsubAck.pm b/fhem/FHEM/lib/Net/MQTT/Message/UnsubAck.pm
new file mode 100644
index 000000000..406863280
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/UnsubAck.pm
@@ -0,0 +1,60 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::UnsubAck;
+$Net::MQTT::Message::UnsubAck::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent an MQTT UnsubAck message
+
+
+use base 'Net::MQTT::Message::JustMessageId';
+use Net::MQTT::Constants qw/:all/;
+
+sub message_type {
+ 11
+}
+
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::UnsubAck - Perl module to represent an MQTT UnsubAck message
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # instantiated by Net::MQTT::Message
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT Unsubscribe Acknowledgement
+message. It is a specific subclass used by L
+and should not need to be instantiated directly.
+
+=head1 METHODS
+
+=head2 C
+
+Returns the message id field of the MQTT Unsubscribe Acknowledgement
+message.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/Message/Unsubscribe.pm b/fhem/FHEM/lib/Net/MQTT/Message/Unsubscribe.pm
new file mode 100644
index 000000000..7139c3490
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/Message/Unsubscribe.pm
@@ -0,0 +1,99 @@
+use strict;
+use warnings;
+package Net::MQTT::Message::Unsubscribe;
+$Net::MQTT::Message::Unsubscribe::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent an MQTT Unsubscribe message
+
+
+use base 'Net::MQTT::Message';
+use Net::MQTT::Constants qw/:all/;
+
+sub message_type {
+ 10
+}
+
+sub _default_qos {
+ MQTT_QOS_AT_LEAST_ONCE
+}
+
+
+sub message_id { shift->{message_id} }
+
+
+sub topics { shift->{topics} }
+
+sub _topics_string { join ',', @{shift->{topics}} }
+
+sub _remaining_string {
+ my ($self, $prefix) = @_;
+ $self->message_id.' '.$self->_topics_string.' '.
+ $self->SUPER::_remaining_string($prefix)
+}
+
+sub _parse_remaining {
+ my $self = shift;
+ my $offset = 0;
+ $self->{message_id} = decode_short($self->{remaining}, \$offset);
+ while ($offset < length $self->{remaining}) {
+ push @{$self->{topics}}, decode_string($self->{remaining}, \$offset);
+ }
+ substr $self->{remaining}, 0, $offset, '';
+}
+
+sub _remaining_bytes {
+ my $self = shift;
+ my $o = encode_short($self->message_id);
+ foreach my $name (@{$self->topics}) {
+ $o .= encode_string($name);
+ }
+ $o
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::Message::Unsubscribe - Perl module to represent an MQTT Unsubscribe message
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ # instantiated by Net::MQTT::Message
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT Unsubscribe message. It is a
+specific subclass used by L and should not
+need to be instantiated directly.
+
+=head1 METHODS
+
+=head2 C
+
+Returns the message id field of the MQTT Unsubscribe message.
+
+=head2 C
+
+Returns the list of topics of the MQTT Subscribe message.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/fhem/FHEM/lib/Net/MQTT/TopicStore.pm b/fhem/FHEM/lib/Net/MQTT/TopicStore.pm
new file mode 100644
index 000000000..3bb70d596
--- /dev/null
+++ b/fhem/FHEM/lib/Net/MQTT/TopicStore.pm
@@ -0,0 +1,116 @@
+use strict;
+use warnings;
+package Net::MQTT::TopicStore;
+$Net::MQTT::TopicStore::VERSION = '1.142010';
+# ABSTRACT: Perl module to represent MQTT topic store
+
+
+sub new {
+ my $pkg = shift;
+ my $self = bless { topics => { } }, $pkg;
+ $self->add($_) foreach (@_);
+ $self
+}
+
+
+sub add {
+ my ($self, $topic_pattern) = @_;
+ unless (exists $self->{topics}->{$topic_pattern}) {
+ $self->{topics}->{$topic_pattern} = _topic_to_regexp($topic_pattern);
+ }
+ $topic_pattern
+}
+
+
+sub delete {
+ my ($self, $topic) = @_;
+ delete $self->{topics}->{$topic};
+}
+
+
+sub values {
+ my ($self, $topic) = @_;
+ my @res = ();
+ foreach my $t (keys %{$self->{topics}}) {
+ my $re = $self->{topics}->{$t};
+ next unless (defined $re ? $topic =~ $re : $topic eq $t);
+ push @res, $t;
+ }
+ return \@res;
+}
+
+sub _topic_to_regexp {
+ my $topic = shift;
+ my $c;
+ $topic = quotemeta $topic;
+ $c += ($topic =~ s!\\/\\\+!\\/[^/]*!g);
+ $c += ($topic =~ s!\\/\\#$!(?:\$|/.*)!);
+ $c += ($topic =~ s!^\\\+\\/![^/]*\\/!g);
+ $c += ($topic =~ s!^\\\+$![^/]*!g);
+ $c += ($topic =~ s!^\\#$!.*!);
+ $topic .= '$' unless ($topic =~ m!\$$!);
+ unless ($c) {
+ return;
+ }
+ qr/^$topic/
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Net::MQTT::TopicStore - Perl module to represent MQTT topic store
+
+=head1 VERSION
+
+version 1.142010
+
+=head1 SYNOPSIS
+
+ use Net::MQTT::TopicStore;
+ my $topic_store = Net::MQTT::TopicStore->new();
+ $topic_store->add($topic_pattern1);
+ $topic_store->add($topic_pattern2);
+ my @topics = @{ $topic->get($topic) };
+ $topic_store->remove($topic_pattern2);
+
+=head1 DESCRIPTION
+
+This module encapsulates a single MQTT topic store.
+
+=head1 METHODS
+
+=head2 C
+
+Constructs a L object.
+
+=head2 C
+
+Adds the topic pattern to the store.
+
+=head2 C
+
+Remove the topic pattern from the store.
+
+=head2 C
+
+Returns all the topic patterns in the store that apply to the given topic.
+
+=head1 AUTHOR
+
+Mark Hindess
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Mark Hindess.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut