############################################## # $Id$ ############################################## 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"; }; }; }; use Device::Firmata::Constants qw/ :all /; ##################################### my %sets = ( "alarm" => "", "count" => 0, ); my %gets = ( "reading" => "", "state" => "", "count" => 0, "alarm" => "off" ); sub FRM_IN_Initialize($) { my ($hash) = @_; $hash->{SetFn} = "FRM_IN_Set"; $hash->{GetFn} = "FRM_IN_Get"; $hash->{AttrFn} = "FRM_IN_Attr"; $hash->{DefFn} = "FRM_Client_Define"; $hash->{InitFn} = "FRM_IN_Init"; $hash->{UndefFn} = "FRM_Client_Undef"; $hash->{AttrList} = "IODev count-mode:none,rising,falling,both count-threshold reset-on-threshold-reached:yes,no internal-pullup:on,off activeLow:yes,no $main::readingFnAttributes"; main::LoadModule("FRM"); } sub FRM_IN_Init($$) { my ($hash,$args) = @_; my $ret = FRM_Init_Pin_Client($hash,$args,PIN_INPUT); return $ret if (defined $ret); eval { my $firmata = FRM_Client_FirmataDevice($hash); my $pin = $hash->{PIN}; if (defined (my $pullup = AttrVal($hash->{NAME},"internal-pullup",undef))) { $firmata->digital_write($pin,$pullup eq "on" ? 1 : 0); } $firmata->observe_digital($pin,\&FRM_IN_observer,$hash); }; return FRM_Catch($@) if $@; if (! (defined AttrVal($hash->{NAME},"stateFormat",undef))) { $main::attr{$hash->{NAME}}{"stateFormat"} = "reading"; } main::readingsSingleUpdate($hash,"state","Initialized",1); return undef; } sub FRM_IN_observer { my ($pin,$old,$new,$hash) = @_; my $name = $hash->{NAME}; Log3 $name,5,"onDigitalMessage for pin ".$pin.", old: ".(defined $old ? $old : "--").", new: ".(defined $new ? $new : "--"); if (AttrVal($hash->{NAME},"activeLow","no") eq "yes") { $old = $old == PIN_LOW ? PIN_HIGH : PIN_LOW if (defined $old); $new = $new == PIN_LOW ? PIN_HIGH : PIN_LOW; } my $changed = ((!(defined $old)) or ($old != $new)); main::readingsBeginUpdate($hash); if ($changed) { if (defined (my $mode = main::AttrVal($name,"count-mode",undef))) { if (($mode eq "both") or (($mode eq "rising") and ($new == PIN_HIGH)) or (($mode eq "falling") and ($new == PIN_LOW))) { my $count = main::ReadingsVal($name,"count",0); $count++; if (defined (my $threshold = main::AttrVal($name,"count-threshold",undef))) { if ( $count > $threshold ) { if (AttrVal($name,"reset-on-threshold-reached","no") eq "yes") { $count=0; main::readingsBulkUpdate($hash,"alarm","on",1); } elsif ( main::ReadingsVal($name,"alarm","off") ne "on" ) { main::readingsBulkUpdate($hash,"alarm","on",1); } } } main::readingsBulkUpdate($hash,"count",$count,1); } }; } main::readingsBulkUpdate($hash,"reading",$new == PIN_HIGH ? "on" : "off", $changed); main::readingsEndUpdate($hash,1); } sub FRM_IN_Set { my ($hash, @a) = @_; return "Need at least one parameters" if(@a < 2); return "Unknown argument $a[1], choose one of " . join(" ", sort keys %sets) if(!defined($sets{$a[1]})); my $command = $a[1]; my $value = $a[2]; COMMAND_HANDLER: { $command eq "alarm" and do { return undef if (!($value eq "off" or $value eq "on")); main::readingsSingleUpdate($hash,"alarm",$value,1); last; }; $command eq "count" and do { main::readingsSingleUpdate($hash,"count",$value,1); last; }; } } sub FRM_IN_Get($) { my ($hash, @a) = @_; return "Need at least one parameters" if(@a < 2); return "Unknown argument $a[1], choose one of " . join(" ", sort keys %gets) if(!defined($gets{$a[1]})); my $name = shift @a; my $cmd = shift @a; ARGUMENT_HANDLER: { $cmd eq "reading" and do { eval { return FRM_Client_FirmataDevice($hash)->digital_read($hash->{PIN}) == PIN_HIGH ? "on" : "off"; }; return $@; }; ( $cmd eq "count" or $cmd eq "alarm" or $cmd eq "state" ) and do { return main::ReadingsVal($name,"count",$gets{$cmd}); }; } return undef; } sub FRM_IN_Attr($$$$) { my ($command,$name,$attribute,$value) = @_; my $hash = $main::defs{$name}; my $pin = $hash->{PIN}; 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)) { FRM_Client_AssignIOPort($hash,$value); FRM_Init_Client($hash) if (defined ($hash->{IODev})); } last; }; $attribute eq "count-mode" and do { if ($value ne "none" and !defined main::ReadingsVal($name,"count",undef)) { main::readingsSingleUpdate($main::defs{$name},"count",$sets{count},1); } last; }; $attribute eq "reset-on-threshold-reached" and do { if ($value eq "yes" and defined (my $threshold = main::AttrVal($name,"count-threshold",undef))) { if (main::ReadingsVal($name,"count",0) > $threshold) { main::readingsSingleUpdate($main::defs{$name},"count",$sets{count},1); } } last; }; $attribute eq "count-threshold" and do { if (main::ReadingsVal($name,"count",0) > $value) { main::readingsBeginUpdate($hash); if (main::ReadingsVal($name,"alarm","off") ne "on") { main::readingsBulkUpdate($hash,"alarm","on",1); } if (main::AttrVal($name,"reset-on-threshold-reached","no") eq "yes") { main::readingsBulkUpdate($main::defs{$name},"count",0,1); } main::readingsEndUpdate($hash,1); } last; }; $attribute eq "internal-pullup" and do { if ($main::init_done) { my $firmata = FRM_Client_FirmataDevice($hash); $firmata->digital_write($pin,$value eq "on" ? 1 : 0); #ignore any errors here, the attribute-value will be applied next time FRM_IN_init() is called. } last; }; $attribute eq "activeLow" and do { my $oldval = AttrVal($hash->{NAME},"activeLow","no"); if ($oldval ne $value) { $main::attr{$hash->{NAME}}{activeLow} = $value; if ($main::init_done) { my $firmata = FRM_Client_FirmataDevice($hash); FRM_IN_observer($pin,undef,$firmata->digital_read($pin),$hash); } }; last; }; } } elsif ($command eq "del") { ARGUMENT_HANDLER: { $attribute eq "internal-pullup" and do { my $firmata = FRM_Client_FirmataDevice($hash); $firmata->digital_write($pin,0); last; }; $attribute eq "activeLow" and do { if (AttrVal($hash->{NAME},"activeLow","no") eq "yes") { delete $main::attr{$hash->{NAME}}{activeLow}; my $firmata = FRM_Client_FirmataDevice($hash); FRM_IN_observer($pin,undef,$firmata->digital_read($pin),$hash); }; last; }; } } }; if (my $error = FRM_Catch($@)) { $hash->{STATE} = "error setting $attribute to $value: ".$error; return "cannot $command attribute $attribute to $value for $name: ".$error; } } 1; =pod =begin html

FRM_IN


=end html =cut