2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-10 03:06:37 +00:00

bugfix: disconnects with new mosquitto version (wrong use of DUP flag)

fix commandref

git-svn-id: https://svn.fhem.de/fhem/trunk@24953 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
hexenmeister 2021-09-11 16:21:40 +00:00
parent c147fd8b6f
commit f4a82eea92

View File

@ -25,6 +25,54 @@
# #
############################################## ##############################################
package MQTT;
use Exporter ('import');
@EXPORT = ();
@EXPORT_OK = qw(send_publish send_subscribe send_unsubscribe client_attr client_subscribe_topic client_unsubscribe_topic topic_to_regexp parseParams);
%EXPORT_TAGS = (all => [@EXPORT_OK]);
use strict;
use warnings;
use GPUtils qw(GP_Import GP_ForallClients);
use Scalar::Util qw(looks_like_number);
use Carp qw(carp);
use Net::MQTT::Constants;
use Net::MQTT::Message;
our %qos = map {qos_string($_) => $_} (MQTT_QOS_AT_MOST_ONCE,MQTT_QOS_AT_LEAST_ONCE,MQTT_QOS_EXACTLY_ONCE);
BEGIN {GP_Import(qw(
gettimeofday
readingsSingleUpdate
readingsBeginUpdate
readingsBulkUpdate
readingsEndUpdate
DevIo_OpenDev
DevIo_SimpleWrite
DevIo_SimpleRead
DevIo_CloseDev
DevIo_IsOpen
DevIo_getState
IsDisabled
RemoveInternalTimer
InternalTimer
AttrVal
ReadingsVal
Log3
AssignIoPort
getKeyValue
setKeyValue
CallFn
defs
modules
init_done
))};
sub ::MQTT_Initialize { goto &Initialize };
my %sets = ( my %sets = (
"connect" => "", "connect" => "",
"disconnect" => "", "disconnect" => "",
@ -42,66 +90,29 @@ my @clients = qw(
use DevIo; use DevIo;
sub MQTT_Initialize($) { sub Initialize {
my $hash = shift @_; my $hash = shift // return;
# Provider # Provider
$hash->{Clients} = join (':',@clients); $hash->{Clients} = join q{:}, @clients;
$hash->{ReadyFn} = "MQTT::Ready"; $hash->{ReadyFn} = \&Ready;
$hash->{ReadFn} = "MQTT::Read"; $hash->{ReadFn} = \&Read;
# Consumer # Consumer
$hash->{DefFn} = "MQTT::Define"; $hash->{DefFn} = \&Define;
$hash->{UndefFn} = "MQTT::Undef"; $hash->{UndefFn} = \&Undef;
$hash->{DeleteFn} = "MQTT::Delete"; $hash->{DeleteFn} = \&Delete;
$hash->{RenameFn} = "MQTT::Rename"; $hash->{RenameFn} = \&Rename;
$hash->{ShutdownFn} = "MQTT::Shutdown"; $hash->{ShutdownFn} = \&Shutdown;
$hash->{SetFn} = "MQTT::Set"; $hash->{SetFn} = \&Set;
$hash->{NotifyFn} = "MQTT::Notify"; $hash->{NotifyFn} = \&Notify;
$hash->{AttrFn} = "MQTT::Attr"; $hash->{AttrFn} = \&Attr;
$hash->{AttrList} = "keep-alive "."last-will client-id "."on-connect on-disconnect on-timeout privacy:1,0 ".$main::readingFnAttributes; $hash->{AttrList} = "keep-alive last-will client-id on-connect on-disconnect on-timeout privacy:1,0 ".$::readingFnAttributes;
} }
package MQTT;
use Exporter ('import');
@EXPORT = ();
@EXPORT_OK = qw(send_publish send_subscribe send_unsubscribe client_attr client_subscribe_topic client_unsubscribe_topic topic_to_regexp parseParams);
%EXPORT_TAGS = (all => [@EXPORT_OK]);
use strict;
use warnings;
use GPUtils qw(:all);
use Net::MQTT::Constants;
use Net::MQTT::Message;
our %qos = map {qos_string($_) => $_} (MQTT_QOS_AT_MOST_ONCE,MQTT_QOS_AT_LEAST_ONCE,MQTT_QOS_EXACTLY_ONCE);
BEGIN {GP_Import(qw(
gettimeofday
readingsSingleUpdate
DevIo_OpenDev
DevIo_SimpleWrite
DevIo_SimpleRead
DevIo_CloseDev
RemoveInternalTimer
InternalTimer
AttrVal
ReadingsVal
Log3
AssignIoPort
getKeyValue
setKeyValue
CallFn
defs
modules
looks_like_number
fhem
))};
sub Define($$) { sub Define($$) {
my ( $hash, $def ) = @_; my ( $hash, $def ) = @_;
@ -115,21 +126,15 @@ sub Define($$) {
$hash->{DeviceName} = $host; $hash->{DeviceName} = $host;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $user = getKeyValue($name."_user"); setKeyValue($name."_user",$username) if defined $username;
my $pass = getKeyValue($name."_pass"); setKeyValue($name."_pass",$password) if defined $password;
setKeyValue($name."_user",$username) unless(defined($user));
setKeyValue($name."_pass",$password) unless(defined($pass));
$hash->{DEF} = $host; $hash->{DEF} = $host;
#readingsSingleUpdate($hash,"connection","disconnected",0); #readingsSingleUpdate($hash,"connection","disconnected",0);
if ($main::init_done) { return Start($hash) if $init_done;
return Start($hash); return;
} else {
return undef;
}
} }
sub Undef($) { sub Undef($) {
@ -206,49 +211,22 @@ sub process_event($$) {
sub Set($@) { sub Set($@) {
my ($hash, @a) = @_; my ($hash, @a) = @_;
return "Need at least one parameters" if(@a < 2); return 'Need at least one parameter!' if @a < 2;
return "Unknown argument $a[1], choose one of " . join(" ", sort keys %sets) return "Unknown argument $a[1], choose one of " . join(" ", sort keys %sets)
if(!defined($sets{$a[1]})); if(!defined($sets{$a[1]}));
my $command = $a[1]; my $command = $a[1];
my $value = $a[2]; #my $value = $a[2];
COMMAND_HANDLER: { return Start($hash) if $command eq 'connect';
$command eq "connect" and do { return Stop($hash) if $command eq 'disconnect';
Start($hash);
last; return "unknown command $command" if $command ne 'publish';
}; my ($qos, $retain,$topic, $value) = parsePublishCmd(@a);
$command eq "disconnect" and do { return 'missing parameters. usage: publish [qos:?] [retain:?] topic value1 [value2]...' if !$topic;
Stop($hash); return "wrong parameter. topic may not be '#' or '+'" if ($topic eq '#' or $topic eq '+');
last; $qos = MQTT_QOS_AT_MOST_ONCE if !defined $qos;
}; send_publish($hash, topic => $topic, message => $value, qos => $qos, retain => $retain);
$command eq "publish" and do { return;
shift(@a);
shift(@a);
#if(scalar(@a)<2) {return "not enough parameters. usage: publish [qos [retain]] topic value";}
#my $qos=0;
#my $retain=0;
#if(looks_like_number ($a[0])) {
# $qos = int($a[0]);
# $qos = 0 if $qos>1;
# shift(@a);
# if(looks_like_number ($a[0])) {
# $retain = int($a[0]);
# $retain = 0 if $retain>2;
# shift(@a);
# }
#}
#if(scalar(@a)<2) {return "missing parameters. usage: publish [qos [retain]] topic value";}
#my $topic = shift(@a);
#my $value = join (" ", @a);
my ($qos, $retain,$topic, $value) = parsePublishCmd(@a);
return "missing parameters. usage: publish [qos:?] [retain:?] topic value1 [value2]..." if(!$topic);
return "wrong parameter. topic may nob be '#' or '+'" if ($topic eq '#' or $topic eq '+');
$qos = MQTT_QOS_AT_MOST_ONCE unless defined($qos);
my $msgid = send_publish($hash, topic => $topic, message => $value, qos => $qos, retain => $retain);
last;
}
};
} }
sub parseParams($;$$$$) { sub parseParams($;$$$$) {
@ -408,7 +386,7 @@ sub Attr($$$$) {
} else { } else {
$hash->{timeout} = 60; $hash->{timeout} = 60;
} }
if ($main::init_done) { if ($init_done) {
$hash->{ping_received}=1; $hash->{ping_received}=1;
Timer($hash); Timer($hash);
}; };
@ -417,7 +395,7 @@ sub Attr($$$$) {
$attribute eq "last-will" and do { $attribute eq "last-will" and do {
if($hash->{STATE} ne "disconnected") { if($hash->{STATE} ne "disconnected") {
Stop($hash); Stop($hash);
InternalTimer(gettimeofday()+1, "MQTT::Start", $hash, 0); InternalTimer(gettimeofday()+1, \&Start, $hash, 0);
} }
last; last;
}; };
@ -445,7 +423,7 @@ sub Start($) {
} }
DevIo_CloseDev($hash); DevIo_CloseDev($hash);
return DevIo_OpenDev($hash, 0, "MQTT::Init"); return DevIo_OpenDev($hash, 0, \&Init);
} }
sub Stop($) { sub Stop($) {
@ -461,20 +439,24 @@ sub Stop($) {
send_disconnect($hash); send_disconnect($hash);
DevIo_CloseDev($hash); DevIo_CloseDev($hash);
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
readingsSingleUpdate($hash,"connection","disconnected",1); readingsBeginUpdate($hash);
readingsSingleUpdate($hash,"state","disconnected",1); readingsBulkUpdate($hash,'connection','disconnected');
readingsBulkUpdate($hash,'state','disconnected');
readingsEndUpdate($hash,1);
} }
sub Ready($) { sub Ready($) {
my $hash = shift; my $hash = shift;
return DevIo_OpenDev($hash, 1, "MQTT::Init") if($hash->{STATE} eq "disconnected"); return if DevIo_IsOpen($hash) || IsDisabled($hash->{NAME});
return DevIo_OpenDev($hash, 1, \&Init, sub(){}) if DevIo_getState($hash) eq 'disconnected';
return;
} }
sub Rename() { sub Rename() {
my ($new,$old) = @_; my ($new,$old) = @_;
setKeyValue($new."_user",getKeyValue($old."_user")); setKeyValue($new."_user",getKeyValue($old."_user"));
setKeyValue($new."_pass",getKeyValue($old."_pass")); setKeyValue($new."_pass",getKeyValue($old."_pass"));
setKeyValue($old."_user",undef); setKeyValue($old."_user",undef);
setKeyValue($old."_pass",undef); setKeyValue($old."_pass",undef);
return undef; return undef;
@ -741,7 +723,7 @@ sub client_subscribe_topic($$;$$) {
$client->{subscribeQos}->{$topic}=$qos; $client->{subscribeQos}->{$topic}=$qos;
my $expr = topic_to_regexp($topic); my $expr = topic_to_regexp($topic);
push @{$client->{subscribeExpr}},$expr unless grep {$_ eq $expr} @{$client->{subscribeExpr}}; push @{$client->{subscribeExpr}},$expr unless grep {$_ eq $expr} @{$client->{subscribeExpr}};
if ($main::init_done) { if ($init_done) {
if (my $mqtt = $client->{IODev}) {; if (my $mqtt = $client->{IODev}) {;
$qos = $client->{".qos"}->{"*"} unless defined $qos; # MQTT_QOS_AT_MOST_ONCE $qos = $client->{".qos"}->{"*"} unless defined $qos; # MQTT_QOS_AT_MOST_ONCE
$retain = 0 unless defined $retain; # not supported yet $retain = 0 unless defined $retain; # not supported yet
@ -760,7 +742,7 @@ sub client_unsubscribe_topic($$) {
delete $client->{subscribeQos}->{$topic}; delete $client->{subscribeQos}->{$topic};
my $expr = topic_to_regexp($topic); my $expr = topic_to_regexp($topic);
$client->{subscribeExpr} = [grep { $_ ne $expr} @{$client->{subscribeExpr}}]; $client->{subscribeExpr} = [grep { $_ ne $expr} @{$client->{subscribeExpr}}];
if ($main::init_done) { if ($init_done) {
if (my $mqtt = $client->{IODev}) {; if (my $mqtt = $client->{IODev}) {;
my $msgid = send_unsubscribe($mqtt, my $msgid = send_unsubscribe($mqtt,
topics => [$topic], topics => [$topic],
@ -783,12 +765,9 @@ sub Client_Define($$) {
$client->{subscribeExpr} = []; $client->{subscribeExpr} = [];
AssignIoPort($client); AssignIoPort($client);
if ($main::init_done) { return client_start($client) if $init_done;
return client_start($client); return;
} else { }
return undef;
}
};
sub Client_Undefine($) { sub Client_Undefine($) {
client_stop(shift); client_stop(shift);
@ -895,7 +874,7 @@ sub client_attr($$$$$) {
last; last;
}; };
$attribute eq "IODev" and do { $attribute eq "IODev" and do {
if ($main::init_done) { if ($init_done) {
if ($command eq "set") { if ($command eq "set") {
client_stop($client); client_stop($client);
$main::attr{$name}{IODev} = $value; $main::attr{$name}{IODev} = $value;
@ -971,48 +950,55 @@ sub client_stop($) {
}; };
1; 1;
__END__
=pod =pod
=item [device] =item [device]
=item summary connects fhem to MQTT =item summary connects fhem to MQTT
=begin html =begin html
<a name="MQTT"></a> <a id="MQTT"></a>
<h3>MQTT</h3> <h3>MQTT</h3>
<ul> <ul>
<p>connects fhem to <a href="http://mqtt.org">mqtt</a>.</p> <p>connects fhem to <a href="http://mqtt.org">mqtt</a>.</p>
<p>A single MQTT device can serve multiple <a href="#MQTT_DEVICE">MQTT_DEVICE</a> and <a href="#MQTT_BRIDGE">MQTT_BRIDGE</a> clients.<br/> <p>A single MQTT device can serve multiple <a href="#MQTT_DEVICE">MQTT_DEVICE</a>, <a href="#MQTT_GENERIC_BRIDGE">MQTT_GENERIC_BRIDGE</a> clients and (<b>outdated</b>) <a href="#MQTT_BRIDGE">MQTT_BRIDGE</a> clients.<br/>
Each <a href="#MQTT_DEVICE">MQTT_DEVICE</a> acts as a bridge in between an fhem-device and mqtt.<br/> Each <a href="#MQTT_DEVICE">MQTT_DEVICE</a> acts as a bridge in between an fhem-device and mqtt.<br/>
Note: this module is based on <a href="https://metacpan.org/pod/distribution/Net-MQTT/lib/Net/MQTT.pod">Net::MQTT</a> which needs to be installed from CPAN first.</p> Note: this module is based on <a href="https://metacpan.org/pod/distribution/Net-MQTT/lib/Net/MQTT.pod">Net::MQTT</a> which needs to be installed from CPAN first.</p>
<a name="MQTTdefine"></a> <a id="MQTT-define"></a>
<p><b>Define</b></p> <p><b>Define</b></p>
<ul> <ul>
<p><code>define &lt;name&gt; MQTT &lt;ip:port&gt; [&lt;username&gt;] [&lt;password&gt;]</code></p> <p><code>define &lt;name&gt; MQTT &lt;ip:port&gt; [&lt;username&gt;] [&lt;password&gt;]</code></p>
<p>Specifies the MQTT device.</p> <p>Specifies the MQTT device.</p>
</ul> </ul>
<a name="MQTTset"></a> <a id="MQTT-set"></a>
<p><b>Set</b></p> <p><b>Set</b></p>
<ul> <ul>
<a id="MQTT-set-connect"></a>
<li> <li>
<p><code>set &lt;name&gt; connect</code><br/> <p><code>set &lt;name&gt; connect</code><br/>
(re-)connects the MQTT-device to the mqtt-broker</p> (re-)connects the MQTT-device to the mqtt-broker</p>
</li> </li>
<a id="MQTT-set-disconnect"></a>
<li> <li>
<p><code>set &lt;name&gt; disconnect</code><br/> <p><code>set &lt;name&gt; disconnect</code><br/>
disconnects the MQTT-device from the mqtt-broker</p> disconnects the MQTT-device from the mqtt-broker</p>
</li> </li>
<a id="MQTT-set-publish"></a>
<li> <li>
<p><code>set &lt;name&gt; publish [qos:?] [retain:?] &lt;topic&gt; &lt;message&gt;</code><br/> <p><code>set &lt;name&gt; publish [qos:?] [retain:?] &lt;topic&gt; &lt;message&gt;</code><br/>
sends message to the specified topic</p> sends message to the specified topic</p>
</li> </li>
</ul> </ul>
<a name="MQTTattr"></a> <a id="MQTT-attr"></a>
<p><b>Attributes</b></p> <p><b>Attributes</b></p>
<ul> <ul>
<a id="MQTT-attr-keep-alive"></a>
<li> <li>
<p>keep-alive<br/> <p>keep-alive<br/>
sets the keep-alive time (in seconds).</p> sets the keep-alive time (in seconds).</p>
</li> </li>
<a id="MQTT-attr-keep-last-will"></a>
<li> <li>
<p><code>attr &lt;name&gt; last-will [qos:?] [retain:?] &lt;topic&gt; &lt;message&gt;</code><br/> <p><code>attr &lt;name&gt; last-will [qos:?] [retain:?] &lt;topic&gt; &lt;message&gt;</code><br/>
Support for MQTT feature "last will" Support for MQTT feature "last will"
@ -1021,6 +1007,7 @@ sub client_stop($) {
<code>attr mqtt last-will /fhem/status crashed</code> <code>attr mqtt last-will /fhem/status crashed</code>
</p> </p>
</li> </li>
<a id="MQTT-attr-keep-client-id"></a>
<li> <li>
<p><code>attr &lt;name&gt; client-id client id</code><br/> <p><code>attr &lt;name&gt; client-id client id</code><br/>
redefines client id redefines client id
@ -1029,6 +1016,7 @@ sub client_stop($) {
<code>attr mqtt client-id fhem1234567</code> <code>attr mqtt client-id fhem1234567</code>
</p> </p>
</li> </li>
<a id="MQTT-attr-keep-last-will data-pattern="on-.*connect""></a>
<li> <li>
<p>on-connect, on-disconnect<br/> <p>on-connect, on-disconnect<br/>
<code>attr &lt;name&gt; on-connect {Perl-expression} &lt;topic&gt; &lt;message&gt;</code><br/> <code>attr &lt;name&gt; on-connect {Perl-expression} &lt;topic&gt; &lt;message&gt;</code><br/>
@ -1041,6 +1029,7 @@ sub client_stop($) {
<code>attr mqtt on-connect {Log3("abc",1,"on-connect")} /fhem/status connected</code> <code>attr mqtt on-connect {Log3("abc",1,"on-connect")} /fhem/status connected</code>
</p> </p>
</li> </li>
<a id="MQTT-attr-on-timeout"></a>
<li> <li>
<p>on-timeout<br/> <p>on-timeout<br/>
<code>attr &lt;name&gt; on-timeout {Perl-expression}</code> <code>attr &lt;name&gt; on-timeout {Perl-expression}</code>