2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-08 13:24:56 +00:00

10_MYSENSORS_DEVICE.pm: adding alive check

git-svn-id: https://svn.fhem.de/fhem/trunk@16597 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
Hauswart 2018-04-13 08:46:18 +00:00
parent cfb5a403b3
commit 62058e6f51
3 changed files with 158 additions and 104 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
- change: 10_MYSENSORS_DEVICE: implemented alive check thanks to Beta-User
- bugfix: 93_DbRep: 7.15.2, Internal MODEL is set, minor fixes
- change: 93_DbRep: 7.15.1, sqlCmd accept widget textField-long,
Internal MODEL is set

View File

@ -3,6 +3,7 @@
# fhem bridge to MySensors (see http://mysensors.org)
#
# Copyright (C) 2014 Norbert Truchsess
# Copyright (C) 2018 Hauswart@forum.fhem.de
#
# This file is part of fhem.
#
@ -19,7 +20,7 @@
# You should have received a copy of the GNU General Public License
# along with fhem. If not, see <http://www.gnu.org/licenses/>.
#
# $Id$
# $Id$id$
#
##############################################
@ -49,6 +50,8 @@ sub MYSENSORS_DEVICE_Initialize($) {
"mapReadingType_.+ " .
"mapReading_.+ " .
"requestAck:1 " .
"timeoutAck " .
"timeoutAlive " .
"IODev " .
"showtime:0,1 " .
$main::readingFnAttributes;
@ -79,6 +82,8 @@ BEGIN {
Log3
SetExtensions
ReadingsVal
InternalTimer
RemoveInternalTimer
))
};
@ -211,7 +216,9 @@ sub Define($$) {
sub UnDefine($) {
my ($hash) = @_;
my $name = $hash->{NAME};
RemoveInternalTimer("timeoutAck:$name");
RemoveInternalTimer("timeoutAlive:$name");
return undef;
}
@ -248,8 +255,8 @@ sub Set($@) {
subType => $type,
payload => $mappedValue,
);
readingsSingleUpdate($hash,$setcommand->{var},$setcommand->{val},1) unless ($hash->{ack} or $hash->{IODev}->{ack});
};
readingsSingleUpdate($hash,$setcommand->{var},$setcommand->{val},1) unless ($hash->{ack} or $hash->{IODev}->{ack});
};
return "$command not defined: ".GP_Catch($@) if $@;
last;
};
@ -382,11 +389,29 @@ sub Attr($$$$) {
}
last;
};
$attribute eq "timeoutAck" and do {
if ($command eq "set") {
$hash->{timeoutAck} = $value;
} else {
$hash->{timeoutAck} = 0;
}
last;
};
$attribute eq "timeoutAlive" and do {
if ($command eq "set" and $value) {
$hash->{timeoutAlive} = $value;
refreshInternalMySTimer($hash,"Alive");
} else {
$hash->{timeoutAlive} = 0;
}
last;
};
}
}
sub onGatewayStarted($) {
my ($hash) = @_;
my ($hash) = @_;
refreshInternalMySTimer($hash,"Alive") if ($hash->{timeoutAlive});
}
sub onPresentationMessage($$) {
@ -447,31 +472,33 @@ sub onPresentationMessage($$) {
}
sub onSetMessage($$) {
my ($hash,$msg) = @_;
if (defined $msg->{payload}) {
eval {
my ($reading,$value) = rawToMappedReading($hash,$msg->{subType},$msg->{childId},$msg->{payload});
readingsSingleUpdate($hash, $reading, $value, 1);
};
Log3 ($hash->{NAME}, 4, "MYSENSORS_DEVICE $hash->{NAME}: ignoring C_SET-message ".GP_Catch($@)) if $@;
} else {
Log3 ($hash->{NAME}, 5, "MYSENSORS_DEVICE $hash->{NAME}: ignoring C_SET-message without payload");
}
my ($hash,$msg) = @_;
my $name = $hash->{NAME};
if (defined $msg->{payload}) {
eval {
my ($reading,$value) = rawToMappedReading($hash,$msg->{subType},$msg->{childId},$msg->{payload});
readingsSingleUpdate($hash, $reading, $value, 1);
};
Log3 ($hash->{NAME}, 4, "MYSENSORS_DEVICE $hash->{NAME}: ignoring C_SET-message ".GP_Catch($@)) if $@;
refreshInternalMySTimer($hash,"Alive") if $hash->{timeoutAlive};
} else {
Log3 ($hash->{NAME}, 5, "MYSENSORS_DEVICE $hash->{NAME}: ignoring C_SET-message without payload");
};
}
sub onRequestMessage($$) {
my ($hash,$msg) = @_;
eval {
my ($readingname,$val) = rawToMappedReading($hash, $msg->{subType}, $msg->{childId}, $msg->{payload});
sendClientMessage($hash,
childId => $msg->{childId},
cmd => C_SET,
subType => $msg->{subType},
payload => ReadingsVal($hash->{NAME},$readingname,$val)
);
};
Log3 ($hash->{NAME}, 4, "MYSENSORS_DEVICE $hash->{NAME}: ignoring C_REQ-message ".GP_Catch($@)) if $@;
my ($hash,$msg) = @_;
eval {
my ($readingname,$val) = rawToMappedReading($hash, $msg->{subType}, $msg->{childId}, $msg->{payload});
sendClientMessage($hash,
childId => $msg->{childId},
cmd => C_SET,
subType => $msg->{subType},
payload => ReadingsVal($hash->{NAME},$readingname,$val)
);
};
refreshInternalMySTimer($hash,"Alive") if $hash->{timeoutAlive};
Log3 ($hash->{NAME}, 4, "MYSENSORS_DEVICE $hash->{NAME}: ignoring C_REQ-message ".GP_Catch($@)) if $@;
}
sub onInternalMessage($$) {
@ -534,7 +561,7 @@ sub onInternalMessage($$) {
};
$type == I_CHILDREN and do {
readingsSingleUpdate($hash, "state", "routingtable cleared", 1);
Log3 ($name, 3, "MYSENSORS_DEVICE $name: routingtable cleared");
Log3 ($name, 3, "MYSENSORS_DEVICE $name: routingtable cleared");
last;
};
$type == I_SKETCH_NAME and do {
@ -571,10 +598,11 @@ sub onInternalMessage($$) {
}
sub sendClientMessage($%) {
my ($hash,%msg) = @_;
$msg{radioId} = $hash->{radioId};
$msg{ack} = $hash->{ack} unless defined $msg{ack};
sendMessage($hash->{IODev},%msg);
my ($hash,%msg) = @_;
$msg{radioId} = $hash->{radioId};
$msg{ack} = $hash->{ack} unless defined $msg{ack};
sendMessage($hash->{IODev},%msg);
refreshInternalMySTimer($hash,"Ack") if (($hash->{ack} or $hash->{IODev}->{ack}) and $hash->{timeoutAck}) ;
}
sub rawToMappedReading($$$$) {
@ -608,6 +636,47 @@ sub mappedReadingToRaw($$$) {
die "no mapping for reading $reading";
}
sub refreshInternalMySTimer($$) {
my ($hash,$calltype) = @_;
my $name = $hash->{NAME};
Log3 $name, 5, "$name: refreshInternalMySTimer called ($calltype)";
if ($calltype eq "Alive") {
RemoveInternalTimer("timeoutAlive:$name");
my $nextTrigger = main::gettimeofday() + $hash->{timeoutAlive};
InternalTimer($nextTrigger, "MYSENSORS::DEVICE::timeoutMySTimer", "timeoutAlive:$name", 0);
if ($hash->{STATE} ne "NACK" or $hash->{STATE} eq "NACK" and @{$hash->{IODev}->{messagesForRadioId}->{$hash->{radioId}}->{messages}} == 0) {
my $do_trigger = $hash->{STATE} ne "alive" ? 1 : 0;
readingsSingleUpdate($hash,"state","alive",$do_trigger);
}
} elsif ($calltype eq "Ack") {
RemoveInternalTimer("timeoutAck:$name");
my $nextTrigger = main::gettimeofday() + $hash->{timeoutAck};
InternalTimer($nextTrigger, "MYSENSORS::DEVICE::timeoutMySTimer", "timeoutAck:$name", 0);
Log3 $name, 4, "$name: Ack timeout timer set at $nextTrigger";
}
}
sub timeoutMySTimer($) {
my ($calltype, $name) = split(':', $_[0]);
my $hash = $main::defs{$name};
Log3 $name, 5, "$name: timeoutMySTimer called ($calltype)";
if ($calltype eq "timeoutAlive") {
readingsSingleUpdate($hash,"state","dead",1) unless ($hash->{STATE} eq "NACK");
} elsif ($calltype eq "timeoutAck") {
#readingsSingleUpdate($hash,"state","timeoutAck passed",1);# if ($hash->{STATE} eq "NACK");
if ($hash->{IODev}->{outstandingAck} == 0) {
Log3 $name, 4, "$name: timeoutMySTimer called ($calltype), no outstanding Acks at all";
readingsSingleUpdate($hash,"state","alive",1) if ($hash->{STATE} eq "NACK");
} elsif (@{$hash->{IODev}->{messagesForRadioId}->{$hash->{radioId}}->{messages}}) {
Log3 $name, 4, "$name: timeoutMySTimer called ($calltype), outstanding: $hash->{IODev}->{messagesForRadioId}->{$hash->{radioId}}->{messages}";
readingsSingleUpdate($hash,"state","NACK",1) ;
} else {
Log3 $name, 4, "$name: timeoutMySTimer called ($calltype), no outstanding Acks for Node";
readingsSingleUpdate($hash,"state","alive",1) if ($hash->{STATE} eq "NACK");
}
}
}
1;
=pod
@ -620,71 +689,55 @@ sub mappedReadingToRaw($$$) {
<a name="MYSENSORS_DEVICE"></a>
<h3>MYSENSORS_DEVICE</h3>
<ul>
<p>represents a mysensors sensor attached to a mysensor-node</p>
<p>requires a <a href="#MYSENSOR">MYSENSOR</a>-device as IODev</p>
<a name="MYSENSORS_DEVICEdefine"></a>
<p><b>Define</b></p>
<ul>
<p><code>define &lt;name&gt; MYSENSORS_DEVICE &lt;Sensor-type&gt; &lt;node-id&gt;</code><br/>
Specifies the MYSENSOR_DEVICE device.</p>
</ul>
<a name="MYSENSORS_DEVICEset"></a>
<p><b>Set</b></p>
<ul>
<li>
<p><code>set &lt;name&gt; clear</code><br/>
clears routing-table of a repeater-node</p>
</li>
<li>
<p><code>set &lt;name&gt; time</code><br/>
sets time for nodes (that support it)</p>
</li>
<li>
<p><code>set &lt;name&gt; reboot</code><br/>
reboots a node (requires a bootloader that supports it).<br/>
Attention: Nodes that run the standard arduino-bootloader will enter a bootloop!<br/>
Dis- and reconnect the nodes power to restart in this case.</p>
</li>
</ul>
<a name="MYSENSORS_DEVICEattr"></a>
<p><b>Attributes</b></p>
<ul>
<li>
<p><code>attr &lt;name&gt; config [&lt;M|I&gt;]</code><br/>
configures metric (M) or inch (I). Defaults to 'M'</p>
</li>
<li>
<p><code>attr &lt;name&gt; setCommands [&lt;command:reading:value&gt;]*</code><br/>
configures one or more commands that can be executed by set.<br/>
e.g.: <code>attr &lt;name&gt; setCommands on:switch_1:on off:switch_1:off</code><br/>
if list of commands contains both 'on' and 'off' <a href="#setExtensions">set extensions</a> are supported</p>
</li>
<li>
<p><code>attr &lt;name&gt; setReading_&lt;reading&gt; [&lt;value&gt;]*</code><br/>
configures a reading that can be modified by set-command<br/>
e.g.: <code>attr &lt;name&gt; setReading_switch_1 on,off</code></p>
</li>
<li>
<p><code>attr &lt;name&gt; mapReading_&lt;reading&gt; &lt;childId&gt; &lt;readingtype&gt; [&lt;value&gt;:&lt;mappedvalue&gt;]*</code><br/>
configures the reading-name for a given childId and sensortype<br/>
E.g.: <code>attr xxx mapReading_aussentemperatur 123 temperature</code></p>
</li>
<li>
<p><code>att &lt;name&gt; requestAck</code><br/>
request acknowledge from nodes.<br/>
if set the Readings of nodes are updated not before requested acknowledge is received<br/>
if not set the Readings of nodes are updated immediatly (not awaiting the acknowledge).<br/>
May also be configured on the gateway for all nodes at once</p>
</li>
<li>
<p><code>attr &lt;name&gt; mapReadingType_&lt;reading&gt; &lt;new reading name&gt; [&lt;value&gt;:&lt;mappedvalue&gt;]*</code><br/>
configures reading type names that should be used instead of technical names<br/>
E.g.: <code>attr xxx mapReadingType_LIGHT switch 0:on 1:off</code>
to be used for mysensor Variabletypes that have no predefined defaults (yet)</p>
</li>
</ul>
<p>represents a mysensors sensor attached to a mysensor-node</p>
<p>requires a <a href="#MYSENSOR">MYSENSOR</a>-device as IODev</p>
<a name="MYSENSORS_DEVICE define"></a>
<p><b>Define</b></p>
<ul>
<p><code>define &lt;name&gt; MYSENSORS_DEVICE &lt;Sensor-type&gt; &lt;node-id&gt;</code><br/>Specifies the MYSENSOR_DEVICE device.</p>
</ul>
<a name="MYSENSORS_DEVICEset"></a>
<p><b>Set</b></p>
<ul>
<li>
<p><code>set &lt;name&gt; clear</code><br/>clears routing-table of a repeater-node</p>
</li>
<li>
<p><code>set &lt;name&gt; time</code><br/>sets time for nodes (that support it)</p>
</li>
<li>
<p><code>set &lt;name&gt; reboot</code><br/>reboots a node (requires a bootloader that supports it).<br/>Attention: Nodes that run the standard arduino-bootloader will enter a bootloop!<br/>Dis- and reconnect the nodes power to restart in this case.</p>
</li>
</ul>
<a name="MYSENSORS_DEVICEattr"></a>
<p><b>Attributes</b></p>
<ul>
<li>
<p><code>attr &lt;name&gt; config [&lt;M|I&gt;]</code><br/>configures metric (M) or inch (I). Defaults to 'M'</p>
</li>
<li>
<p><code>attr &lt;name&gt; setCommands [&lt;command:reading:value&gt;]*</code><br/>configures one or more commands that can be executed by set.<br/>e.g.: <code>attr &lt;name&gt; setCommands on:switch_1:on off:switch_1:off</code><br/>if list of commands contains both 'on' and 'off' <a href="#setExtensions">set extensions</a> are supported</p>
</li>
<li>
<p><code>attr &lt;name&gt; setReading_&lt;reading&gt; [&lt;value&gt;]*</code><br/>configures a reading that can be modified by set-command<br/>e.g.: <code>attr &lt;name&gt; setReading_switch_1 on,off</code></p>
</li>
<li>
<p><code>attr &lt;name&gt; mapReading_&lt;reading&gt; &lt;childId&gt; &lt;readingtype&gt; [&lt;value&gt;:&lt;mappedvalue&gt;]*</code><br/>configures the reading-name for a given childId and sensortype<br/>e.g.: <code>attr xxx mapReading_aussentemperatur 123 temperature</code></p>
</li>
<li>
<p><code>att &lt;name&gt; requestAck</code><br/>request acknowledge from nodes.<br/>if set the Readings of nodes are updated not before requested acknowledge is received<br/>if not set the Readings of nodes are updated immediatly (not awaiting the acknowledge).<br/>May also be configured on the gateway for all nodes at once</p>
</li>
<li>
<p><code>attr &lt;name&gt; mapReadingType_&lt;reading&gt; &lt;new reading name&gt; [&lt;value&gt;:&lt;mappedvalue&gt;]*</code><br/>configures reading type names that should be used instead of technical names<br/>e.g.: <code>attr xxx mapReadingType_LIGHT switch 0:on 1:off</code>to be used for mysensor Variabletypes that have no predefined defaults (yet)</p>
</li>
<li>
<p><code>attr &lt;name&gt; timeoutAck &lt;time in seconds&gt;*</code><br/>configures timeout to set device state to NACK in case not all requested acks are received</p>
</li>
<li>
<p><code>attr &lt;name&gt; timeoutAlive &lt;time in seconds&gt;*</code><br/>configures timeout to set device state to alive or dead. If messages from node are received within timout spec, state will be alive, otherwise dead. If state is NACK (in case timeoutAck is also set), state will only be changed to alive, if there are no outstanding messages to be sent.</p>
</li>
</ul>
</ul>
=end html
=cut

View File

@ -36,17 +36,17 @@ sub createMsg(%) {
}
sub dumpMsg($) {
my $msgRef = shift;
my $cmd = defined $msgRef->{'cmd'} ? commandToStr($msgRef->{'cmd'}) : "''";
my $st = (defined $msgRef->{'cmd'} and defined $msgRef->{'subType'}) ? subTypeToStr( $msgRef->{'cmd'}, $msgRef->{'subType'} ) : "''";
return sprintf("Rx: fr=%03d ci=%03d c=%03d(%-14s) st=%03d(%-16s) ack=%d %s\n", $msgRef->{'radioId'} // -1, $msgRef->{'childId'} // -1, $msgRef->{'cmd'} // -1, $cmd, $msgRef->{'subType'} // -1, $st, $msgRef->{'ack'} // -1, "'".($msgRef->{'payload'} // "")."'");
my $msgRef = shift;
my $cmd = defined $msgRef->{'cmd'} ? commandToStr($msgRef->{'cmd'}) : "''";
my $st = (defined $msgRef->{'cmd'} and defined $msgRef->{'subType'}) ? subTypeToStr( $msgRef->{'cmd'}, $msgRef->{'subType'} ) : "''";
return sprintf("Rx: fr=%03d ci=%03d c=%03d(%-14s) st=%03d(%-16s) ack=%d %s\n", $msgRef->{'radioId'} // -1, $msgRef->{'childId'} // -1, $msgRef->{'cmd'} // -1, $cmd, $msgRef->{'subType'} // -1, $st, $msgRef->{'ack'} // -1, "'".($msgRef->{'payload'} // "")."'");
}
sub gettime {
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year += 1900;
$mon++;
return sprintf("%04d%02d%02d-%02d:%02d:%02d", $year, $mon, $mday, $hour, $min, $sec);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year += 1900;
$mon++;
return sprintf("%04d%02d%02d-%02d:%02d:%02d", $year, $mon, $mday, $hour, $min, $sec);
}
1;