From 319591260e251eea192f84c9c35dec972ae9bba2 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;