| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- ##############################################
- # $Id: 52_I2C_DS1307.pm 5927 2014-05-21 21:56:37Z ntruchsess $
- ##############################################
- package main;
- use strict;
- use warnings;
- #add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
- BEGIN {
- if ( !grep( /FHEM\/lib$/, @INC ) ) {
- foreach my $inc ( grep( /FHEM$/, @INC ) ) {
- push @INC, $inc . "/lib";
- }
- }
- }
- #####################################
- my %sets = (
- "datetime" => "",
- "now" => ""
- );
- my %gets = (
- "second" => "",
- "minute" => "",
- "hour" => "",
- "day" => "",
- "month" => "",
- "year" => "",
- "datetime" => "",
- "date" => "",
- "time" => ""
- );
- sub I2C_DS1307_Initialize($) {
- my ($hash) = @_;
- $hash->{DefFn} = "I2C_DS1307_Define";
- $hash->{InitFn} = "I2C_DS1307_Init";
- $hash->{SetFn} = "I2C_DS1307_Set";
- $hash->{AttrFn} = "I2C_DS1307_Attr";
-
- $hash->{I2CRecFn} = "I2C_DS1307_Receive";
- $hash->{AttrList} = "IODev poll_interval $main::readingFnAttributes";
- }
- sub I2C_DS1307_Define($$) {
- my ( $hash, $def ) = @_;
- my @a = split( "[ \t][ \t]*", $def );
- $hash->{STATE} = "defined";
- if ($main::init_done) {
- eval { I2C_DS1307_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
- return I2C_DS1307_Catch($@) if $@;
- }
- return undef;
- }
- sub I2C_DS1307_Init($$) {
- my ( $hash, $args ) = @_;
- my $u =
- "wrong syntax: define <name> I2C_DS1307 [<address>]";
- return $u if (defined $args and int(@$args) > 1 );
- my $name = $hash->{NAME};
- my $address = defined $args ? shift @$args : 0b1101000; #default address
- $hash->{I2C_Address} = $address;
- if (! (defined AttrVal($name,"stateFormat",undef))) {
- $main::attr{$name}{"stateFormat"} = "datetime";
- }
- eval {
- main::AssignIoPort( $hash, AttrVal( $name, "IODev", undef ) );
- $hash->{DS1307} = Device::DS1307->new($address);
- $hash->{DS1307}->attach(I2C_DS1307_IO->new($hash));
- $hash->{STATE} = "Initialized";
- };
- return I2C_DS1307_Catch($@) if $@;
- I2C_DS1307_Poll($hash);
- return undef;
- }
- sub I2C_DS1307_Attr($$$$) {
- my ( $command, $name, $attribute, $value ) = @_;
- my $hash = $main::defs{$name};
- eval {
- if ( $command eq "set" )
- {
- ARGUMENT_HANDLER: {
- $attribute eq "IODev" and do {
- if ( $main::init_done and ( !defined( $hash->{IODev} ) or $hash->{IODev}->{NAME} ne $value ) )
- {
- main::AssignIoPort( $hash, $value );
- my @def = split( ' ', $hash->{DEF} ) if defined $hash->{DEF};
- I2C_DS1307_Init( $hash, \@def ) if ( defined( $hash->{IODev} ) );
- }
- last;
- };
- $attribute eq "poll_interval" and do {
- $hash->{POLL_INTERVAL} = $value;
- if ( $main::init_done )
- {
- I2C_DS1307_Poll($hash);
- }
- last;
- }
- }
- }
- };
- my $ret = I2C_DS1307_Catch($@) if $@;
- if ($ret) {
- $hash->{STATE} = "error setting $attribute to $value: " . $ret;
- return "cannot $command attribute $attribute to $value for $name: " . $ret;
- }
- }
- sub I2C_DS1307_Set(@) {
- my ( $hash, @a ) = @_;
- return "Need at least one parameters" if ( @a < 2 );
- shift @a;
- my $command = shift @a;
- if ( !defined( $sets{$command} ) ) {
- my @commands = ();
- foreach my $key ( sort keys %sets ) {
- push @commands,
- $sets{$key} ? $key . ":" . join( ",", $sets{$key} ) : $key;
- }
- return "Unknown argument $command, choose one of " . join( " ", @commands );
- }
- my $ds1307 = $hash->{DS1307};
- return unless defined $ds1307;
- eval {
- COMMAND_HANDLER: {
- $command eq "datetime" and do {
- $ds1307->setDatetime(join (' ',@a));
- main::readingsSingleUpdate( $hash, "datetime", $ds1307->getDatetime(), 1 );
- last;
- };
- $command eq "now" and do {
- $ds1307->setTime(time());
- main::readingsSingleUpdate( $hash, "datetime", $ds1307->getDatetime(), 1 );
- last;
- };
- }
- };
- return I2C_DS1307_Catch($@) if $@;
- return undef;
- }
- sub I2C_DS1307_Poll {
- my ( $hash ) = @_;
- RemoveInternalTimer($hash);
- eval {
- $hash->{DS1307}->read();
- };
- my $ret = I2C_DS1307_Catch($@) if $@;
- if ($ret) {
- $hash->{STATE} = "error reading DS1307: " . $ret;
- main::Log3 $hash->{NAME},4,"error reading DS1307: ".$ret;
- }
- InternalTimer(gettimeofday()+$hash->{POLL_INTERVAL}, 'I2C_DS1307_Poll', $hash, 0) if defined $hash->{POLL_INTERVAL};
- }
- # package:
- # i2caddress => $data->{address},
- # direction => "i2cread",
- # reg => $data->{register},
- # nbyte => scalar(@{$data->{data}}),
- # received => join (' ',@{$data->{data}})
- sub I2C_DS1307_Receive {
- my ( $hash, $package ) = @_;
-
- $hash->{DS1307}->receive(
- split (' ',$package->{received})
- );
- main::readingsSingleUpdate( $hash, "datetime", $hash->{DS1307}->getDatetime(), 1 );
- }
- sub I2C_DS1307_Catch($) {
- my $exception = shift;
- if ($exception) {
- $exception =~ /^(.*)( at.*FHEM.*)$/;
- return $1;
- }
- return undef;
- }
- package I2C_DS1307_IO;
- use strict;
- use warnings;
- sub new {
- my ( $class, $hash ) = @_;
- return bless { hash => $hash, }, $class;
- }
- sub i2c_write {
- my ( $self, $address, @data ) = @_;
- my $hash = $self->{hash};
- if ( defined( my $iodev = $hash->{IODev} ) ) {
- main::CallFn(
- $iodev->{NAME},
- "I2CWrtFn",
- $iodev,
- {
- i2caddress => $address,
- direction => "i2cwrite",
- data => join( ' ', @data ),
- }
- );
- }
- else {
- die "no IODev assigned to '$hash->{NAME}'";
- }
- }
- sub i2c_read {
- my ( $self, $address, $reg, $nbyte ) = @_;
- my $hash = $self->{hash};
- if ( defined( my $iodev = $hash->{IODev} ) ) {
- main::CallFn(
- $iodev->{NAME},
- "I2CWrtFn",
- $iodev,
- {
- i2caddress => $address,
- direction => "i2cread",
- reg => $reg,
- nbyte => $nbyte,
- }
- );
- }
- else {
- die "no IODev assigned to '$hash->{NAME}'";
- }
- }
- package Device::DS1307;
- use strict;
- use warnings;
- # DS1307 ADDRESS MAP
- use constant DS1307_SECONDS => 0x00;
- use constant DS1307_MINUTES => 0x01;
- use constant DS1307_HOURS => 0x02;
- use constant DS1307_DAY => 0x03;
- use constant DS1307_DATE => 0x04;
- use constant DS1307_MONTH => 0x05;
- use constant DS1307_YEAR => 0x06;
- use constant DS1307_CONTROL => 0x07;
- use constant DS1307_RAM => 0x08;
- # RAM 56 x 8 ?
- # ...
- # 0x3F
- # DS1307 Control Register:
- use constant DS1307_OUT => 0x40; # BIT 7 OUT
- use constant DS1307_SQWE => 0x10; # BIT 4 SQWE
- use constant DS1307_RS1 => 0x02; # BIT 1 RS1
- use constant DS1307_RS0 => 0x01; # BIT 0 RS0
- sub new {
- my ( $class, $address, $timezone, $century ) = @_;
- return bless {
- address => $address,
- time => time(),
- }, $class;
- }
- sub getDatetime {
- my ( $self ) = @_;
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($self->{datetime});
- return sprintf "%04d-%02d-%02d %02d:%02d:%02d", ($year+1900,$mon+1,$mday,$hour,$min,$sec);
- }
- sub setDatetime {
- my ( $self, $value ) = @_;
- $self->{datetime} = main::time_str2num($value);
- $self->write();
- }
- sub setTime {
- my ( $self, $value ) = @_;
- $self->{datetime} = $value;
- $self->write();
- }
- sub attach {
- my ( $self, $io ) = @_;
- $self->{io} = $io;
- }
- sub read {
- my ( $self ) = @_;
- $self->{io}->i2c_read( $self->{address}, 0, 7 );
- }
- sub receive {
- my ($self, @data) = @_;
- my $sec = shift @data;
- my $min = shift @data;
- my $hour = shift @data;
- my $wday = shift @data;
- my $mday = shift @data;
- my $mon = shift @data;
- my $year = shift @data;
-
- #$self->{time} = mktime(sec, min, hour, mday, mon, year, wday = 0, yday = 0, isdst = -1)
- $self->{datetime} = main::mktime($sec, $min, $hour, $mday, $mon, $year, $wday, 0, -1);
- }
- sub write {
- my ( $self ) = @_;
- if (defined $self->{io}) {
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($self->{datetime});
- $self->{io}->i2c_write(
- $self->{address}, #slave address
- 0, #register
- $sec, #data...
- $min,
- $hour,
- $wday, #DS1307 week starts on Sunday
- $mday,
- $mon,
- $year,
- 0); #control
- }
- };
- 1;
- =pod
- =begin html
- <a name="I2C_DS1307"></a>
- <h3>I2C_DS1307</h3>
- <ul>
- reads a DS1307 real-time clock chip via I2C.
- Requires a defined <a href="#I2C">I2C</a>-device to work.<br>
- <a name="I2C_DS1307define"></a>
- <b>Define</b>
- <ul>
- <code>define <name> I2C_DS1307 <i2c-address></code> <br>
- Specifies the I2C_DS1307 device.<br>
- <li>i2c-address is the (device-specific) address of the ic on the i2c-bus</li>
- </ul>
-
- <br>
- <a name="I2C_DS1307set"></a>
- <b>Set</b><br>
- <ul>
- <li><code>set <name> datetime</code>; set DS1307 time. Format is JJJJ-MM-DD HH:MM:SSdisplayed><br></li>
- <li><code>set <name> now</code><br></li>
- </ul>
-
- <a name="I2C_I2Cget"></a>
- <b>Get</b><br>
- <ul>
- N/A<br>
- </ul><br>
- <a name="I2C_DS1307attr"></a>
- <b>Attributes</b><br>
- <ul>
- <li>poll_interval <seconds></li>
- <li><a href="#IODev">IODev</a><br>
- Specify which <a href="#I2C">I2C</a> to use. (Optional, only required if there is more
- than one I2C-device defined.)
- </li>
- <li><a href="#eventMap">eventMap</a><br></li>
- <li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
- </ul>
- </ul>
- <br>
- =end html
- =cut
|