I2C_EMC1001 [I2C-Address]';
}
if ($msg) {
Log3 ($hash, 1, $msg);
return $msg;
}
if ($main::init_done) {
eval { I2C_EMC1001_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
return I2C_EMC1001_Catch($@) if $@;
}
}
sub I2C_EMC1001_Init($$) { # wird bei FHEM start Define oder wieder
my ( $hash, $args ) = @_;
my $name = $hash->{NAME};
if (defined (my $address = shift @$args)) {
$hash->{I2C_Address} = $address =~ /^0x.*$/ ? oct($address) : $address;
return "$name: I2C Address not valid" unless ($hash->{I2C_Address} < 128 && $hash->{I2C_Address} > 3);
} else {
$hash->{I2C_Address} = EMC1001_I2C_ADDRESS;
}
my $msg = '';
# create default attributes
#if (AttrVal($name, 'poll_interval', '?') eq '?') {
# $msg = CommandAttr(undef, $name . ' poll_interval 5');
# if ($msg) {
# Log3 ($hash, 1, $msg);
# return $msg;
# }
#}
eval {
AssignIoPort($hash, AttrVal($hash->{NAME},"IODev",undef));
I2C_EMC1001_i2cread($hash, Reg_Prd_ID, 1); #get Prd Id
I2C_EMC1001_i2cread($hash, Reg_Mnf_ID, 1); #get Mnf Id
I2C_EMC1001_i2cread($hash, Reg_Rev_No, 1); #get Reg Rev No
};
return I2C_EMC1001_Catch($@) if $@;
}
sub I2C_EMC1001_Catch($) { # Fehlermeldungen formattieren
my $exception = shift;
if ($exception) {
$exception =~ /^(.*)( at.*FHEM.*)$/;
return $1;
}
return undef;
}
sub I2C_EMC1001_Attr (@) { # Wird beim Attribut anlegen/aendern aufgerufen
my ($command, $name, $attr, $val) = @_;
my $hash = $defs{$name};
my $msg = '';
if (defined $command && $command eq "set" && $attr eq "IODev") {
eval {
if ($main::init_done and (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $val)) {
main::AssignIoPort($hash,$val);
my @def = split (' ',$hash->{DEF});
I2C_EMC1001_Init($hash,\@def) if (defined ($hash->{IODev}));
}
};
$msg = I2C_EMC1001_Catch($@) if $@;
} elsif ($attr eq 'poll_interval') {
if (defined($val)) {
if ($val =~ m/^(0*[1-9][0-9]*)$/) {
RemoveInternalTimer($hash);
I2C_EMC1001_Poll($hash) if ($main::init_done);
#InternalTimer(gettimeofday() + 5, 'I2C_EMC1001_Poll', $hash, 0) if ($main::init_done);
} else {
$msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
}
} else {
RemoveInternalTimer($hash);
}
} elsif ($attr =~ m/^round(Temperature)Decimal$/ && defined($val)) {
$msg = 'Wrong value: $val for $attr defined. value must be a one of 0,1,2' unless ($val =~ m/^(0*[0-2])$/);
}
return ($msg) ? $msg : undef;
}
sub I2C_EMC1001_Poll($) { # Messwerte regelmaessig anfordern
my ($hash) = @_;
my $name = $hash->{NAME};
I2C_EMC1001_Set($hash, ($name, 'readValues')); # Read values
my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0);
if ($pollInterval > 0) {
InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_EMC1001_Poll', $hash, 0);
}
}
sub I2C_EMC1001_Set($@) { # Messwerte manuell anfordern
my ($hash, @a) = @_;
my $name = $a[0];
my $cmd = $a[1];
if(!defined($sets{$cmd})) {
return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %sets) . ":noArg"
}
if ($cmd eq 'readValues') {
I2C_EMC1001_i2cread($hash, Reg_TMP_HB, 1);
I2C_EMC1001_i2cread($hash, Reg_TMP_LB, 1);
RemoveInternalTimer($hash);
InternalTimer(gettimeofday() + 1, 'I2C_EMC1001_UpdateReadings', $hash, 0);
}
return undef
}
sub I2C_EMC1001_Get($@) { # Messwerte manuell anfordern
my ($hash, @a) = @_;
my $name = $a[0];
my $cmd = $a[1];
if (defined($cmd) && $cmd eq 'readValues') {
I2C_EMC1001_i2cread($hash, Reg_TMP_HB, 1);
I2C_EMC1001_i2cread($hash, Reg_TMP_LB, 1);
RemoveInternalTimer($hash);
InternalTimer(gettimeofday() + 1, 'I2C_EMC1001_UpdateReadings', $hash, 0);
} else {
return 'Unknown argument ' . $cmd . ', choose one of readValues:noArg';
}
return undef
}
sub I2C_EMC1001_UpdateReadings($) { # Messwerte auslesen
my ($hash) = @_;
I2C_EMC1001_i2cread($hash, Reg_TMP_HB, 1);
I2C_EMC1001_i2cread($hash, Reg_TMP_LB, 1);
my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0); #poll_interval Timer wiederherstellen
InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_EMC1001_Poll', $hash, 0) if ($pollInterval > 0);
}
sub I2C_EMC1001_Undef($$) { # Device loeschen
my ($hash, $arg) = @_;
RemoveInternalTimer($hash);
return undef;
}
sub I2C_EMC1001_I2CRec ($$) { # wird vom IODev aus aufgerufen wenn I2C Daten vorliegen
my ($hash, $clientmsg) = @_;
my $name = $hash->{NAME};
my $pname = undef;
my $phash = $hash->{IODev};
$pname = $phash->{NAME};
while ( my ( $k, $v ) = each %$clientmsg ) { #erzeugen von Internals fuer alle Keys in $clientmsg die mit dem physical Namen beginnen
$hash->{$k} = $v if $k =~ /^$pname/ ;
}
if ( $clientmsg->{direction} && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok" ) {
if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received}) ) {
Log3 $hash, 5, "$name Rx, Reg: $clientmsg->{reg}, Data: $clientmsg->{received}";
I2C_EMC1001_GetProdId ($hash, $clientmsg->{received}) if $clientmsg->{reg} == Reg_Prd_ID && $clientmsg->{nbyte} == 1;
I2C_EMC1001_GetMnfId ($hash, $clientmsg->{received}) if $clientmsg->{reg} == Reg_Mnf_ID && $clientmsg->{nbyte} == 1;
I2C_EMC1001_GetRevN ($hash, $clientmsg->{received}) if $clientmsg->{reg} == Reg_Rev_No && $clientmsg->{nbyte} == 1;
I2C_EMC1001_GetReadingsTemperatureValueHighByte ($hash, $clientmsg->{received}) if $clientmsg->{reg} == Reg_TMP_HB && $clientmsg->{nbyte} == 1;
I2C_EMC1001_GetReadingsTemperatureValueLowByte ($hash, $clientmsg->{received}) if $clientmsg->{reg} == Reg_TMP_LB && $clientmsg->{nbyte} == 1;
}
}
return undef
}
sub I2C_EMC1001_GetProdId ($$) {
my ($hash, $rawdata) = @_;
if ($rawdata == hex("00")) {
$hash->{DeviceType} = "EMC1001";
} elsif ($rawdata == hex("01")) {
$hash->{DeviceType} = "EMC1001-1";
}
readingsSingleUpdate($hash, 'DeviceType', $hash->{DeviceType}, 1);
$hash->{STATE} = 'Initialized';
I2C_EMC1001_Poll($hash) if defined(AttrVal($hash->{NAME}, 'poll_interval', undef)); # wenn poll_interval definiert -> timer starten
}
sub I2C_EMC1001_GetMnfId ($$) {
my ($hash, $rawdata) = @_;
readingsSingleUpdate($hash, 'DeviceManufactureId', sprintf("0x%X", $rawdata), 1);
}
sub I2C_EMC1001_GetRevN ($$) {
my ($hash, $rawdata) = @_;
readingsSingleUpdate($hash, 'DeviceRevisionNumber', sprintf("%d", $rawdata), 1);
}
sub I2C_EMC1001_GetReadingsTemperatureValueHighByte ($$) { # empfangenes Temperature High Byte verarbeiten
my ($hash, $rawdata) = @_;
Log3 $hash, 5, "ReadingsTemperatureValueHighByte: $rawdata";
$hash->{TemperatureValueHighByte} = $rawdata;
}
sub I2C_EMC1001_GetReadingsTemperatureValueLowByte ($$) { # empfangenes Temperature Low Byte verarbeiten
my ($hash, $rawdata) = @_;
Log3 $hash, 5, "ReadingsTemperatureValueLowByte: $rawdata";
$hash->{TemperatureValueLowByte} = $rawdata;
I2C_EMC1001_GetTemp($hash, $rawdata);
my $tem = ReadingsVal($hash->{NAME},"temperature", undef);
readingsSingleUpdate(
$hash,
'state',
(defined $tem ? "T: $tem " : ""),
1
);
}
sub I2C_EMC1001_GetTemp($@) { # Temperatur Messwerte verarbeiten
my ($hash, @raw) = @_;
my $temp= $hash->{TemperatureValueHighByte};
my $templo= $hash->{TemperatureValueLowByte};
$templo = $templo >> 6;
if ($temp < 0) {
$templo = 3-$templo;
}
my $temperature = sprintf(
'%.' . AttrVal($hash->{NAME}, 'roundTemperatureDecimal', 1) . 'f',
sprintf("%d.%d", $temp, $templo*25)
);
readingsSingleUpdate($hash, 'temperature', $temperature, 1);
}
sub I2C_EMC1001_i2cread($$$) { # Lesebefehl an Hardware absetzen (antwort kommt in I2C_*****_I2CRec an)
my ($hash, $reg, $nbyte) = @_;
if (defined (my $iodev = $hash->{IODev})) {
Log3 $hash, 5, "$hash->{NAME}: $hash->{I2C_Address} read $nbyte Byte from Register $reg";
CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
direction => "i2cread",
i2caddress => $hash->{I2C_Address},
reg => $reg,
nbyte => $nbyte
});
} else {
return "no IODev assigned to '$hash->{NAME}'";
}
}
sub I2C_EMC1001_i2cwrite($$$) { # Schreibbefehl an Hardware absetzen
my ($hash, $reg, @data) = @_;
if (defined (my $iodev = $hash->{IODev})) {
Log3 $hash, 5, "$hash->{NAME}: $hash->{I2C_Address} write " . join (' ',@data) . " to Register $reg";
CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
direction => "i2cwrite",
i2caddress => $hash->{I2C_Address},
reg => $reg,
data => join (' ',@data),
});
} else {
return "no IODev assigned to '$hash->{NAME}'";
}
}
sub I2C_EMC1001_DbLog_splitFn($) { # Einheiten
my ($event) = @_;
Log3 undef, 5, "in DbLog_splitFn empfangen: $event";
my ($reading, $value, $unit) = "";
my @parts = split(/ /,$event);
$reading = shift @parts;
$reading =~ tr/://d;
$value = $parts[0];
$unit = "\xB0C" if(lc($reading) =~ m/temp/);
return ($reading, $value, $unit);
}
1;
=pod
=item device
=item summary reads temperature from an via I2C connected EMC1001
=item summary_DE lese Temperatur eines über I2C angeschlossenen EMC1001
=begin html
I2C_EMC1001
(en | de)
Provides an interface to the digital temperature sensor EMC1001
The I2C messages are send through an I2C interface module like RPII2C, FRM
or NetzerI2C so this device must be defined first.
attribute IODev must be set
Define
define EMC1001 I2C_EMC1001 [<I2C Address>]
without defined <I2C Address>
0x48 will be used as address
Examples:
define EMC1001 I2C_EMC1001 0x48
attr EMC1001 poll_interval 5
attr roundTemperatureDecimal 2
Set
set EMC1001 <readValues>
Reads current temperature values from the sensor.
Normaly this execute automaticly at each poll intervall. You can execute
this manually if you want query the current values.
Attributes
- poll_interval
Set the polling interval in minutes to query the sensor for new measured
values.
Default: 5, valid values: any whole number
- roundTemperatureDecimal
Round temperature values to given decimal places.
Default: 1, valid values: 0, 1, 2
- IODev
- do_not_notify
- showtime
=end html
=begin html_DE
I2C_EMC1001
(en | de)
Ermöglicht die Verwendung eines digitalen Temperatur EMC1001 über den I2C Bus des Raspberry Pi.
I2C-Botschaften werden über ein I2C Interface Modul wie beispielsweise das RPII2C, FRM
oder NetzerI2C gesendet. Daher muss dieses vorher definiert werden.
Das Attribut IODev muss definiert sein.
Define
define EMC1001 <EMC1001_name> [<I2C Addresse>]
Fehlt <I2C Address>
wird 0x48 verwendet
Beispiel:
define EMC1001 I2C_EMC1001 0x48
attr EMC1001 poll_interval 5
attr roundTemperatureDecimal 2
Set
set EMC1001 readValues
set <name> readValues
Aktuelle Temperatur Werte vom Sensor lesen.
Attribute
- poll_interval
Definiert das Poll Intervall in Minuten für das Auslesen einer neuen Messung.
Default: 5, gültige Werte: 1, 2, 5, 10, 20, 30
- roundTemperatureDecimal
Rundet jeweils den Temperaturwert mit den angegebenen Nachkommastellen.
Standard: 1, gültige Werte: 0, 1, 2
- IODev
- do_not_notify
- showtime
=end html_DE
=cut