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

+ +
+ +=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

+ +
+ +=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