From b53754839bc3c391086f90714f256ca9329f2d8a Mon Sep 17 00:00:00 2001
From: Hauswart
Date: Thu, 6 Sep 2018 08:13:08 +0000
Subject: [PATCH] 00_MYSENSORS.pm: FOTA, gateway reconnect
git-svn-id: https://svn.fhem.de/fhem/trunk@17286 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
fhem/CHANGED | 1 +
fhem/FHEM/00_MYSENSORS.pm | 137 ++++++++++++++++++--
fhem/FHEM/lib/Device/MySensors/Constants.pm | 4 +
3 files changed, 131 insertions(+), 11 deletions(-)
diff --git a/fhem/CHANGED b/fhem/CHANGED
index 86662702b..f86307017 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -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.
+ - feature: 00_MYSENSORS: FOTA (thanks Beta-User), gateway fix (thanks Sidey)
- new: 12_HProtocolGateway / 12_HProtocolTank
- bugfix: 72_XiaomiDevice: remove unused battery readings for new fans
- feature: 49_SSCam: activate/deactivate cam internal PIR-sensor
diff --git a/fhem/FHEM/00_MYSENSORS.pm b/fhem/FHEM/00_MYSENSORS.pm
index 5a07f40bc..3ae16ca7a 100644
--- a/fhem/FHEM/00_MYSENSORS.pm
+++ b/fhem/FHEM/00_MYSENSORS.pm
@@ -60,14 +60,19 @@ sub MYSENSORS_Initialize($) {
"requestAck:1 ".
"first-sensorid ".
"last-sensorid ".
- "stateFormat";
+ "stateFormat ".
+ "OTA_firmwareConfig";
}
package MYSENSORS;
use Exporter ('import');
@EXPORT = ();
-@EXPORT_OK = qw(sendMessage);
+@EXPORT_OK = qw(
+ sendMessage
+ getFirmwareTypes
+ getLatestFirmware
+ );
%EXPORT_TAGS = (all => [@EXPORT_OK]);
use strict;
@@ -92,6 +97,7 @@ BEGIN {GP_Import(qw(
InternalTimer
AttrVal
Log3
+ FileRead
))};
my %sensorAttr = (
@@ -165,6 +171,9 @@ sub Attr($$$$) {
}
last;
};
+ $attribute eq "OTA_firmwareConfig" and do {
+ last;
+ };
}
}
@@ -195,11 +204,11 @@ sub Stop($) {
sub Ready($) {
my $hash = shift;
return DevIo_OpenDev($hash, 1, "MYSENSORS::Init") if($hash->{STATE} eq "disconnected");
- if(defined($hash->{USBDev})) {
- my $po = $hash->{USBDev};
- my ( $BlockingFlags, $InBytes, $OutBytes, $ErrorFlags ) = $po->status;
- return ( $InBytes > 0 );
- }
+ if(defined($hash->{USBDev})) {
+ my $po = $hash->{USBDev};
+ my ( $BlockingFlags, $InBytes, $OutBytes, $ErrorFlags ) = $po->status;
+ return ( $InBytes > 0 );
+ }
}
sub Init($) {
@@ -224,6 +233,22 @@ sub Init($) {
return undef;
}
+
+# GetConnectStatus
+sub GetConnectStatus($){
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+ Log3 $name, 4, "MySensors: GetConnectStatus called ...";
+
+ #query heartbeat from gateway
+ sendMessage($hash, radioId => 0, childId => 0, cmd => C_INTERNAL, ack => 0, subType => I_HEARTBEAT_REQUEST, payload => '');
+
+ # neuen Timer starten in einem konfigurierten Interval.
+ InternalTimer(gettimeofday()+300, "MYSENSORS::GetConnectStatus", $hash);# Restart check in 5 mins again
+ InternalTimer(gettimeofday()+5, "MYSENSORS::Start", $hash); #Start timer for reset if after 5 seconds RESPONSE is not received
+
+}
+
sub Timer($) {
my $hash = shift;
my $now = time;
@@ -250,7 +275,7 @@ sub Read {
return "" if(!defined($buf));
my $data = $hash->{PARTIAL};
- Log3 ($name, 5, "MYSENSORS/RAW: $data/$buf");
+ Log3 ($name, 4, "MYSENSORS/RAW: $data/$buf");
$data .= $buf;
while ($data =~ m/\n/) {
@@ -258,12 +283,13 @@ sub Read {
($txt,$data) = split("\n", $data, 2);
$txt =~ s/\r//;
if (my $msg = parseMsg($txt)) {
- Log3 ($name,5,"MYSENSORS Read: ".dumpMsg($msg));
-
+ Log3 ($name,4,"MYSENSORS Read: ".dumpMsg($msg));
if ($msg->{ack}) {
onAcknowledge($hash,$msg);
}
-
+ RemoveInternalTimer($hash,"MYSENSORS::GetConnectStatus");
+ InternalTimer(gettimeofday()+300, "MYSENSORS::GetConnectStatus", $hash);# Restart check in 5 mins again
+
my $type = $msg->{cmd};
MESSAGE_TYPE: {
$type == C_PRESENTATION and do {
@@ -354,8 +380,13 @@ sub onInternalMsg($$) {
my $client = shift;
MYSENSORS::DEVICE::onGatewayStarted($client);
});
+ InternalTimer(gettimeofday()+300, "MYSENSORS::GetConnectStatus", $hash);
last;
};
+ $type == I_HEARTBEAT_RESPONSE and do {
+ RemoveInternalTimer($hash,"MYSENSORS::Start"); ## Reset reconnect because timeout was not reached
+ readingsSingleUpdate($hash, "heartbeat", "last", 0);
+ };
$type == I_VERSION and do {
$hash->{version} = $msg->{payload};
last;
@@ -393,6 +424,11 @@ sub onInternalMsg($$) {
sub onStreamMsg($$) {
my ($hash,$msg) = @_;
+ if (my $client = matchClient($hash, $msg)) {
+ MYSENSORS::DEVICE::onStreamMessage($client, $msg);
+ } else {
+ Log3($hash->{NAME},3,"MYSENSORS: ignoring stream-msg from unknown radioId $msg->{radioId}, childId $msg->{childId} for ".datastreamTypeToStr($msg->{subType}));
+ }
};
sub onAcknowledge($$) {
@@ -414,6 +450,62 @@ sub onAcknowledge($$) {
Log3 ($hash->{NAME},4,"MYSENSORS Read: unexpected ack ".dumpMsg($msg)) unless $ack;
}
+sub getFirmwareTypes($) {
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+ my @fwTypes = ();
+ my $filename = AttrVal($name, "OTA_firmwareConfig", undef);
+ if (defined($filename)) {
+ my ($err, @lines) = FileRead({FileName => "./FHEM/firmware/" . $filename,
+ ForceType => "file"});
+ if (defined($err) && $err) {
+ Log3($name, 2, "$name: could not read MySensor firmware configuration file - $err");
+ } else {
+ for (my $i = 0; $i < @lines ; $i++) {
+ chomp(my $row = $lines[$i]);
+ if (index($row, "#") != 0) {
+ my @tokens = split(",", $row);
+ push(@fwTypes, $tokens[0]);
+ }
+ }
+ }
+ }
+ Log3($name, 5, "$name: getFirmwareTypes - list contains: @fwTypes");
+ return @fwTypes;
+}
+
+sub getLatestFirmware($$) {
+ my ($hash, $type) = @_;
+ my $name = $hash->{NAME};
+ my $cfgfilename = AttrVal($name, "OTA_firmwareConfig", undef);
+ my $version = undef;
+ $name = undef;
+ my $filename = undef;
+ if (defined($cfgfilename)) {
+ my ($err, @lines) = FileRead({FileName => "./FHEM/firmware/" . $cfgfilename,
+ ForceType => "file"});
+ if (defined($err) && $err) {
+ Log3($name, 2, "$name: could not read MySensor firmware configuration file - $err");
+ } else {
+ for (my $i = 0; $i < @lines ; $i++) {
+ chomp(my $row = $lines[$i]);
+ if (index($row, "#") != 0) {
+ my @tokens = split(",", $row);
+ if ($tokens[0] eq $type) {
+ if ((not defined $version) || ($tokens[2] > $version)) {
+ $name = $tokens[1];
+ $version = $tokens[2];
+ $filename = $tokens[3];
+ }
+ }
+ }
+ }
+ }
+ }
+ return ($version, $filename, $name);
+}
+
+
sub sendMessage($%) {
my ($hash,%msg) = @_;
$msg{ack} = $hash->{ack} unless defined $msg{ack};
@@ -525,6 +617,29 @@ sub matchClient($$) {
att <name> first-sensorid <<number <h; 255>>
configures the lowest node-id assigned to a mysensor-node on request (defaults to 20)
+
+
+ att <name> OTA_firmwareConfig <filename>
+ specifies a configuration file for the FOTA
+ (firmware over the air - wireless programming of the nodes) configuration. It must be stored
+ in the folder FHEM/firmware. The format of the configuration file is the following (csv):
+ #Type,Name,Version,File,Comments
+ 10,Blink,1,Blink.hex,blinking example
+ The meaning of the columns is the following:
+
+ Type
+ - a numeric value (range 0 .. 65536) - each node will be assigned a firmware type
+ Name
+ - a short name for this type
+ Version
+ - a numeric value (range 0 .. 65536) - the version of the firmware (may be different
+ to the value that is send during the node presentation)
+ File
+ - the filename containing the firmware - must also be stored in the folder FHEM/firmware
+ Comments
+ - a description / comment for the firmware
+
+
diff --git a/fhem/FHEM/lib/Device/MySensors/Constants.pm b/fhem/FHEM/lib/Device/MySensors/Constants.pm
index a5ac0cc63..a467cced0 100644
--- a/fhem/FHEM/lib/Device/MySensors/Constants.pm
+++ b/fhem/FHEM/lib/Device/MySensors/Constants.pm
@@ -259,6 +259,10 @@ sub subTypeToStr($$) {
$subType = (internalMessageTypes)[$subType];
last;
};
+ $cmd == C_STREAM and do {
+ $subType = (datastreamTypes)[$subType];
+ last;
+ };
$subType = "";
}
return $subType;