mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 18:59:33 +00:00
MYSENSORS: initial commit
git-svn-id: https://svn.fhem.de/fhem/trunk@6800 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
27acaa8b74
commit
0d7a412a4e
@ -1,5 +1,7 @@
|
||||
# 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.
|
||||
- added: MYSENSORS: connect to serial or Ethernet MySensors Gateway
|
||||
- added: MYSENSORS_DEVICE: represent a MySensors sensor- or actor node
|
||||
- feature: global ATTR/DELETEATTR/MODIFIED events
|
||||
- feature: 55_GDS.pm - attr disable added
|
||||
- bugfix: SYSMON: prevent endless loop at startup with 'disable' attribute
|
||||
|
451
fhem/FHEM/00_MYSENSORS.pm
Normal file
451
fhem/FHEM/00_MYSENSORS.pm
Normal file
@ -0,0 +1,451 @@
|
||||
##############################################
|
||||
#
|
||||
# fhem driver for MySensors serial or network gateway (see http://mysensors.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
##############################################
|
||||
|
||||
my %sets = (
|
||||
"connect" => [],
|
||||
"disconnect" => [],
|
||||
"inclusion-mode" => [qw(on off)],
|
||||
);
|
||||
|
||||
my %gets = (
|
||||
"version" => ""
|
||||
);
|
||||
|
||||
my @clients = qw(
|
||||
MYSENSORS_DEVICE
|
||||
);
|
||||
|
||||
sub MYSENSORS_Initialize($) {
|
||||
|
||||
my $hash = shift @_;
|
||||
|
||||
require "$main::attr{global}{modpath}/FHEM/DevIo.pm";
|
||||
|
||||
# Provider
|
||||
$hash->{Clients} = join (':',@clients);
|
||||
$hash->{ReadyFn} = "MYSENSORS::Ready";
|
||||
$hash->{ReadFn} = "MYSENSORS::Read";
|
||||
|
||||
# Consumer
|
||||
$hash->{DefFn} = "MYSENSORS::Define";
|
||||
$hash->{UndefFn} = "MYSENSORS::Undef";
|
||||
$hash->{SetFn} = "MYSENSORS::Set";
|
||||
$hash->{AttrFn} = "MYSENSORS::Attr";
|
||||
$hash->{NotifyFn} = "MYSENSORS::Notify";
|
||||
|
||||
$hash->{AttrList} =
|
||||
"autocreate:1 ".
|
||||
"requestAck:1 ".
|
||||
"first-sensorid ".
|
||||
"stateFormat";
|
||||
}
|
||||
|
||||
package MYSENSORS;
|
||||
|
||||
use Exporter ('import');
|
||||
@EXPORT = ();
|
||||
@EXPORT_OK = qw(sendMessage);
|
||||
%EXPORT_TAGS = (all => [@EXPORT_OK]);
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use GPUtils qw(:all);
|
||||
|
||||
use Device::MySensors::Constants qw(:all);
|
||||
use Device::MySensors::Message qw(:all);
|
||||
|
||||
BEGIN {GP_Import(qw(
|
||||
CommandDefine
|
||||
CommandModify
|
||||
CommandAttr
|
||||
gettimeofday
|
||||
readingsSingleUpdate
|
||||
DevIo_OpenDev
|
||||
DevIo_SimpleWrite
|
||||
DevIo_SimpleRead
|
||||
DevIo_CloseDev
|
||||
RemoveInternalTimer
|
||||
InternalTimer
|
||||
AttrVal
|
||||
Log3
|
||||
))};
|
||||
|
||||
my %sensorAttr = (
|
||||
LIGHT => ['setCommands on:V_LIGHT:1 off:V_LIGHT:0' ],
|
||||
ARDUINO_NODE => [ 'config M' ],
|
||||
ARDUINO_REPEATER_NODE => [ 'config M' ],
|
||||
);
|
||||
|
||||
sub Define($$) {
|
||||
my ( $hash, $def ) = @_;
|
||||
|
||||
$hash->{NOTIFYDEV} = "global";
|
||||
|
||||
if ($main::init_done) {
|
||||
return Start($hash);
|
||||
} else {
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
sub Undef($) {
|
||||
Stop(shift);
|
||||
}
|
||||
|
||||
sub Set($@) {
|
||||
my ($hash, @a) = @_;
|
||||
return "Need at least one parameters" if(@a < 2);
|
||||
return "Unknown argument $a[1], choose one of " . join(" ", map {@{$sets{$_}} ? $_.':'.join ',', @{$sets{$_}} : $_} sort keys %sets)
|
||||
if(!defined($sets{$a[1]}));
|
||||
my $command = $a[1];
|
||||
my $value = $a[2];
|
||||
|
||||
COMMAND_HANDLER: {
|
||||
$command eq "connect" and do {
|
||||
Start($hash);
|
||||
last;
|
||||
};
|
||||
$command eq "disconnect" and do {
|
||||
Stop($hash);
|
||||
last;
|
||||
};
|
||||
$command eq "inclusion-mode" and do {
|
||||
sendMessage($hash,radioId => 0, childId => 0, cmd => C_INTERNAL, ack => 0, subType => I_INCLUSION_MODE, payload => $value eq 'on' ? 1 : 0);
|
||||
$hash->{'inclusion-mode'} = $value eq 'on' ? 1 : 0;
|
||||
last;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
sub Attr($$$$) {
|
||||
my ($command,$name,$attribute,$value) = @_;
|
||||
|
||||
my $hash = $main::defs{$name};
|
||||
ATTRIBUTE_HANDLER: {
|
||||
$attribute eq "autocreate" and do {
|
||||
if ($main::init_done) {
|
||||
my $mode = $command eq "set" ? 1 : 0;
|
||||
sendMessage($hash,radioId => $hash->{radioId}, childId => $hash->{childId}, ack => 0, subType => I_INCLUSION_MODE, payload => $mode);
|
||||
$hash->{'inclusion-mode'} = $mode;
|
||||
}
|
||||
last;
|
||||
};
|
||||
$attribute eq "requestAck" and do {
|
||||
if ($command eq "set") {
|
||||
$hash->{ack} = 1;
|
||||
} else {
|
||||
$hash->{ack} = 0;
|
||||
$hash->{messages} = {};
|
||||
$hash->{outstandingAck} = 0;
|
||||
}
|
||||
last;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
sub Notify($$) {
|
||||
my ($hash,$dev) = @_;
|
||||
if( grep(m/^(INITIALIZED|REREADCFG)$/, @{$dev->{CHANGED}}) ) {
|
||||
Start($hash);
|
||||
} elsif( grep(m/^SAVE$/, @{$dev->{CHANGED}}) ) {
|
||||
}
|
||||
}
|
||||
|
||||
sub Start($) {
|
||||
my $hash = shift;
|
||||
my ($dev) = split("[ \t]+", $hash->{DEF});
|
||||
$hash->{DeviceName} = $dev;
|
||||
CommandAttr(undef, "$hash->{NAME} stateFormat connection") unless AttrVal($hash->{NAME},"stateFormat",undef);
|
||||
DevIo_CloseDev($hash);
|
||||
return DevIo_OpenDev($hash, 0, "MYSENSORS::Init");
|
||||
}
|
||||
|
||||
sub Stop($) {
|
||||
my $hash = shift;
|
||||
DevIo_CloseDev($hash);
|
||||
RemoveInternalTimer($hash);
|
||||
readingsSingleUpdate($hash,"connection","disconnected",1);
|
||||
}
|
||||
|
||||
sub Ready($) {
|
||||
my $hash = shift;
|
||||
return DevIo_OpenDev($hash, 1, "MYSENSORS::Init") if($hash->{STATE} eq "disconnected");
|
||||
}
|
||||
|
||||
sub Init($) {
|
||||
my $hash = shift;
|
||||
my $name = $hash->{NAME};
|
||||
$hash->{'inclusion-mode'} = AttrVal($name,"autocreate",0);
|
||||
$hash->{ack} = AttrVal($name,"requestAck",0);
|
||||
$hash->{messages} = {};
|
||||
$hash->{outstandingAck} = 0;
|
||||
readingsSingleUpdate($hash,"connection","connected",1);
|
||||
Timer($hash);
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub Timer($) {
|
||||
my $hash = shift;
|
||||
RemoveInternalTimer($hash);
|
||||
my $now = time;
|
||||
foreach my $msg (keys %{$hash->{messages}}) {
|
||||
if ($now > $hash->{messages}->{$msg}) {
|
||||
Log3 ($hash->{NAME},5,"MYSENSORS outstanding ack, re-send: ".$msg);
|
||||
DevIo_SimpleWrite($hash,"$msg\n", undef);
|
||||
}
|
||||
}
|
||||
InternalTimer(gettimeofday()+1, "MYSENSORS::Timer", $hash, 0);
|
||||
}
|
||||
|
||||
sub Read {
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $buf = DevIo_SimpleRead($hash);
|
||||
return "" if(!defined($buf));
|
||||
|
||||
my $data = $hash->{PARTIAL};
|
||||
Log3 ($name, 5, "MYSENSORS/RAW: $data/$buf");
|
||||
$data .= $buf;
|
||||
|
||||
while ($data =~ m/\n/) {
|
||||
my $txt;
|
||||
($txt,$data) = split("\n", $data, 2);
|
||||
$txt =~ s/\r//;
|
||||
my $msg = parseMsg($txt);
|
||||
Log3 ($name,5,"MYSENSORS Read: ".dumpMsg($msg));
|
||||
|
||||
if ($msg->{ack}) {
|
||||
delete $hash->{messages}->{$txt};
|
||||
$hash->{outstandingAck} = keys %{$hash->{messages}};
|
||||
}
|
||||
|
||||
my $type = $msg->{cmd};
|
||||
MESSAGE_TYPE: {
|
||||
$type == C_PRESENTATION and do {
|
||||
onPresentationMsg($hash,$msg);
|
||||
last;
|
||||
};
|
||||
$type == C_SET and do {
|
||||
onSetMsg($hash,$msg);
|
||||
last;
|
||||
};
|
||||
$type == C_REQ and do {
|
||||
onRequestMsg($hash,$msg);
|
||||
last;
|
||||
};
|
||||
$type == C_INTERNAL and do {
|
||||
onInternalMsg($hash,$msg);
|
||||
last;
|
||||
};
|
||||
$type == C_STREAM and do {
|
||||
onStreamMsg($hash,$msg);
|
||||
last;
|
||||
};
|
||||
}
|
||||
}
|
||||
$hash->{PARTIAL} = $data;
|
||||
return undef;
|
||||
};
|
||||
|
||||
sub onPresentationMsg($$) {
|
||||
my ($hash,$msg) = @_;
|
||||
my $client = matchClient($hash,$msg);
|
||||
my $clientname;
|
||||
my $sensorType = $msg->{subType};
|
||||
unless ($client) {
|
||||
if ($hash->{'inclusion-mode'}) {
|
||||
$clientname = "MYSENSOR_$msg->{radioId}";
|
||||
CommandDefine(undef,"$clientname MYSENSORS_DEVICE $msg->{radioId}");
|
||||
$client = $main::defs{$clientname};
|
||||
return unless ($client);
|
||||
} else {
|
||||
Log3($hash->{NAME},3,"MYSENSORS: ignoring presentation-msg from unknown radioId $msg->{radioId}, childId $msg->{childId}, sensorType $sensorType");
|
||||
return;
|
||||
}
|
||||
}
|
||||
MYSENSORS::DEVICE::onPresentationMessage($client,$msg);
|
||||
};
|
||||
|
||||
sub onSetMsg($$) {
|
||||
my ($hash,$msg) = @_;
|
||||
if (my $client = matchClient($hash,$msg)) {
|
||||
MYSENSORS::DEVICE::onSetMessage($client,$msg);
|
||||
} else {
|
||||
Log3($hash->{NAME},3,"MYSENSORS: ignoring set-msg from unknown radioId $msg->{radioId}, childId $msg->{childId} for ".variableTypeToStr($msg->{subType}));
|
||||
}
|
||||
};
|
||||
|
||||
sub onRequestMsg($$) {
|
||||
my ($hash,$msg) = @_;
|
||||
if (my $client = matchClient($hash,$msg)) {
|
||||
MYSENSORS::DEVICE::onRequestMessage($client,$msg);
|
||||
} else {
|
||||
Log3($hash->{NAME},3,"MYSENSORS: ignoring req-msg from unknown radioId $msg->{radioId}, childId $msg->{childId} for ".variableTypeToStr($msg->{subType}));
|
||||
}
|
||||
};
|
||||
|
||||
sub onInternalMsg($$) {
|
||||
my ($hash,$msg) = @_;
|
||||
my $address = $msg->{radioId};
|
||||
my $type = $msg->{subType};
|
||||
if ($address == 0 or $address == 255) { #msg to or from gateway
|
||||
TYPE: {
|
||||
$type == I_INCLUSION_MODE and do {
|
||||
if (AttrVal($hash->{NAME},"autocreate",0)) { #if autocreate is switched on, keep gateways inclusion-mode active
|
||||
if ($msg->{payload} == 0) {
|
||||
sendMessage($hash,radioId => $msg->{radioId}, childId => $msg->{childId}, ack => 0, subType => I_INCLUSION_MODE, payload => 1);
|
||||
}
|
||||
} else {
|
||||
$hash->{'inclusion-mode'} = $msg->{payload};
|
||||
}
|
||||
last;
|
||||
};
|
||||
$type == I_STARTUP_COMPLETE and do {
|
||||
readingsSingleUpdate($hash,'connection','startup complete',1);
|
||||
GP_ForallClients($hash,sub {
|
||||
my $client = shift;
|
||||
MYSENSORS::DEVICE::onGatewayStarted($client);
|
||||
});
|
||||
last;
|
||||
};
|
||||
$type == I_LOG_MESSAGE and do {
|
||||
Log3($hash->{NAME},5,"MYSENSORS gateway $hash->{NAME}: $msg->{payload}");
|
||||
last;
|
||||
};
|
||||
$type == I_ID_REQUEST and do {
|
||||
if ($hash->{'inclusion-mode'}) {
|
||||
my %nodes = map {$_ => 1} (AttrVal($hash->{NAME},"first-sensorid",20) ... 254);
|
||||
GP_ForallClients($hash,sub {
|
||||
my $client = shift;
|
||||
delete $nodes{$client->{radioId}};
|
||||
});
|
||||
if (keys %nodes) {
|
||||
my $newid = (keys %nodes)[0];
|
||||
sendMessage($hash,radioId => 255, childId => 255, cmd => C_INTERNAL, ack => 0, subType => I_ID_RESPONSE, payload => $newid);
|
||||
Log3($hash->{NAME},4,"MYSENSORS $hash->{NAME} assigned new nodeid $newid");
|
||||
} else {
|
||||
Log3($hash->{NAME},4,"MYSENSORS $hash->{NAME} cannot assign new nodeid");
|
||||
}
|
||||
} else {
|
||||
Log3($hash->{NAME},4,"MYSENSORS: ignoring id-request-msg from unknown radioId $msg->{radioId}");
|
||||
}
|
||||
last;
|
||||
};
|
||||
}
|
||||
} elsif (my $client = matchClient($hash,$msg)) {
|
||||
MYSENSORS::DEVICE::onInternalMessage($client,$msg);
|
||||
} else {
|
||||
Log3($hash->{NAME},3,"MYSENSORS: ignoring internal-msg from unknown radioId $msg->{radioId}, childId $msg->{childId} for ".internalMessageTypeToStr($msg->{subType}));
|
||||
}
|
||||
};
|
||||
|
||||
sub onStreamMsg($$) {
|
||||
my ($hash,$msg) = @_;
|
||||
};
|
||||
|
||||
sub sendMessage($%) {
|
||||
my ($hash,%msg) = @_;
|
||||
$msg{ack} = $hash->{ack} unless $msg{ack};
|
||||
my $txt = createMsg(%msg);
|
||||
Log3 ($hash->{NAME},5,"MYSENSORS send: ".dumpMsg(\%msg));
|
||||
DevIo_SimpleWrite($hash,"$txt\n",undef);
|
||||
if ($msg{ack}) {
|
||||
$hash->{messages}->{$txt} = gettimeofday() + 1,
|
||||
$hash->{outstandingAck} = keys %{$hash->{messages}};
|
||||
}
|
||||
};
|
||||
|
||||
sub matchClient($$) {
|
||||
my ($hash,$msg) = @_;
|
||||
my $radioId = $msg->{radioId};
|
||||
my $found;
|
||||
GP_ForallClients($hash,sub {
|
||||
return if $found;
|
||||
my $client = shift;
|
||||
if ($client->{radioId} == $radioId) {
|
||||
$found = $client;
|
||||
}
|
||||
});
|
||||
return $found;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
=begin html
|
||||
|
||||
<a name="MYSENSORS"></a>
|
||||
<h3>MYSENSORS</h3>
|
||||
<ul>
|
||||
<p>connects fhem to <a href="http://MYSENSORS.org">MYSENSORS</a>.</p>
|
||||
<p>A single MYSENSORS device can serve multiple <a href="#MYSENSORS_DEVICE">MYSENSORS_DEVICE</a> clients.<br/>
|
||||
Each <a href="#MYSENSORS_DEVICE">MYSENSORS_DEVICE</a> represents a mysensors node.<br/>
|
||||
<a name="MYSENSORSdefine"></a>
|
||||
<p><b>Define</b></p>
|
||||
<ul>
|
||||
<p><code>define <name> MYSENSORS <serial device>|<ip:port></code></p>
|
||||
<p>Specifies the MYSENSORS device.</p>
|
||||
</ul>
|
||||
<a name="MYSENSORSset"></a>
|
||||
<p><b>Set</b></p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>set <name> connect</code><br/>
|
||||
(re-)connects the MYSENSORS-device to the MYSENSORS-gateway</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>set <name> disconnect</code><br/>
|
||||
disconnects the MYSENSORS-device from the MYSENSORS-gateway</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>set <name> inclusion-mode on|off</code><br/>
|
||||
turns the gateways inclusion-mode on or off</p>
|
||||
</li>
|
||||
</ul>
|
||||
<a name="MYSENSORSattr"></a>
|
||||
<p><b>Attributes</b></p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>att <name> autocreate</code><br/>
|
||||
enables auto-creation of MYSENSOR_DEVICE-devices on receival of presentation-messages</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>att <name> 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).
|
||||
May also be configured for individual nodes if not set for gateway.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>att <name> first-sensorid <<number <h; 255>></code><br/>
|
||||
configures the lowest node-id assigned to a mysensor-node on request (defaults to 20)</p>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
=cut
|
410
fhem/FHEM/10_MYSENSORS_DEVICE.pm
Normal file
410
fhem/FHEM/10_MYSENSORS_DEVICE.pm
Normal file
@ -0,0 +1,410 @@
|
||||
##############################################
|
||||
#
|
||||
# fhem bridge to MySensors (see http://mysensors.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
##############################################
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my %gets = (
|
||||
"version" => "",
|
||||
);
|
||||
|
||||
sub MYSENSORS_DEVICE_Initialize($) {
|
||||
|
||||
my $hash = shift @_;
|
||||
|
||||
# Consumer
|
||||
$hash->{DefFn} = "MYSENSORS::DEVICE::Define";
|
||||
$hash->{UndefFn} = "MYSENSORS::DEVICE::UnDefine";
|
||||
$hash->{SetFn} = "MYSENSORS::DEVICE::Set";
|
||||
$hash->{AttrFn} = "MYSENSORS::DEVICE::Attr";
|
||||
|
||||
$hash->{AttrList} =
|
||||
"config:M,I ".
|
||||
"setCommands ".
|
||||
"setReading_.+_\\d+ ".
|
||||
"mapReadingType_.+ ".
|
||||
"requestAck:1 ".
|
||||
"IODev ".
|
||||
$main::readingFnAttributes;
|
||||
|
||||
main::LoadModule("MYSENSORS");
|
||||
}
|
||||
|
||||
package MYSENSORS::DEVICE;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use GPUtils qw(:all);
|
||||
|
||||
use Device::MySensors::Constants qw(:all);
|
||||
use Device::MySensors::Message qw(:all);
|
||||
|
||||
BEGIN {
|
||||
MYSENSORS->import(qw(:all));
|
||||
|
||||
GP_Import(qw(
|
||||
AttrVal
|
||||
readingsSingleUpdate
|
||||
CommandDeleteReading
|
||||
AssignIoPort
|
||||
Log3
|
||||
))
|
||||
};
|
||||
|
||||
my %static_mappings = (
|
||||
V_TEMP => { type => "temperature" },
|
||||
V_HUM => { type => "humidity" },
|
||||
V_PRESSURE => { type => "pressure" },
|
||||
V_LIGHT_LEVEL => { type => "brightness" },
|
||||
V_LIGHT => { type => "switch", val => { 0 => 'off', 1 => 'on' }},
|
||||
);
|
||||
|
||||
sub Define($$) {
|
||||
my ( $hash, $def ) = @_;
|
||||
my ($name, $type, $radioId) = split("[ \t]+", $def);
|
||||
return "requires 1 parameters" unless (defined $radioId and $radioId ne "");
|
||||
$hash->{radioId} = $radioId;
|
||||
$hash->{sets} = {
|
||||
'time' => "",
|
||||
clear => "",
|
||||
reboot => "",
|
||||
};
|
||||
$hash->{ack} = 0;
|
||||
$hash->{typeMappings} = {map {variableTypeToIdx($_) => $static_mappings{$_}} keys %static_mappings};
|
||||
$hash->{readingMappings} = {};
|
||||
AssignIoPort($hash);
|
||||
};
|
||||
|
||||
sub UnDefine($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub Set($@) {
|
||||
my ($hash,$name,$command,@values) = @_;
|
||||
return "Need at least one parameters" unless defined $command;
|
||||
return "Unknown argument $command, choose one of " . join(" ", map {$hash->{sets}->{$_} ne "" ? "$_:$hash->{sets}->{$_}" : $_} sort keys %{$hash->{sets}})
|
||||
if(!defined($hash->{sets}->{$command}));
|
||||
COMMAND_HANDLER: {
|
||||
$command eq "clear" and do {
|
||||
sendClientMessage($hash, childId => 255, cmd => C_INTERNAL, subType => I_CHILDREN, payload => "C");
|
||||
last;
|
||||
};
|
||||
$command eq "time" and do {
|
||||
sendClientMessage($hash, childId => 255, cmd => C_INTERNAL, subType => I_TIME, payload => time);
|
||||
last;
|
||||
};
|
||||
$command eq "reboot" and do {
|
||||
sendClientMessage($hash, childId => 255, cmd => C_INTERNAL, subType => I_REBOOT);
|
||||
last;
|
||||
};
|
||||
$command =~ /^(.+_\d+)$/ and do {
|
||||
my $value = @values ? join " ",@values : "";
|
||||
my ($type,$childId,$mappedValue) = readingToType($hash,$1,$value);
|
||||
sendClientMessage($hash, childId => $childId, cmd => C_SET, subType => $type, payload => $mappedValue);
|
||||
readingsSingleUpdate($hash,$command,$value,1) unless ($hash->{ack} or $hash->{IODev}->{ack});
|
||||
last;
|
||||
};
|
||||
(defined ($hash->{setcommands}->{$command})) and do {
|
||||
my $setcommand = $hash->{setcommands}->{$command};
|
||||
my ($type,$childId,$mappedValue) = readingToType($hash,$setcommand->{var},$setcommand->{val});
|
||||
sendClientMessage($hash,
|
||||
childId => $childId,
|
||||
cmd => C_SET,
|
||||
subType => $type,
|
||||
payload => $mappedValue,
|
||||
);
|
||||
readingsSingleUpdate($hash,$setcommand->{var},$setcommand->{val},1) unless ($hash->{ack} or $hash->{IODev}->{ack});
|
||||
last;
|
||||
};
|
||||
return "$command not defined by attr setCommands";
|
||||
}
|
||||
}
|
||||
|
||||
sub Attr($$$$) {
|
||||
my ($command,$name,$attribute,$value) = @_;
|
||||
|
||||
my $hash = $main::defs{$name};
|
||||
ATTRIBUTE_HANDLER: {
|
||||
$attribute eq "config" and do {
|
||||
if ($main::init_done) {
|
||||
sendClientMessage($hash, cmd => C_INTERNAL, subType => I_CONFIG, payload => $command eq 'set' ? $value : "M");
|
||||
}
|
||||
last;
|
||||
};
|
||||
$attribute eq "setCommands" and do {
|
||||
if ($command eq "set") {
|
||||
foreach my $setCmd (split ("[, \t]+",$value)) {
|
||||
$setCmd =~ /^(.+):(.+_\d+):(.+)$/;
|
||||
$hash->{sets}->{$1}="";
|
||||
$hash->{setcommands}->{$1} = {
|
||||
var => $2,
|
||||
val => $3,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
foreach my $set (keys %{$hash->{setcommands}}) {
|
||||
delete $hash->{sets}->{$set};
|
||||
}
|
||||
$hash->{setcommands} = {};
|
||||
}
|
||||
last;
|
||||
};
|
||||
$attribute =~ /^setReading_(.+_\d+)$/ and do {
|
||||
if ($command eq "set") {
|
||||
$hash->{sets}->{$1}=join(",",split ("[, \t]+",$value));
|
||||
} else {
|
||||
CommandDeleteReading(undef,"$hash->{NAME} $1");
|
||||
delete $hash->{sets}->{$1};
|
||||
}
|
||||
last;
|
||||
};
|
||||
$attribute =~ /^mapReadingType_(.+)/ and do {
|
||||
my $type = variableTypeToIdx("V_$1");
|
||||
if ($command eq "set") {
|
||||
my @values = split ("[, \t]",$value);
|
||||
$hash->{typeMappings}->{$type}={
|
||||
type => shift @values,
|
||||
val => {map {$_ =~ /^(.+):(.+)$/; $1 => $2} @values},
|
||||
}
|
||||
} else {
|
||||
if ($static_mappings{"V_$1"}) {
|
||||
$hash->{typeMappings}->{$type}=$static_mappings{"V_$1"};
|
||||
} else {
|
||||
delete $hash->{typeMappings}->{$type};
|
||||
}
|
||||
CommandDeleteReading(undef,"$hash->{NAME} $1"); #TODO do propper remap of existing readings
|
||||
}
|
||||
last;
|
||||
};
|
||||
$attribute eq "requestAck" and do {
|
||||
if ($command eq "set") {
|
||||
$hash->{ack} = 1;
|
||||
} else {
|
||||
$hash->{ack} = 0;
|
||||
}
|
||||
last;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
sub onGatewayStarted($) {
|
||||
my ($hash) = @_;
|
||||
}
|
||||
|
||||
sub onPresentationMessage($$) {
|
||||
my ($hash,$msg) = @_;
|
||||
}
|
||||
|
||||
sub onSetMessage($$) {
|
||||
my ($hash,$msg) = @_;
|
||||
if (defined $msg->{payload}) {
|
||||
my ($reading,$value) = mapReading($hash,$msg->{subType},$msg->{childId},$msg->{payload});
|
||||
readingsSingleUpdate($hash,$reading,$value,1);
|
||||
} else {
|
||||
Log3 ($hash->{NAME},5,"MYSENSORS_DEVICE $hash->{NAME}: ignoring C_SET-message without payload");
|
||||
}
|
||||
}
|
||||
|
||||
sub onRequestMessage($$) {
|
||||
my ($hash,$msg) = @_;
|
||||
variableTypeToStr($msg->{subType}) =~ /^V_(.+)$/;
|
||||
sendClientMessage($hash,
|
||||
childId => $msg->{childId},
|
||||
cmd => C_SET,
|
||||
subType => $msg->{subType},
|
||||
payload => ReadingsVal($hash->{NAME},"$1\_$msg->{childId}","")
|
||||
);
|
||||
}
|
||||
|
||||
sub onInternalMessage($$) {
|
||||
my ($hash,$msg) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my $type = $msg->{subType};
|
||||
my $typeStr = internalMessageTypeToStr($type);
|
||||
INTERNALMESSAGE: {
|
||||
$type == I_BATTERY_LEVEL and do {
|
||||
readingsSingleUpdate($hash,"batterylevel",$msg->{payload},1);
|
||||
Log3 ($name,4,"MYSENSORS_DEVICE $name: batterylevel $msg->{payload}");
|
||||
last;
|
||||
};
|
||||
$type == I_TIME and do {
|
||||
sendClientMessage($hash,cmd => C_INTERNAL, subType => I_TIME, payload => time);
|
||||
Log3 ($name,4,"MYSENSORS_DEVICE $name: update of time requested");
|
||||
last;
|
||||
};
|
||||
$type == I_VERSION and do {
|
||||
$hash->{$typeStr} = $msg->{payload};
|
||||
last;
|
||||
};
|
||||
$type == I_ID_REQUEST and do {
|
||||
$hash->{$typeStr} = $msg->{payload};
|
||||
last;
|
||||
};
|
||||
$type == I_ID_RESPONSE and do {
|
||||
$hash->{$typeStr} = $msg->{payload};
|
||||
last;
|
||||
};
|
||||
$type == I_INCLUSION_MODE and do {
|
||||
$hash->{$typeStr} = $msg->{payload};
|
||||
last;
|
||||
};
|
||||
$type == I_CONFIG and do {
|
||||
sendClientMessage($hash,cmd => C_INTERNAL, subType => I_CONFIG, payload => AttrVal($name,"config","M"));
|
||||
Log3 ($name,4,"MYSENSORS_DEVICE $name: respond to config-request");
|
||||
last;
|
||||
};
|
||||
$type == I_PING and do {
|
||||
$hash->{$typeStr} = $msg->{payload};
|
||||
last;
|
||||
};
|
||||
$type == I_PING_ACK and do {
|
||||
$hash->{$typeStr} = $msg->{payload};
|
||||
last;
|
||||
};
|
||||
$type == I_LOG_MESSAGE and do {
|
||||
$hash->{$typeStr} = $msg->{payload};
|
||||
last;
|
||||
};
|
||||
$type == I_CHILDREN and do {
|
||||
readingsSingleUpdate($hash,"state","routingtable cleared",1);
|
||||
Log3 ($name,4,"MYSENSORS_DEVICE $name: routingtable cleared");
|
||||
last;
|
||||
};
|
||||
$type == I_SKETCH_NAME and do {
|
||||
$hash->{$typeStr} = $msg->{payload};
|
||||
last;
|
||||
};
|
||||
$type == I_SKETCH_VERSION and do {
|
||||
$hash->{$typeStr} = $msg->{payload};
|
||||
last;
|
||||
};
|
||||
$type == I_REBOOT and do {
|
||||
$hash->{$typeStr} = $msg->{payload};
|
||||
last;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
sub sendClientMessage($%) {
|
||||
my ($hash,%msg) = @_;
|
||||
$msg{radioId} = $hash->{radioId};
|
||||
$msg{ack} = 1 if $hash->{ack};
|
||||
sendMessage($hash->{IODev},%msg);
|
||||
}
|
||||
|
||||
sub mapReading($$) {
|
||||
my($hash, $type, $childId, $value) = @_;
|
||||
|
||||
if(defined (my $mapping = $hash->{typeMappings}->{$type})) {
|
||||
return ("$mapping->{type}_$childId",defined $mapping->{val}->{$value} ? $mapping->{val}->{$value} : $value);
|
||||
} else {
|
||||
return (variableTypeToStr($type)."_$childId",$value);
|
||||
}
|
||||
}
|
||||
|
||||
sub readingToType($$$) {
|
||||
my ($hash,$reading,$value) = @_;
|
||||
$reading =~ /^(.+)_(\d+)$/;
|
||||
if (my @types = grep {$hash->{typeMappings}->{$_}->{type} eq $1} keys %{$hash->{typeMappings}}) {
|
||||
my $type = shift @types;
|
||||
my $valueMappings = $hash->{typeMappings}->{$type}->{val};
|
||||
if (my @mappedValues = grep {$valueMappings->{$_} eq $value} keys %$valueMappings) {
|
||||
return ($type,$2,shift @mappedValues);
|
||||
}
|
||||
return ($type,$2,$value);
|
||||
}
|
||||
return (variableTypeToIdx("V_$1"),$2,$value);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
=begin html
|
||||
|
||||
<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 <name> MYSENSORS_DEVICE <Sensor-type> <node-id></code><br/>
|
||||
Specifies the MYSENSOR_DEVICE device.</p>
|
||||
</ul>
|
||||
<a name="MYSENSORS_DEVICEset"></a>
|
||||
<p><b>Set</b></p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>set <name> clear</code><br/>
|
||||
clears routing-table of a repeater-node</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>set <name> time</code><br/>
|
||||
sets time for nodes (that support it)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>set <name> 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 <name> config [<M|I>]</code><br/>
|
||||
configures metric (M) or inch (I). Defaults to 'M'</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>attr <name> setCommands [<command:reading:value>]*</code><br/>
|
||||
configures one or more commands that can be executed by set.<br/>
|
||||
e.g.: <code>attr <name> setCommands on:switch_1:on off:switch_1:off</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>attr <name> setReading_<reading> [<value>]*</code><br/>
|
||||
configures a reading that can be modified by set-command<br/>
|
||||
e.g.: <code>attr <name> setReading_switch_1 on,off</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>attr <name> mapReadingType_<reading> <new reading name> [<value>:<mappedvalue>]*</code><br/>
|
||||
configures reading user names that should be used instead of technical names<br/>
|
||||
E.g.: <code>attr xxx mapReadingType_LIGHT switch 0:on 1:off</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>att <name> 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>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
=cut
|
236
fhem/FHEM/lib/Device/MySensors/Constants.pm
Normal file
236
fhem/FHEM/lib/Device/MySensors/Constants.pm
Normal file
@ -0,0 +1,236 @@
|
||||
package Device::MySensors::Constants;
|
||||
|
||||
use List::Util qw(first);
|
||||
|
||||
#-- Message types
|
||||
use constant {
|
||||
C_PRESENTATION => 0,
|
||||
C_SET => 1,
|
||||
C_REQ => 2,
|
||||
C_INTERNAL => 3,
|
||||
C_STREAM => 4,
|
||||
};
|
||||
|
||||
use constant commands => qw( C_PRESENTATION C_SET C_REQ C_INTERNAL C_STREAM );
|
||||
|
||||
sub commandToStr($) {
|
||||
(commands)[shift];
|
||||
}
|
||||
|
||||
#-- Variable types
|
||||
use constant {
|
||||
V_TEMP => 0,
|
||||
V_HUM => 1,
|
||||
V_LIGHT => 2,
|
||||
V_DIMMER => 3,
|
||||
V_PRESSURE => 4,
|
||||
V_FORECAST => 5,
|
||||
V_RAIN => 6,
|
||||
V_RAINRATE => 7,
|
||||
V_WIND => 8,
|
||||
V_GUST => 9,
|
||||
V_DIRECTION => 10,
|
||||
V_UV => 11,
|
||||
V_WEIGHT => 12,
|
||||
V_DISTANCE => 13,
|
||||
V_IMPEDANCE => 14,
|
||||
V_ARMED => 15,
|
||||
V_TRIPPED => 16,
|
||||
V_WATT => 17,
|
||||
V_KWH => 18,
|
||||
V_SCENE_ON => 19,
|
||||
V_SCENE_OFF => 20,
|
||||
V_HEATER => 21,
|
||||
V_HEATER_SW => 22,
|
||||
V_LIGHT_LEVEL => 23,
|
||||
V_VAR1 => 24,
|
||||
V_VAR2 => 25,
|
||||
V_VAR3 => 26,
|
||||
V_VAR4 => 27,
|
||||
V_VAR5 => 28,
|
||||
V_UP => 29,
|
||||
V_DOWN => 30,
|
||||
V_STOP => 31,
|
||||
V_IR_SEND => 32,
|
||||
V_IR_RECEIVE => 33,
|
||||
V_FLOW => 34,
|
||||
V_VOLUME => 35,
|
||||
V_LOCK_STATUS => 36,
|
||||
V_DUST_LEVEL => 37,
|
||||
V_VOLTAGE => 38,
|
||||
V_CURRENT => 39,
|
||||
};
|
||||
|
||||
use constant variableTypes => qw{ V_TEMP V_HUM V_LIGHT V_DIMMER V_PRESSURE V_FORECAST V_RAIN
|
||||
V_RAINRATE V_WIND V_GUST V_DIRECTION V_UV V_WEIGHT V_DISTANCE
|
||||
V_IMPEDANCE V_ARMED V_TRIPPED V_WATT V_KWH V_SCENE_ON V_SCENE_OFF
|
||||
V_HEATER V_HEATER_SW V_LIGHT_LEVEL V_VAR1 V_VAR2 V_VAR3 V_VAR4 V_VAR5
|
||||
V_UP V_DOWN V_STOP V_IR_SEND V_IR_RECEIVE V_FLOW V_VOLUME V_LOCK_STATUS
|
||||
V_DUST_LEVEL V_VOLTAGE V_CURRENT};
|
||||
|
||||
sub variableTypeToStr($) {
|
||||
(variableTypes)[shift];
|
||||
}
|
||||
|
||||
sub variableTypeToIdx($) {
|
||||
my $var = shift;
|
||||
return first { (variableTypes)[$_] eq $var } 0 .. scalar(variableTypes);
|
||||
}
|
||||
|
||||
#-- Internal messages
|
||||
use constant {
|
||||
I_BATTERY_LEVEL => 0,
|
||||
I_TIME => 1,
|
||||
I_VERSION => 2,
|
||||
I_ID_REQUEST => 3,
|
||||
I_ID_RESPONSE => 4,
|
||||
I_INCLUSION_MODE => 5,
|
||||
I_CONFIG => 6,
|
||||
I_PING => 7,
|
||||
I_PING_ACK => 8,
|
||||
I_LOG_MESSAGE => 9,
|
||||
I_CHILDREN => 10,
|
||||
I_SKETCH_NAME => 11,
|
||||
I_SKETCH_VERSION => 12,
|
||||
I_REBOOT => 13,
|
||||
I_STARTUP_COMPLETE => 14.
|
||||
};
|
||||
|
||||
use constant internalMessageTypes => qw{ I_BATTERY_LEVEL I_TIME I_VERSION I_ID_REQUEST I_ID_RESPONSE
|
||||
I_INCLUSION_MODE I_CONFIG I_PING I_PING_ACK
|
||||
I_LOG_MESSAGE I_CHILDREN I_SKETCH_NAME I_SKETCH_VERSION
|
||||
I_REBOOT I_STARTUP_COMPLETE };
|
||||
|
||||
sub internalMessageTypeToStr($) {
|
||||
(internalMessageTypes)[shift];
|
||||
}
|
||||
|
||||
#-- Sensor types
|
||||
use constant {
|
||||
S_DOOR => 0,
|
||||
S_MOTION => 1,
|
||||
S_SMOKE => 2,
|
||||
S_LIGHT => 3,
|
||||
S_DIMMER => 4,
|
||||
S_COVER => 5,
|
||||
S_TEMP => 6,
|
||||
S_HUM => 7,
|
||||
S_BARO => 8,
|
||||
S_WIND => 9,
|
||||
S_RAIN => 10,
|
||||
S_UV => 11,
|
||||
S_WEIGHT => 12,
|
||||
S_POWER => 13,
|
||||
S_HEATER => 14,
|
||||
S_DISTANCE => 15,
|
||||
S_LIGHT_LEVEL => 16,
|
||||
S_ARDUINO_NODE => 17,
|
||||
S_ARDUINO_REPEATER_NODE => 18,
|
||||
S_LOCK => 19,
|
||||
S_IR => 20,
|
||||
S_WATER => 21,
|
||||
S_AIR_QUALITY => 22,
|
||||
};
|
||||
|
||||
use constant sensorTypes => qw{ S_DOOR S_MOTION S_SMOKE S_LIGHT S_DIMMER S_COVER S_TEMP S_HUM S_BARO S_WIND
|
||||
S_RAIN S_UV S_WEIGHT S_POWER S_HEATER S_DISTANCE S_LIGHT_LEVEL S_ARDUINO_NODE
|
||||
S_ARDUINO_REPEATER_NODE S_LOCK S_IR S_WATER S_AIR_QUALITY };
|
||||
|
||||
sub sensorTypeToStr($) {
|
||||
(sensorTypes)[shift];
|
||||
}
|
||||
|
||||
sub sensorTypeToIdx($) {
|
||||
my $var = shift;
|
||||
return first { (sensorTypes)[$_] eq $var } 0 .. scalar(sensorTypes);
|
||||
}
|
||||
|
||||
#-- Datastream types
|
||||
use constant {
|
||||
ST_FIRMWARE_CONFIG_REQUEST => 0,
|
||||
ST_FIRMWARE_CONFIG_RESPONSE => 1,
|
||||
ST_FIRMWARE_REQUEST => 2,
|
||||
ST_FIRMWARE_RESPONSE => 3,
|
||||
ST_SOUND => 4,
|
||||
ST_IMAGE => 5,
|
||||
};
|
||||
|
||||
use constant datastreamTypes => qw{ ST_FIRMWARE_CONFIG_REQUEST ST_FIRMWARE_CONFIG_RESPONSE ST_FIRMWARE_REQUEST ST_FIRMWARE_RESPONSE
|
||||
ST_SOUND ST_IMAGE };
|
||||
|
||||
sub datastreamTypeToStr($) {
|
||||
(datastreamTypes)[shift];
|
||||
}
|
||||
|
||||
#-- Payload types
|
||||
use constant {
|
||||
P_STRING => 0,
|
||||
P_BYTE => 1,
|
||||
P_INT16 => 2,
|
||||
P_UINT16 => 3,
|
||||
P_LONG32 => 4,
|
||||
P_ULONG32 => 5,
|
||||
P_CUSTOM => 6,
|
||||
};
|
||||
|
||||
use constant payloadTypes => qw{ P_STRING P_BYTE P_INT16 P_UINT16 P_LONG32 P_ULONG32 P_CUSTOM };
|
||||
|
||||
sub payloadTypeToStr($) {
|
||||
(payloadTypes)[shift];
|
||||
}
|
||||
|
||||
sub subTypeToStr($$) {
|
||||
my $cmd = shift;
|
||||
my $subType = shift;
|
||||
# Convert subtype to string, depending on message type
|
||||
TYPE: {
|
||||
$cmd == C_PRESENTATION and do {
|
||||
$subType = (sensorTypes)[$subType];
|
||||
last;
|
||||
};
|
||||
$cmd == C_SET and do {
|
||||
$subType = (variableTypes)[$subType];
|
||||
last;
|
||||
};
|
||||
$cmd == C_REQ and do {
|
||||
$subType = (variableTypes)[$subType];
|
||||
last;
|
||||
};
|
||||
$cmd == C_INTERNAL and do {
|
||||
$subType = (internalMessageTypes)[$subType];
|
||||
last;
|
||||
};
|
||||
$subType = "<UNKNOWN_$subType>";
|
||||
}
|
||||
return $subType;
|
||||
}
|
||||
|
||||
use Exporter ('import');
|
||||
@EXPORT = ();
|
||||
@EXPORT_OK = (
|
||||
commands,
|
||||
variableTypes,
|
||||
internalMessageTypes,
|
||||
sensorTypes,
|
||||
datastreamTypes,
|
||||
payloadTypes,
|
||||
qw(commandToStr
|
||||
variableTypeToStr
|
||||
variableTypeToIdx
|
||||
internalMessageTypeToStr
|
||||
sensorTypeToStr
|
||||
sensorTypeToIdx
|
||||
datastreamTypeToStr
|
||||
payloadTypeToStr
|
||||
subTypeToStr
|
||||
commands
|
||||
variableTypes
|
||||
internalMessageTypes
|
||||
sensorTypes
|
||||
datastreamTypes
|
||||
payloadTypes
|
||||
));
|
||||
|
||||
%EXPORT_TAGS = (all => [@EXPORT_OK]);
|
||||
|
||||
1;
|
50
fhem/FHEM/lib/Device/MySensors/Message.pm
Normal file
50
fhem/FHEM/lib/Device/MySensors/Message.pm
Normal file
@ -0,0 +1,50 @@
|
||||
package Device::MySensors::Message;
|
||||
|
||||
use Device::MySensors::Constants qw(:all);
|
||||
|
||||
use Exporter ('import');
|
||||
@EXPORT = ();
|
||||
@EXPORT_OK = qw(parseMsg createMsg dumpMsg);
|
||||
%EXPORT_TAGS = (all => [@EXPORT_OK]);
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
sub parseMsg($) {
|
||||
my $txt = shift;
|
||||
my @fields = split(/;/,$txt);
|
||||
my $msgRef = { radioId => $fields[0],
|
||||
childId => $fields[1],
|
||||
cmd => $fields[2],
|
||||
ack => $fields[3],
|
||||
subType => $fields[4],
|
||||
payload => $fields[5] };
|
||||
return $msgRef;
|
||||
}
|
||||
|
||||
sub createMsg(%) {
|
||||
my %msgRef = @_;
|
||||
my @fields = ( $msgRef{'radioId'},
|
||||
$msgRef{'childId'},
|
||||
$msgRef{'cmd'},
|
||||
$msgRef{'ack'},
|
||||
$msgRef{'subType'},
|
||||
defined($msgRef{'payload'}) ? $msgRef{'payload'} : "" );
|
||||
return join(';', @fields);
|
||||
}
|
||||
|
||||
sub dumpMsg($) {
|
||||
my $msgRef = shift;
|
||||
my $cmd = commandToStr($msgRef->{'cmd'});
|
||||
my $st = subTypeToStr( $msgRef->{'cmd'}, $msgRef->{'subType'} );
|
||||
return sprintf("Rx: fr=%03d ci=%03d c=%03d(%-14s) st=%03d(%-16s) ack=%d %s\n", $msgRef->{'radioId'}, $msgRef->{'childId'}, $msgRef->{'cmd'}, $cmd, $msgRef->{'subType'}, $st, $msgRef->{'ack'}, defined($msgRef->{'payload'}) ? "'".$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);
|
||||
}
|
||||
|
||||
1;
|
@ -24,6 +24,7 @@ FHEM/00_KM271.pm physikus http://forum.fhem.de Sonstiges
|
||||
FHEM/00_LIRC.pm rudolfkoenig http://forum.fhem.de Sonstiges
|
||||
FHEM/00_MAXLAN.pm mgehre http://forum.fhem.de MAX
|
||||
FHEM/00_MQTT.pm ntruchsess http://forum.fhem.de Sonstige Systeme
|
||||
FHEM/00_MYSENSORS.pm ntruchsess http://forum.fhem.de Sonstige Systeme
|
||||
FHEM/00_NetzerI2C.pm klausw http://forum.fhem.de Sonstige Systeme
|
||||
FHEM/00_OWX.pm pahenning/ntruchsess http://forum.fhem.de 1Wire
|
||||
FHEM/00_OWX_ASYNC ntruchsess http://forum.fhem.de 1Wire
|
||||
@ -51,6 +52,7 @@ FHEM/10_Itach_IR ulimaass http://forum.fhem.de Sonstige
|
||||
FHEM/10_MAX.pm mgehre http://forum.fhem.de MAX
|
||||
FHEM/10_MQTT_BRIDGE ntruchsess http://forum.fhem.de Sonstige Systeme
|
||||
FHEM/10_MQTT_DEVICE ntruchsess http://forum.fhem.de Sonstige Systeme
|
||||
FHEM/10_MYSENSORS_DEVICE ntruchsess http://forum.fhem.de Sonstige Systeme
|
||||
FHEM/10_OWServer.pm borisneubert/mfr69bs http://forum.fhem.de 1Wire
|
||||
FHEM/10_SOMFY.pm thdankert http://forum.fhem.de Sonstiges
|
||||
FHEM/10_UNIRoll.pm c-herrmann http://forum.fhem.de SlowRF
|
||||
@ -276,6 +278,7 @@ FHEM/SHC_datafields.pm rr2000 http://forum.fhem.de Sonstige
|
||||
FHEM/SHC_parser.pm rr2000 http://forum.fhem.de Sonstige Systeme
|
||||
FHEM/TcpServerUtils.pm rudolfkoenig http://forum.fhem.de Automatisierung
|
||||
FHEM/lib/Device/Firmata/* ntruchsess http://forum.fhem.de Sonstige Systeme
|
||||
FHEM/lib/Device/MySensors/* ntruchsess http://forum.fhem.de Sonstige Systeme
|
||||
FHEM/lib/ProtoThreads.pm ntruchsess http://forum.fhem.de FHEM Development
|
||||
FHEM/lib/SHC_packet_layout.xml rr2000 http://forum.fhem.de Sonstige Systeme
|
||||
FHEM/lib/SWAP/* justme1968 http://forum.fhem.de Sonstige Systeme
|
||||
|
Loading…
Reference in New Issue
Block a user