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
+
diff --git a/MAINTAINER.txt b/MAINTAINER.txt
index 4c398f7a3..59f4036a3 100644
--- a/MAINTAINER.txt
+++ b/MAINTAINER.txt
@@ -249,6 +249,7 @@ FHEM/51_Netzer.pm klausw Sonstige Systeme
FHEM/51_RPI_GPIO.pm klausw Einplatinencomputer
FHEM/52_I2C_DS1307 ntruchsess Sonstige Systeme
FHEM/52_I2C_EEPROM.pm klausw Sonstige Systeme
+FHEM/52_I2C_EMC1001.pm eisler Sonstige Systeme
FHEM/52_I2C_LCD ntruchsess Sonstige Systeme
FHEM/52_I2C_BME280 klausw Sonstige Systeme
FHEM/52_I2C_K30 yoda_gh Sonstige Systeme