From 79a664abe5a9242e4995d2ca151e717b068b4140 Mon Sep 17 00:00:00 2001 From: jensb <> Date: Fri, 30 Oct 2020 18:16:24 +0000 Subject: [PATCH] 20_FRM_*.pm: feature update, see forum #114552 msg #1087982 - check for IODev install error in Init, Get, Set, Attr and Undef - missing get/set argument metadata added - get/set argument verifier improved - moved define argument verification and decoding from Init to Define - error behaviour of Init, Get, Set and Attr standardized - annotaded module help of attributes for FHEMWEB git-svn-id: https://svn.fhem.de/fhem/trunk@23054 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 3 + fhem/FHEM/20_FRM_AD.pm | 358 +++++++++++++---------- fhem/FHEM/20_FRM_IN.pm | 409 +++++++++++++++----------- fhem/FHEM/20_FRM_OUT.pm | 398 ++++++++++++++++---------- fhem/FHEM/20_FRM_PWM.pm | 428 ++++++++++++++++------------ fhem/FHEM/20_FRM_RGB.pm | 316 +++++++++++++-------- fhem/FHEM/20_FRM_ROTENC.pm | 336 +++++++++++++--------- fhem/FHEM/20_FRM_SERVO.pm | 227 +++++++++++---- fhem/FHEM/20_FRM_STEPPER.pm | 552 ++++++++++++++++++++++-------------- fhem/MAINTAINER.txt | 12 +- 10 files changed, 1872 insertions(+), 1167 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 67877b8c3..974beda21 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,8 @@ # 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. + - feature: 10_FRM: Device::Firmata, receiveTimeout, ... (forum #114552) + - feature: 20_FRM_*: Device::Firmata, ... (forum #114552) + - change: 20_FRM_LCD: removed, use I2C_LCD (forum #114552) - bugfix: 70_DENON_AVR: fixed multizone bug (thx timmib) - feature: 49_Arlo: Added 2-factor authentication - bugfix: 73_AutoShuttersControl: fix IsDay Fn for weekend condition diff --git a/fhem/FHEM/20_FRM_AD.pm b/fhem/FHEM/20_FRM_AD.pm index c36acf79c..34c799ebc 100755 --- a/fhem/FHEM/20_FRM_AD.pm +++ b/fhem/FHEM/20_FRM_AD.pm @@ -1,37 +1,42 @@ ######################################################################################## -# # $Id$ -# -# FHEM module for one Firmata analog input pin -# -######################################################################################## -# -# LICENSE AND COPYRIGHT -# -# Copyright (C) 2013 ntruchess -# Copyright (C) 2016 jensb -# -# All rights reserved -# -# This script 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. -# -# The GNU General Public License can be found at -# http://www.gnu.org/copyleft/gpl.html. -# A copy is found in the textfile GPL.txt and important notices to the license -# from the author is found in LICENSE.txt distributed with these scripts. -# -# This script 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. -# -# This copyright notice MUST APPEAR in all copies of the script! -# ######################################################################################## +=encoding UTF-8 + +=head1 NAME + +FHEM module for one Firmata analog input pin + +=head1 LICENSE AND COPYRIGHT + +Copyright (C) 2013 ntruchess +Copyright (C) 2016 jensb + +All rights reserved + +This script 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. + +This script 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 this script; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +A copy of the GNU General Public License, Version 2 can also be found at + +http://www.gnu.org/licenses/old-licenses/gpl-2.0. + +This copyright notice MUST APPEAR in all copies of the script! + +=cut + package main; use strict; @@ -39,26 +44,24 @@ 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"; - }; - }; + if (!grep(/FHEM\/lib$/,@INC)) { + foreach my $inc (grep(/FHEM$/,@INC)) { + push @INC,$inc."/lib"; + }; + }; }; -use Device::Firmata::Constants qw/ :all /; - ##################################### +# get command default return values my %gets = ( - "reading" => "", - "state" => "", - "alarm-upper-threshold" => "off", - "alarm-lower-threshold" => "off", + "reading" => "", + "state" => "", + "alarm-upper-threshold" => "off", + "alarm-lower-threshold" => "off", ); -sub -FRM_AD_Initialize($) +sub FRM_AD_Initialize { my ($hash) = @_; @@ -66,83 +69,102 @@ FRM_AD_Initialize($) $hash->{GetFn} = "FRM_AD_Get"; $hash->{DefFn} = "FRM_Client_Define"; $hash->{InitFn} = "FRM_AD_Init"; - + $hash->{AttrList} = "IODev upper-threshold lower-threshold $main::readingFnAttributes"; main::LoadModule("FRM"); } -sub -FRM_AD_Init($$) +sub FRM_AD_Init { - my ($hash,$args) = @_; - my $ret = FRM_Init_Pin_Client($hash,$args,PIN_ANALOG); - return $ret if (defined $ret); - my $firmata = $hash->{IODev}->{FirmataDevice}; - my $name = $hash->{NAME}; - my $resolution = 10; - if (defined $firmata->{metadata}{analog_resolutions}) { - $resolution = $firmata->{metadata}{analog_resolutions}{$hash->{PIN}} - } - $hash->{resolution} = $resolution; - $hash->{".max"} = defined $resolution ? (1<<$resolution)-1 : 1024; - eval { - $firmata->observe_analog($hash->{PIN},\&FRM_AD_observer,$hash); - }; - return FRM_Catch($@) if $@; - if (! (defined AttrVal($name,"stateFormat",undef))) { - $main::attr{$name}{"stateFormat"} = "reading"; - } - if (! (defined AttrVal($name,"event-min-interval",undef))) { - $main::attr{$name}{"event-min-interval"} = 5; - } - main::readingsSingleUpdate($hash,"state","Initialized",1); - return undef; + my ($hash,$args) = @_; + my $name = $hash->{NAME}; + + if (defined($main::defs{$name}{IODev_ERROR})) { + return 'Perl module Device::Firmata not properly installed'; + } + + my $ret = FRM_Init_Pin_Client($hash, $args, Device::Firmata::Constants->PIN_ANALOG); + if (defined($ret)) { + readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1); + return $ret; + } + + eval { + my $firmata = FRM_Client_FirmataDevice($hash); + my $resolution = 10; + if (defined $firmata->{metadata}{analog_resolutions}) { + $resolution = $firmata->{metadata}{analog_resolutions}{$hash->{PIN}} + } + $hash->{resolution} = $resolution; + $hash->{".max"} = defined $resolution ? (1<<$resolution)-1 : 1024; + $firmata->observe_analog($hash->{PIN},\&FRM_AD_observer,$hash); + }; + if ($@) { + $ret = FRM_Catch($@); + readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1); + return $ret; + } + + if (!(defined AttrVal($name,"stateFormat",undef))) { + $main::attr{$name}{"stateFormat"} = "reading"; + } + + if (!(defined AttrVal($name,"event-min-interval",undef))) { + $main::attr{$name}{"event-min-interval"} = 5; + } + + main::readingsSingleUpdate($hash,"state","Initialized",1); + + return undef; } -sub -FRM_AD_observer +sub FRM_AD_observer { - my ($pin,$old,$new,$hash) = @_; - my $name = $hash->{NAME}; - Log3 $name,5,"onAnalogMessage for pin ".$pin.", old: ".(defined $old ? $old : "--").", new: ".(defined $new ? $new : "--"); - main::readingsBeginUpdate($hash); - main::readingsBulkUpdate($hash,"reading",$new,1); - my $upperthresholdalarm = ReadingsVal($name,"alarm-upper-threshold","off"); - if ( $new < AttrVal($name,"upper-threshold",$hash->{".max"}) ) { - if ( $upperthresholdalarm eq "on" ) { - main::readingsBulkUpdate($hash,"alarm-upper-threshold","off",1); - } - my $lowerthresholdalarm = ReadingsVal($name,"alarm-lower-threshold","off"); - if ( $new > AttrVal($name,"lower-threshold",-1) ) { - if ( $lowerthresholdalarm eq "on" ) { - main::readingsBulkUpdate($hash,"alarm-lower-threshold","off",1); - } - } else { - if ( $lowerthresholdalarm eq "off" ) { - main::readingsBulkUpdate($hash,"alarm-lower-threshold","on",1); - } - } - } else { - if ( $upperthresholdalarm eq "off" ) { - main::readingsBulkUpdate($hash,"alarm-upper-threshold","on",1); - } - }; - main::readingsEndUpdate($hash,1); + my ($pin,$old,$new,$hash) = @_; + my $name = $hash->{NAME}; + Log3 $name, 5, "$name: observer pin: ".$pin.", old: ".(defined $old ? $old : "--").", new: ".(defined $new ? $new : "--"); + main::readingsBeginUpdate($hash); + main::readingsBulkUpdate($hash,"reading",$new,1); + my $upperthresholdalarm = ReadingsVal($name,"alarm-upper-threshold","off"); + if ( $new < AttrVal($name,"upper-threshold",$hash->{".max"}) ) { + if ( $upperthresholdalarm eq "on" ) { + main::readingsBulkUpdate($hash,"alarm-upper-threshold","off",1); + } + my $lowerthresholdalarm = ReadingsVal($name,"alarm-lower-threshold","off"); + if ( $new > AttrVal($name,"lower-threshold",-1) ) { + if ( $lowerthresholdalarm eq "on" ) { + main::readingsBulkUpdate($hash,"alarm-lower-threshold","off",1); + } + } else { + if ( $lowerthresholdalarm eq "off" ) { + main::readingsBulkUpdate($hash,"alarm-lower-threshold","on",1); + } + } + } else { + if ( $upperthresholdalarm eq "off" ) { + main::readingsBulkUpdate($hash,"alarm-upper-threshold","on",1); + } + }; + main::readingsEndUpdate($hash,1); } -sub -FRM_AD_Get($) +sub FRM_AD_Get { - my ($hash,@a) = @_; - my $name = shift @a; - my $cmd = shift @a; - my $ret; + my ($hash, $name, $cmd, @a) = @_; + + return "get command missing" if(!defined($cmd)); + return "unknown get command '$cmd', choose one of " . join(":noArg ", sort keys %gets) . ":noArg" if(!defined($gets{$cmd})); + ARGUMENT_HANDLER: { $cmd eq "reading" and do { - eval { + my $result = eval { + if (defined($main::defs{$name}{IODev_ERROR})) { + return 'Perl module Device::Firmata not properly installed'; + } return FRM_Client_FirmataDevice($hash)->analog_read($hash->{PIN}); }; - return $@; + return FRM_Catch($@) if ($@); + return $result; }; ( $cmd eq "alarm-upper-threshold" or $cmd eq "alarm-lower-threshold" or $cmd eq "state" ) and do { return main::ReadingsVal($name,"count",$gets{$cmd}); @@ -151,8 +173,8 @@ FRM_AD_Get($) return undef; } -sub -FRM_AD_Attr($$$$) { +sub FRM_AD_Attr +{ my ($command,$name,$attribute,$value) = @_; my $hash = $main::defs{$name}; eval { @@ -169,9 +191,9 @@ FRM_AD_Attr($$$$) { } }; if ($@) { - $@ =~ /^(.*)( at.*FHEM.*)$/; - $hash->{STATE} = "error setting $attribute to $value: ".$1; - return "cannot $command attribute $attribute to $value for $name: ".$1; + my $ret = FRM_Catch($@); + $hash->{STATE} = "$command $attribute error: " . $ret; + return $hash->{STATE}; } } @@ -179,46 +201,65 @@ FRM_AD_Attr($$$$) { =pod - CHANGES +=head1 CHANGES 2016 jensb o modified sub FRM_AD_Init to catch exceptions and return error message - + 19.01.2018 jensb o support analog resolution depending on device capability + 24.08.2020 jensb + o check for IODev install error in Init and Get + o prototypes removed + o set argument verifier added + + 22.10.2020 jensb + o annotaded module help of attributes for FHEMWEB + =cut -=pod + +=head1 FHEM COMMANDREF METADATA + +=over + =item device + =item summary Firmata: analog input -=item summary_DE Firmata: analog Eingang + +=item summary_DE Firmata: analoger Eingang + +=back + +=head1 INSTALLATION AND CONFIGURATION + =begin html - +

FRM_AD


=end html + +=begin html_DE + +
+

FRM_AD

+

+ +=end html_DE + =cut diff --git a/fhem/FHEM/20_FRM_IN.pm b/fhem/FHEM/20_FRM_IN.pm index 33cf0b976..acca1bb22 100755 --- a/fhem/FHEM/20_FRM_IN.pm +++ b/fhem/FHEM/20_FRM_IN.pm @@ -1,37 +1,42 @@ ######################################################################################## -# # $Id$ -# -# FHEM module for one Firmata digial input pin -# -######################################################################################## -# -# LICENSE AND COPYRIGHT -# -# Copyright (C) 2013 ntruchess -# Copyright (C) 2018 jensb -# -# All rights reserved -# -# This script 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. -# -# The GNU General Public License can be found at -# http://www.gnu.org/copyleft/gpl.html. -# A copy is found in the textfile GPL.txt and important notices to the license -# from the author is found in LICENSE.txt distributed with these scripts. -# -# This script 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. -# -# This copyright notice MUST APPEAR in all copies of the script! -# ######################################################################################## +=encoding UTF-8 + +=head1 NAME + +FHEM module for one Firmata digial input pin + +=head1 LICENSE AND COPYRIGHT + +Copyright (C) 2013 ntruchess +Copyright (C) 2018 jensb + +All rights reserved + +This script 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. + +This script 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 this script; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +A copy of the GNU General Public License, Version 2 can also be found at + +http://www.gnu.org/licenses/old-licenses/gpl-2.0. + +This copyright notice MUST APPEAR in all copies of the script! + +=cut + package main; use strict; @@ -39,22 +44,22 @@ use warnings; #add FHEM/lib to @INC if it's not already 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"; - }; - }; + if (!grep(/FHEM\/lib$/,@INC)) { + foreach my $inc (grep(/FHEM$/,@INC)) { + push @INC,$inc."/lib"; + }; + }; }; -use Device::Firmata::Constants qw/ :all /; - ##################################### +# default values for Attr my %sets = ( "alarm" => "", "count" => 0, ); +# default values for Get my %gets = ( "reading" => "", "state" => "", @@ -62,8 +67,7 @@ my %gets = ( "alarm" => "off" ); -sub -FRM_IN_Initialize($) +sub FRM_IN_Initialize { my ($hash) = @_; @@ -78,8 +82,7 @@ FRM_IN_Initialize($) main::LoadModule("FRM"); } -sub -FRM_IN_PinModePullupSupported($) +sub FRM_IN_PinModePullupSupported { my ($hash) = @_; my $iodev = $hash->{IODev}; @@ -88,133 +91,160 @@ FRM_IN_PinModePullupSupported($) return defined($pullupPins); } -sub -FRM_IN_Init($$) +sub FRM_IN_Init { - my ($hash,$args) = @_; - if (FRM_IN_PinModePullupSupported($hash)) { - my $pullup = AttrVal($hash->{NAME},"internal-pullup","off"); - my $ret = FRM_Init_Pin_Client($hash,$args,defined($pullup) && ($pullup eq "on")? PIN_PULLUP : PIN_INPUT); - return $ret if (defined $ret); - eval { - my $firmata = FRM_Client_FirmataDevice($hash); - my $pin = $hash->{PIN}; - $firmata->observe_digital($pin,\&FRM_IN_observer,$hash); - }; - return FRM_Catch($@) if $@; - } else { - 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; + my ($hash,$args) = @_; + my $name = $hash->{NAME}; + + if (defined($main::defs{$name}{IODev_ERROR})) { + return 'Perl module Device::Firmata not properly installed'; + } + + if (FRM_IN_PinModePullupSupported($hash)) { + my $pullup = AttrVal($name, "internal-pullup", "off"); + my $ret = FRM_Init_Pin_Client($hash,$args,defined($pullup) && ($pullup eq "on")? Device::Firmata::Constants->PIN_PULLUP : Device::Firmata::Constants->PIN_INPUT); + return $ret if (defined $ret); + eval { + my $firmata = FRM_Client_FirmataDevice($hash); + my $pin = $hash->{PIN}; + $firmata->observe_digital($pin,\&FRM_IN_observer,$hash); + }; + if ($@) { + my $ret = FRM_Catch($@); + readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1); + return $ret; + } + } else { + my $ret = FRM_Init_Pin_Client($hash, $args, Device::Firmata::Constants->PIN_INPUT); + return $ret if (defined $ret); + eval { + my $firmata = FRM_Client_FirmataDevice($hash); + my $pin = $hash->{PIN}; + if (defined(my $pullup = AttrVal($name, "internal-pullup", undef))) { + $firmata->digital_write($pin,$pullup eq "on" ? 1 : 0); + } + $firmata->observe_digital($pin,\&FRM_IN_observer,$hash); + }; + if ($@) { + my $ret = FRM_Catch($@); + readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1); + return $ret; + } + } + + if (!(defined AttrVal($name, "stateFormat", undef))) { + $main::attr{$name}{"stateFormat"} = "reading"; + } + + main::readingsSingleUpdate($hash,"state","Initialized",1); + + return undef; } -sub -FRM_IN_observer($$$$) +sub FRM_IN_observer { - my ($pin,$last,$new,$hash) = @_; - my $name = $hash->{NAME}; - my $old = ReadingsVal($name, "reading", undef); - if (defined($old)) { - $old = $old eq "on" ? PIN_HIGH : PIN_LOW; - } - if (AttrVal($hash->{NAME},"activeLow","no") eq "yes") { - $new = $new == PIN_LOW ? PIN_HIGH : PIN_LOW; - } - Log3 $name, 5, "$name observer pin: $pin, old: ".(defined $old ? $old : "--").", new: ".(defined $new ? $new : "--"); - my $changed = !defined($old) || $old != $new; - if ($changed) { - main::readingsBeginUpdate($hash); - 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", 1); - main::readingsEndUpdate($hash,1); - } + my ($pin,$last,$new,$hash) = @_; + my $name = $hash->{NAME}; + + my $old = ReadingsVal($name, "reading", undef); + if (defined($old)) { + $old = $old eq "on" ? Device::Firmata::Constants->PIN_HIGH : Device::Firmata::Constants->PIN_LOW; + } + if (AttrVal($hash->{NAME},"activeLow","no") eq "yes") { + $new = $new == Device::Firmata::Constants->PIN_LOW ? Device::Firmata::Constants->PIN_HIGH : Device::Firmata::Constants->PIN_LOW; + } + Log3 $name, 5, "$name: observer pin: $pin, old: ".(defined $old ? $old : "--").", new: ".(defined $new ? $new : "--"); + + my $changed = !defined($old) || $old != $new; + if ($changed) { + main::readingsBeginUpdate($hash); + if (defined (my $mode = main::AttrVal($name,"count-mode",undef))) { + if (($mode eq "both") + or (($mode eq "rising") and ($new == Device::Firmata::Constants->PIN_HIGH)) + or (($mode eq "falling") and ($new == Device::Firmata::Constants->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 == Device::Firmata::Constants->PIN_HIGH ? "on" : "off", 1); + main::readingsEndUpdate($hash,1); + } } -sub -FRM_IN_Set($@) +sub FRM_IN_Set { - my ($hash, @a) = @_; - return "set command missing" if(@a < 2 || !defined($a[1])); - return "unknown set command '$a[1]', choose one of " . join(" ", sort keys %sets) if(!defined($sets{$a[1]})); - my $command = $a[1]; - my $value = $a[2]; + my ($hash, $name, $cmd, @a) = @_; + + return "set command missing" if(!defined($cmd)); + return "unknown set command '$cmd', choose one of " . join(" ", sort keys %sets) if(!defined($sets{$cmd})); + return "$cmd requires 1 argument" unless (@a == 1); + + my $value = shift @a; COMMAND_HANDLER: { - $command eq "alarm" and do { + $cmd 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 { + $cmd eq "count" and do { main::readingsSingleUpdate($hash,"count",$value,1); last; }; } } -sub -FRM_IN_Get($@) +sub FRM_IN_Get { - my ($hash, @a) = @_; - return "get command missing" if(@a < 2 || !defined($a[1])); - return "unknown get command '$a[1]', choose one of " . join(":noArg ", sort keys %gets) . ":noArg" if(!defined($gets{$a[1]})); - my $name = shift @a; - my $cmd = shift @a; + my ($hash, $name, $cmd, @a) = @_; + + return "get command missing" if(!defined($cmd)); + return "unknown get command '$cmd', choose one of " . join(":noArg ", sort keys %gets) . ":noArg" if(!defined($gets{$cmd})); + ARGUMENT_HANDLER: { - ( $cmd eq "reading" ) and do { + $cmd eq "reading" and do { my $last; eval { + if (defined($main::defs{$name}{IODev_ERROR})) { + die 'Perl module Device::Firmata not properly installed'; + } $last = FRM_Client_FirmataDevice($hash)->digital_read($hash->{PIN}); if (AttrVal($hash->{NAME},"activeLow","no") eq "yes") { - $last = $last == PIN_LOW ? PIN_HIGH : PIN_LOW; + $last = $last == Device::Firmata::Constants->PIN_LOW ? Device::Firmata::Constants->PIN_HIGH : Device::Firmata::Constants->PIN_LOW; } }; - return FRM_Catch($@) if $@; - return $last == PIN_HIGH ? "on" : "off"; + if ($@) { + my $ret = FRM_Catch($@); + $hash->{STATE} = "get $cmd error: " . $ret; + return $hash->{STATE}; + } + return $last == Device::Firmata::Constants->PIN_HIGH ? "on" : "off"; }; - ( $cmd eq "count" or $cmd eq "alarm" or $cmd eq "state" ) and do { + + ($cmd eq "count" or $cmd eq "alarm" or $cmd eq "state") and do { return main::ReadingsVal($name,$cmd,$gets{$cmd}); }; } + return undef; } -sub -FRM_IN_Attr($$$$) { +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: { @@ -225,12 +255,14 @@ FRM_IN_Attr($$$$) { } 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))) { @@ -240,6 +272,7 @@ FRM_IN_Attr($$$$) { } last; }; + $attribute eq "count-threshold" and do { if (main::ReadingsVal($name,"count",0) > $value) { main::readingsBeginUpdate($hash); @@ -253,11 +286,15 @@ FRM_IN_Attr($$$$) { } last; }; + $attribute eq "internal-pullup" and do { if ($main::init_done) { + if (defined($main::defs{$name}{IODev_ERROR})) { + die 'Perl module Device::Firmata not properly installed'; + } my $firmata = FRM_Client_FirmataDevice($hash); if (FRM_IN_PinModePullupSupported($hash)) { - $firmata->pin_mode($pin,$value eq "on"? PIN_PULLUP : PIN_INPUT); + $firmata->pin_mode($pin, $value eq "on"? Device::Firmata::Constants->PIN_PULLUP : Device::Firmata::Constants->PIN_INPUT); } else { $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. @@ -265,11 +302,15 @@ FRM_IN_Attr($$$$) { } 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) { + if (defined($main::defs{$name}{IODev_ERROR})) { + die 'Perl module Device::Firmata not properly installed'; + } my $firmata = FRM_Client_FirmataDevice($hash); FRM_IN_observer($pin,undef,$firmata->digital_read($pin),$hash); } @@ -280,16 +321,23 @@ FRM_IN_Attr($$$$) { } elsif ($command eq "del") { ARGUMENT_HANDLER: { $attribute eq "internal-pullup" and do { + if (defined($main::defs{$name}{IODev_ERROR})) { + die 'Perl module Device::Firmata not properly installed'; + } my $firmata = FRM_Client_FirmataDevice($hash); if (FRM_IN_PinModePullupSupported($hash)) { - $firmata->pin_mode($pin,PIN_INPUT); + $firmata->pin_mode($pin, Device::Firmata::Constants->PIN_INPUT); } else { $firmata->digital_write($pin,0); } last; }; + $attribute eq "activeLow" and do { if (AttrVal($hash->{NAME},"activeLow","no") eq "yes") { + if (defined($main::defs{$name}{IODev_ERROR})) { + die 'Perl module Device::Firmata not properly installed'; + } delete $main::attr{$hash->{NAME}}{activeLow}; my $firmata = FRM_Client_FirmataDevice($hash); FRM_IN_observer($pin,undef,$firmata->digital_read($pin),$hash); @@ -299,9 +347,10 @@ FRM_IN_Attr($$$$) { } } }; - if (my $error = FRM_Catch($@)) { - $hash->{STATE} = "error setting $attribute to $value: ".$error; - return "cannot $command attribute $attribute to $value for $name: ".$error; + if ($@) { + my $ret = FRM_Catch($@); + $hash->{STATE} = "$command $attribute error: " . $ret; + return $hash->{STATE}; } } @@ -309,11 +358,11 @@ FRM_IN_Attr($$$$) { =pod - CHANGES +=head1 CHANGES 15.02.2019 jensb o bugfix: change detection no longer assumes that reading "reading" is defined - + 04.11.2018 jensb o bugfix: get alarm/reading/state o feature: remove unused FHEMWEB input field from all get commands @@ -324,8 +373,17 @@ FRM_IN_Attr($$$$) { 03.01.2018 jensb o implemented Firmata 2.5 feature PIN_MODE_PULLUP (requires perl-firmata 0.64 or higher) + 24.08.2020 jensb + o check for IODev install error in Init, Get and Attr + o prototypes removed + o set argument verifier improved + + 19.10.2020 jensb + o annotaded module help of attributes for FHEMWEB + =cut + =pod =head1 FHEM COMMANDREF METADATA @@ -344,7 +402,7 @@ FRM_IN_Attr($$$$) { =begin html - +

FRM_IN



+ set <name> toggle
+ toggles in between the last dimmed value, 0% and 100%. If no dimmed value was set before defaults to pulsewidth 50% on all channels

+ set <name> rgb <value>
+ sets the pulse-width of all channels at once. Also sets the value toggle can switch to
+ Value is encoded as hex-string, 2-digigs per channel (e.g. FFFFFF for reguler rgb)

+ set <name> pct <value>
+ dims all channels at once while leving the ratio in between the channels unaltered.
+ Range is 0-100 ('pct' stands for 'percent')

+ set <name> dimUp
+ dims up by 10%

- + set <name> dimDown
+ dims down by 10% +
+ + Get



-
+ + Attributes
+ +
  • restoreOnStartup <on|off>
  • + +
    +
  • restoreOnReconnect <on|off>
  • + +
    +
  • IODev
    + Specify which FRM to use. Only required if there is more than one FRM-device defined. +
  • + +
  • global attributes
  • + +
  • readingFnAttributes
  • -
    +
    =end html + +=begin html_DE + + +

    FRM_RGB

    +

    + +=end html_DE + =cut diff --git a/fhem/FHEM/20_FRM_ROTENC.pm b/fhem/FHEM/20_FRM_ROTENC.pm index 753316185..83e5ad932 100755 --- a/fhem/FHEM/20_FRM_ROTENC.pm +++ b/fhem/FHEM/20_FRM_ROTENC.pm @@ -1,6 +1,42 @@ -############################################## +######################################################################################## # $Id$ -############################################## +######################################################################################## + +=encoding UTF-8 + +=head1 NAME + +FHEM module for two Firmata rotary encoder input pins + +=head1 LICENSE AND COPYRIGHT + +Copyright (C) 2013 ntruchess +Copyright (C) 2020 jensb + +All rights reserved + +This script 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. + +This script 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 this script; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +A copy of the GNU General Public License, Version 2 can also be found at + +http://www.gnu.org/licenses/old-licenses/gpl-2.0. + +This copyright notice MUST APPEAR in all copies of the script! + +=cut + package main; use strict; @@ -8,37 +44,35 @@ 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"; - }; - }; + if (!grep(/FHEM\/lib$/,@INC)) { + foreach my $inc (grep(/FHEM$/,@INC)) { + push @INC,$inc."/lib"; + }; + }; }; -use Device::Firmata::Constants qw/ :all /; - ##################################### +# number of arguments my %sets = ( - "reset" => "noArg", - "offset"=> "", + "reset:noArg" => 0, + "offset" => 1, ); my %gets = ( - "position" => "noArg", - "offset" => "noArg", - "value" => "noArg", + "position" => "", + "offset" => "", + "value" => "", ); -sub -FRM_ROTENC_Initialize($) +sub FRM_ROTENC_Initialize { my ($hash) = @_; $hash->{SetFn} = "FRM_ROTENC_Set"; $hash->{GetFn} = "FRM_ROTENC_Get"; $hash->{AttrFn} = "FRM_ROTENC_Attr"; - $hash->{DefFn} = "FRM_Client_Define"; + $hash->{DefFn} = "FRM_ROTENC_Define"; $hash->{InitFn} = "FRM_ROTENC_Init"; $hash->{UndefFn} = "FRM_ROTENC_Undef"; $hash->{StateFn} = "FRM_ROTENC_State"; @@ -47,73 +81,87 @@ FRM_ROTENC_Initialize($) main::LoadModule("FRM"); } -sub -FRM_ROTENC_Init($$) +sub FRM_ROTENC_Define { - my ($hash,$args) = @_; + my ($hash, $def) = @_; - my $u = "wrong syntax: define FRM_ROTENC pinA pinB [id]"; - return $u unless defined $args and int(@$args) > 1; - my $pinA = @$args[0]; - my $pinB = @$args[1]; - my $encoder = defined @$args[2] ? @$args[2] : 0; - my $name = $hash->{NAME}; - - $hash->{PINA} = $pinA; - $hash->{PINB} = $pinB; - - $hash->{ENCODERNUM} = $encoder; - - eval { - FRM_Client_AssignIOPort($hash); - my $firmata = FRM_Client_FirmataDevice($hash); - $firmata->encoder_attach($encoder,$pinA,$pinB); - $firmata->observe_encoder($encoder, \&FRM_ROTENC_observer, $hash ); - }; - if ($@) { - $@ =~ /^(.*)( at.*FHEM.*)$/; - $hash->{STATE} = "error initializing: ".$1; - return "error initializing '$name': $1"; - } + # verify define arguments + my $usage = "usage: define FRM_ROTENC pinA pinB [id]"; - if (! (defined AttrVal($name,"stateFormat",undef))) { - $main::attr{$name}{"stateFormat"} = "position"; - } + my @a = split("[ \t]+", $def); + return $usage if (scalar(@a) < 4); + my $args = [@a[2..scalar(@a)-1]]; + + $hash->{PINA} = @$args[0]; + $hash->{PINB} = @$args[1]; + + $hash->{ENCODERNUM} = defined @$args[2] ? @$args[2] : 0; + + my $ret = FRM_Client_Define($hash, $def); + if ($ret) { + return $ret; + } + return undef; +} + +sub FRM_ROTENC_Init +{ + my ($hash,$args) = @_; + my $name = $hash->{NAME}; + + if (defined($main::defs{$name}{IODev_ERROR})) { + return 'Perl module Device::Firmata not properly installed'; + } + + eval { + FRM_Client_AssignIOPort($hash); + my $firmata = FRM_Client_FirmataDevice($hash); + $firmata->encoder_attach($hash->{ENCODERNUM}, $hash->{PINA}, $hash->{PINB}); + $firmata->observe_encoder($hash->{ENCODERNUM}, \&FRM_ROTENC_observer, $hash ); + }; + if ($@) { + my $ret = FRM_Catch($@); + readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1); + return $ret; + } + + if (! (defined AttrVal($name,"stateFormat",undef))) { + $main::attr{$name}{"stateFormat"} = "position"; + } $hash->{offset} = ReadingsVal($name,"position",0); - main::readingsSingleUpdate($hash,"state","Initialized",1); - return undef; + main::readingsSingleUpdate($hash,"state","Initialized",1); + + return undef; } -sub -FRM_ROTENC_observer +sub FRM_ROTENC_observer { - my ( $encoder, $value, $hash ) = @_; - my $name = $hash->{NAME}; - Log3 ($name,5,"onEncoderMessage for pins ".$hash->{PINA}.",".$hash->{PINB}." encoder: ".$encoder." position: ".$value."\n"); - main::readingsBeginUpdate($hash); - main::readingsBulkUpdate($hash,"position",$value+$hash->{offset}, 1); - main::readingsBulkUpdate($hash,"value",$value, 1); - main::readingsEndUpdate($hash,1); + my ($encoder, $value, $hash) = @_; + my $name = $hash->{NAME}; + Log3 ($name, 5, "$name: observer pins: ".$hash->{PINA}.", ".$hash->{PINB}." encoder: ".$encoder." position: ".$value."\n"); + main::readingsBeginUpdate($hash); + main::readingsBulkUpdate($hash,"position",$value+$hash->{offset}, 1); + main::readingsBulkUpdate($hash,"value",$value, 1); + main::readingsEndUpdate($hash,1); } -sub -FRM_ROTENC_Set +sub FRM_ROTENC_Set { - my ($hash, @a) = @_; - return "Need at least one parameters" if(@a < 2); - my $command = $a[1]; - my $value = $a[2]; - if(!defined($sets{$command})) { - my @commands = (); - foreach my $key (sort keys %sets) { - push @commands, $sets{$key} ? $key.":".join(",",$sets{$key}) : $key; - } - return "Unknown argument $a[1], choose one of " . join(" ", @commands); - } - COMMAND_HANDLER: { - $command eq "reset" and do { + my ($hash, $name, $cmd, @a) = @_; + + return "set command missing" if(!defined($cmd)); + my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets ); + return "unknown set command '$cmd', choose one of " . join(" ", sort keys %sets) if ($cmd eq '?' || @match == 0); + return "$cmd requires $sets{$match[0]} argument(s)" unless (@a == $sets{$match[0]}); + + my $value = shift @a; + SETHANDLER: { + $cmd eq "reset" and do { + if (defined($main::defs{$name}{IODev_ERROR})) { + return 'Perl module Device::Firmata not properly installed'; + } eval { FRM_Client_FirmataDevice($hash)->encoder_reset_position($hash->{ENCODERNUM}); }; @@ -123,46 +171,37 @@ FRM_ROTENC_Set main::readingsEndUpdate($hash,1); last; }; - $command eq "offset" and do { + $cmd eq "offset" and do { $hash->{offset} = $value; - readingsSingleUpdate($hash,"position",ReadingsVal($hash->{NAME},"value",0)+$value,1); + readingsSingleUpdate($hash,"position",ReadingsVal($name,"value",0)+$value,1); last; }; } } -sub -FRM_ROTENC_Get($) +sub FRM_ROTENC_Get { - my ($hash, @a) = @_; - return "Need at least one parameters" if(@a < 2); - my $command = $a[1]; - my $value = $a[2]; - if(!defined($gets{$command})) { - my @commands = (); - foreach my $key (sort keys %gets) { - push @commands, $gets{$key} ? $key.":".join(",",$gets{$key}) : $key; - } - return "Unknown argument $a[1], choose one of " . join(" ", @commands); - } - my $name = shift @a; - my $cmd = shift @a; - ARGUMENT_HANDLER: { + my ($hash, $name, $cmd, @a) = @_; + + return "get command missing" if(!defined($cmd)); + return "unknown get command '$cmd', choose one of " . join(":noArg ", sort keys %gets) . ":noArg" if(!defined($gets{$cmd})); + + GETHANDLER: { $cmd eq "position" and do { - return ReadingsVal($hash->{NAME},"position","0"); + return ReadingsVal($name,"position","0"); }; $cmd eq "offset" and do { return $hash->{offset}; }; $cmd eq "value" and do { - return ReadingsVal($hash->{NAME},"value","0"); + return ReadingsVal($name,"value","0"); }; } return undef; } -sub -FRM_ROTENC_Attr($$$$) { +sub FRM_ROTENC_Attr +{ my ($command,$name,$attribute,$value) = @_; my $hash = $main::defs{$name}; eval { @@ -179,38 +218,32 @@ FRM_ROTENC_Attr($$$$) { } }; if ($@) { - $@ =~ /^(.*)( at.*FHEM.*)$/; - $hash->{STATE} = "error setting $attribute to $value: ".$1; - return "cannot $command attribute $attribute to $value for $name: ".$1; + my $ret = FRM_Catch($@); + $hash->{STATE} = "$command $attribute error: " . $ret; + return $hash->{STATE}; } } -sub -FRM_ROTENC_Undef($$) +sub FRM_ROTENC_Undef { my ($hash, $name) = @_; + my $pinA = $hash->{PINA}; my $pinB = $hash->{PINB}; eval { my $firmata = FRM_Client_FirmataDevice($hash); $firmata->encoder_detach($hash->{ENCODERNUM}); - $firmata->pin_mode($pinA,PIN_ANALOG); - $firmata->pin_mode($pinB,PIN_ANALOG); }; - if ($@) { - eval { - my $firmata = FRM_Client_FirmataDevice($hash); - $firmata->pin_mode($pinA,PIN_INPUT); - $firmata->digital_write($pinA,0); - $firmata->pin_mode($pinB,PIN_INPUT); - $firmata->digital_write($pinB,0); - }; - } + + $hash->{PIN} = $hash->{PINA}; + FRM_Client_Undef($hash, $name); + $hash->{PIN} = $hash->{PINB}; + FRM_Client_Undef($hash, $name); + return undef; } -sub -FRM_ROTENC_State($$$$) +sub FRM_ROTENC_State { my ($hash, $tim, $sname, $sval) = @_; if ($sname eq "position") { @@ -222,30 +255,64 @@ FRM_ROTENC_State($$$$) 1; =pod + +=head1 CHANGES + + 05.09.2020 jensb + o check for IODev install error in Init, Set and Undef + o prototypes removed + o set argument verifier improved + o moved define argument verification and decoding from Init to Define + + 19.10.2020 jensb + o annotaded module help of attributes for FHEMWEB + +=cut + + +=pod + +=head1 FHEM COMMANDREF METADATA + +=over + +=item device + +=item summary Firmata: rotary encoder input + +=item summary_DE Firmata: Drehgeber Eingang + +=back + +=head1 INSTALLATION AND CONFIGURATION + =begin html - +

    FRM_ROTENC

    -
    +
    =end html + +=begin html_DE + + +

    FRM_ROTENC

    +

    + +=end html_DE + =cut diff --git a/fhem/FHEM/20_FRM_SERVO.pm b/fhem/FHEM/20_FRM_SERVO.pm index bedb4486f..720bfb8fb 100755 --- a/fhem/FHEM/20_FRM_SERVO.pm +++ b/fhem/FHEM/20_FRM_SERVO.pm @@ -1,6 +1,42 @@ -############################################## +######################################################################################## # $Id$ -############################################## +######################################################################################## + +=encoding UTF-8 + +=head1 NAME + +FHEM module for one Firmata PMW controlled servo output + +=head1 LICENSE AND COPYRIGHT + +Copyright (C) 2013 ntruchess +Copyright (C) 2020 jensb + +All rights reserved + +This script 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. + +This script 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 this script; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +A copy of the GNU General Public License, Version 2 can also be found at + +http://www.gnu.org/licenses/old-licenses/gpl-2.0. + +This copyright notice MUST APPEAR in all copies of the script! + +=cut + package main; use strict; @@ -8,23 +44,21 @@ 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"; - }; - }; + if (!grep(/FHEM\/lib$/,@INC)) { + foreach my $inc (grep(/FHEM$/,@INC)) { + push @INC,$inc."/lib"; + }; + }; }; -use Device::Firmata::Constants qw/ :all /; - ##################################### +# number of arguments my %sets = ( - "angle" => "", + "angle" => 1, ); -sub -FRM_SERVO_Initialize($) +sub FRM_SERVO_Initialize { my ($hash) = @_; @@ -33,29 +67,44 @@ FRM_SERVO_Initialize($) $hash->{InitFn} = "FRM_SERVO_Init"; $hash->{UndefFn} = "FRM_Client_Undef"; $hash->{AttrFn} = "FRM_SERVO_Attr"; - + $hash->{AttrList} = "min-pulse max-pulse IODev $main::readingFnAttributes"; main::LoadModule("FRM"); } -sub -FRM_SERVO_Init($$) +sub FRM_SERVO_Init { my ($hash,$args) = @_; - my $ret = FRM_Init_Pin_Client($hash,$args,PIN_SERVO); - return $ret if (defined $ret); + my $name = $hash->{NAME}; + + if (defined($main::defs{$name}{IODev_ERROR})) { + return 'Perl module Device::Firmata not properly installed'; + } + + my $ret = FRM_Init_Pin_Client($hash,$args,Device::Firmata::Constants->PIN_SERVO); + if (defined($ret)) { + readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1); + return $ret; + } + eval { my $firmata = FRM_Client_FirmataDevice($hash); $hash->{resolution}=$firmata->{metadata}{servo_resolutions}{$hash->{PIN}} if (defined $firmata->{metadata}{servo_resolutions}); FRM_SERVO_apply_attribute($hash,"max-pulse"); #sets min-pulse as well }; - return FRM_Catch($@) if $@; + if ($@) { + $ret = FRM_Catch($@); + readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1); + return $ret; + } + main::readingsSingleUpdate($hash,"state","Initialized",1); + return undef; } -sub -FRM_SERVO_Attr($$$$) { +sub FRM_SERVO_Attr +{ my ($command,$name,$attribute,$value) = @_; my $hash = $main::defs{$name}; eval { @@ -70,7 +119,10 @@ FRM_SERVO_Attr($$$$) { }; ($attribute eq "min-pulse" || $attribute eq "max-pulse") and do { if ($main::init_done) { - $main::attr{$name}{$attribute}=$value; + if (defined($main::defs{$name}{IODev_ERROR})) { + die 'Perl module Device::Firmata not properly installed'; + } + $main::attr{$name}{$attribute}=$value; FRM_SERVO_apply_attribute($hash,$attribute); } last; @@ -78,15 +130,15 @@ FRM_SERVO_Attr($$$$) { } } }; - my $ret = FRM_Catch($@) if $@; - if ($ret) { - $hash->{STATE} = "error setting $attribute to $value: ".$ret; - return "cannot $command attribute $attribute to $value for $name: ".$ret; + if ($@) { + my $ret = FRM_Catch($@); + $hash->{STATE} = "$command $attribute error: " . $ret; + return $hash->{STATE}; } - return undef; } -sub FRM_SERVO_apply_attribute { +sub FRM_SERVO_apply_attribute +{ my ($hash,$attribute) = @_; if ( $attribute eq "min-pulse" || $attribute eq "max-pulse" ) { my $name = $hash->{NAME}; @@ -95,69 +147,130 @@ sub FRM_SERVO_apply_attribute { } } -sub -FRM_SERVO_Set($@) +sub FRM_SERVO_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]; + my ($hash, $name, $cmd, @a) = @_; + + return "set command missing" if(!defined($cmd)); + my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets ); + return "unknown set command '$cmd', choose one of " . join(" ", sort keys %sets) if ($cmd eq '?' || @match == 0); + return "$cmd requires $sets{$match[0]} argument" unless (@a == $sets{$match[0]}); + + if (defined($main::defs{$name}{IODev_ERROR})) { + return 'Perl module Device::Firmata not properly installed'; + } + + my $value = shift @a; eval { FRM_Client_FirmataDevice($hash)->servo_write($hash->{PIN},$value); main::readingsSingleUpdate($hash,"state",$value, 1); }; - return $@; + if ($@) { + my $ret = FRM_Catch($@); + $hash->{STATE} = "set $cmd error: " . $ret; + return $hash->{STATE}; + } + + return undef; } 1; =pod + +=head1 CHANGES + + 05.09.2020 jensb + o check for IODev install error in Init and Set + o prototypes removed + o set argument verifier improved + + 19.10.2020 jensb + o annotaded module help of attributes for FHEMWEB + +=cut + + +=pod + +=head1 FHEM COMMANDREF METADATA + +=over + +=item device + +=item summary Firmata: PWM controlled servo output + +=item summary_DE Firmata: PWM gesteuerter Servo Ausgang + +=back + +=head1 INSTALLATION AND CONFIGURATION + =begin html - +

    FRM_SERVO

    -
    +
    =end html + +=begin html_DE + + +

    FRM_SERVO

    +

    + +=end html_DE + =cut diff --git a/fhem/FHEM/20_FRM_STEPPER.pm b/fhem/FHEM/20_FRM_STEPPER.pm index 30dbb942d..bdbaed7df 100755 --- a/fhem/FHEM/20_FRM_STEPPER.pm +++ b/fhem/FHEM/20_FRM_STEPPER.pm @@ -1,6 +1,42 @@ -############################################## +######################################################################################## # $Id$ -############################################## +######################################################################################## + +=encoding UTF-8 + +=head1 NAME + +FHEM module for two/four Firmata stepper motor output pins + +=head1 LICENSE AND COPYRIGHT + +Copyright (C) 2013 ntruchess +Copyright (C) 2020 jensb + +All rights reserved + +This script 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. + +This script 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 this script; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +A copy of the GNU General Public License, Version 2 can also be found at + +http://www.gnu.org/licenses/old-licenses/gpl-2.0. + +This copyright notice MUST APPEAR in all copies of the script! + +=cut + package main; use strict; @@ -8,136 +44,151 @@ 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"; - }; - }; + if (!grep(/FHEM\/lib$/,@INC)) { + foreach my $inc (grep(/FHEM$/,@INC)) { + push @INC,$inc."/lib"; + }; + }; }; -use Device::Firmata::Constants qw/ :all /; - ##################################### +# (min) number of arguments my %sets = ( - "reset" => "noArg", - "position" => "", - "step" => "", + "reset:noArg" => 0, + "position" => 1, + "step" => 1, ); my %gets = ( - "position" => "noArg", + "position" => "", ); -sub -FRM_STEPPER_Initialize($) +sub FRM_STEPPER_Initialize { my ($hash) = @_; $hash->{SetFn} = "FRM_STEPPER_Set"; $hash->{GetFn} = "FRM_STEPPER_Get"; - $hash->{DefFn} = "FRM_Client_Define"; + $hash->{DefFn} = "FRM_STEPPER_Define"; $hash->{InitFn} = "FRM_STEPPER_Init"; $hash->{UndefFn} = "FRM_Client_Undef"; $hash->{AttrFn} = "FRM_STEPPER_Attr"; $hash->{StateFn} = "FRM_STEPPER_State"; - + $hash->{AttrList} = "restoreOnReconnect:on,off restoreOnStartup:on,off speed acceleration deceleration IODev $main::readingFnAttributes"; main::LoadModule("FRM"); } -sub -FRM_STEPPER_Init($$) +sub FRM_STEPPER_Define { - my ($hash,$args) = @_; + my ($hash, $def) = @_; - my $u = "wrong syntax: define FRM_STEPPER [DRIVER|TWO_WIRE|FOUR_WIRE] directionPin stepPin [motorPin3 motorPin4] stepsPerRev [id]"; - return $u unless defined $args; - - my $driver = shift @$args; - - return $u unless ( $driver eq 'DRIVER' or $driver eq 'TWO_WIRE' or $driver eq 'FOUR_WIRE' ); - return $u if (($driver eq 'DRIVER' or $driver eq 'TWO_WIRE') and (scalar(@$args) < 3 or scalar(@$args) > 4)); - return $u if (($driver eq 'FOUR_WIRE') and (scalar(@$args) < 5 or scalar(@$args) > 6)); - - $hash->{DRIVER} = $driver; - - $hash->{PIN1} = shift @$args; - $hash->{PIN2} = shift @$args; - - if ($driver eq 'FOUR_WIRE') { - $hash->{PIN3} = shift @$args; - $hash->{PIN4} = shift @$args; - } - - $hash->{STEPSPERREV} = shift @$args; - $hash->{STEPPERNUM} = shift @$args; - - eval { - FRM_Client_AssignIOPort($hash); - my $firmata = FRM_Client_FirmataDevice($hash); - $firmata->stepper_config( - $hash->{STEPPERNUM}, - $driver, - $hash->{STEPSPERREV}, - $hash->{PIN1}, - $hash->{PIN2}, - $hash->{PIN3}, - $hash->{PIN4}); - $firmata->observe_stepper(0, \&FRM_STEPPER_observer, $hash ); - }; - if ($@) { - $@ =~ /^(.*)( at.*FHEM.*)$/; - $hash->{STATE} = "error initializing: ".$1; - return "error initializing '".$hash->{NAME}."': ".$1; - } - $hash->{POSITION} = 0; - $hash->{DIRECTION} = 0; - $hash->{STEPS} = 0; - if (! (defined AttrVal($hash->{NAME},"stateFormat",undef))) { - $main::attr{$hash->{NAME}}{"stateFormat"} = "position"; - } - main::readingsSingleUpdate($hash,"state","Initialized",1); - return undef; -} + # verify define arguments + my $usage = "usage: define FRM_STEPPER [DRIVER|TWO_WIRE|FOUR_WIRE] directionPin stepPin [motorPin3 motorPin4] stepsPerRev [id]"; -sub -FRM_STEPPER_observer -{ - my ( $stepper, $hash ) = @_; - my $name = $hash->{NAME}; - Log3 $name,5,"onStepperMessage for pins ".$hash->{PIN1}.",".$hash->{PIN2}.(defined ($hash->{PIN3}) ? ",".$hash->{PIN3} : ",-").(defined ($hash->{PIN4}) ? ",".$hash->{PIN4} : ",-")." stepper: ".$stepper; - my $position = $hash->{DIRECTION} ? $hash->{POSITION} - $hash->{STEPS} : $hash->{POSITION} + $hash->{STEPS}; - $hash->{POSITION} = $position; - $hash->{DIRECTION} = 0; - $hash->{STEPS} = 0; - main::readingsSingleUpdate($hash,"position",$position,1); -} + my @a = split("[ \t][ \t]*", $def); + my $args = [@a[2..scalar(@a)-1]]; + return $usage unless defined $args; -sub -FRM_STEPPER_Set -{ - my ($hash, @a) = @_; - return "Need at least one parameters" if(@a < 2); - shift @a; - my $name = $hash->{NAME}; - 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 $driver = shift @$args; + return $usage unless ( $driver eq 'DRIVER' or $driver eq 'TWO_WIRE' or $driver eq 'FOUR_WIRE' ); + return $usage if (($driver eq 'DRIVER' or $driver eq 'TWO_WIRE') and (scalar(@$args) < 3 or scalar(@$args) > 4)); + return $usage if (($driver eq 'FOUR_WIRE') and (scalar(@$args) < 5 or scalar(@$args) > 6)); + + $hash->{DRIVER} = $driver; + + $hash->{PIN1} = shift @$args; + $hash->{PIN2} = shift @$args; + + if ($driver eq 'FOUR_WIRE') { + $hash->{PIN3} = shift @$args; + $hash->{PIN4} = shift @$args; } - COMMAND_HANDLER: { - $command eq "reset" and do { + + $hash->{STEPSPERREV} = shift @$args; + $hash->{STEPPERNUM} = shift @$args; + + my $ret = FRM_Client_Define($hash, $def); + if ($ret) { + return $ret; + } + return undef; +} + +sub FRM_STEPPER_Init +{ + my ($hash,$args) = @_; + my $name = $hash->{NAME}; + + if (defined($main::defs{$name}{IODev_ERROR})) { + return 'Perl module Device::Firmata not properly installed'; + } + + eval { + FRM_Client_AssignIOPort($hash); + my $firmata = FRM_Client_FirmataDevice($hash); + $firmata->stepper_config( + $hash->{STEPPERNUM}, + $hash->{DRIVER}, + $hash->{STEPSPERREV}, + $hash->{PIN1}, + $hash->{PIN2}, + $hash->{PIN3}, + $hash->{PIN4}); + $firmata->observe_stepper(0, \&FRM_STEPPER_observer, $hash ); + }; + if ($@) { + my $ret = FRM_Catch($@); + readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1); + return $ret; + } + + $hash->{POSITION} = 0; + $hash->{DIRECTION} = 0; + $hash->{STEPS} = 0; + if (! (defined AttrVal($name,"stateFormat",undef))) { + $main::attr{$name}{"stateFormat"} = "position"; + } + + main::readingsSingleUpdate($hash,"state","Initialized",1); + + return undef; +} + +sub FRM_STEPPER_observer +{ + my ( $stepper, $hash ) = @_; + my $name = $hash->{NAME}; + Log3 $name, 5, "$name: observer pins: ".$hash->{PIN1}.",".$hash->{PIN2}.(defined ($hash->{PIN3}) ? ",".$hash->{PIN3} : ",-").(defined ($hash->{PIN4}) ? ",".$hash->{PIN4} : ",-")." stepper: ".$stepper; + my $position = $hash->{DIRECTION} ? $hash->{POSITION} - $hash->{STEPS} : $hash->{POSITION} + $hash->{STEPS}; + $hash->{POSITION} = $position; + $hash->{DIRECTION} = 0; + $hash->{STEPS} = 0; + main::readingsSingleUpdate($hash,"position",$position,1); +} + +sub FRM_STEPPER_Set +{ + my ($hash, $name, $cmd, @a) = @_; + + return "set command missing" if(!defined($cmd)); + my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets ); + return "unknown set command '$cmd', choose one of " . join(" ", sort keys %sets) if ($cmd eq '?' || @match == 0); + return "$cmd requires (at least) $sets{$match[0]} argument(s)" unless (@a >= $sets{$match[0]}); + + my $value = shift @a; + SETHANDLER: { + $cmd eq "reset" and do { $hash->{POSITION} = 0; main::readingsSingleUpdate($hash,"position",0,1); last; }; - $command eq "position" and do { + $cmd eq "position" and do { + if (defined($main::defs{$name}{IODev_ERROR})) { + return 'Perl module Device::Firmata not properly installed'; + } my $position = $hash->{POSITION}; - my $value = shift @a; my $direction = $value < $position ? 1 : 0; my $steps = $direction ? $position - $value : $value - $position; my $speed = shift @a; @@ -149,13 +200,19 @@ FRM_STEPPER_Set $hash->{DIRECTION} = $direction; $hash->{STEPS} = $steps; eval { - # $stepperNum, $direction, $numSteps, $stepSpeed, $accel, $decel FRM_Client_FirmataDevice($hash)->stepper_step($hash->{STEPPERNUM},$direction,$steps,$speed,$accel,$decel); }; + if ($@) { + my $ret = FRM_Catch($@); + $hash->{STATE} = "set $cmd error: " . $ret; + return $hash->{STATE}; + } last; }; - $command eq "step" and do { - my $value = shift @a; + $cmd eq "step" and do { + if (defined($main::defs{$name}{IODev_ERROR})) { + return 'Perl module Device::Firmata not properly installed'; + } my $direction = $value < 0 ? 1 : 0; my $steps = abs $value; my $speed = shift @a; @@ -167,42 +224,53 @@ FRM_STEPPER_Set $hash->{DIRECTION} = $direction; $hash->{STEPS} = $steps; eval { - # $stepperNum, $direction, $numSteps, $stepSpeed, $accel, $decel FRM_Client_FirmataDevice($hash)->stepper_step($hash->{STEPPERNUM},$direction,$steps,$speed,$accel,$decel); }; + if ($@) { + my $ret = FRM_Catch($@); + $hash->{STATE} = "set $cmd error: " . $ret; + return $hash->{STATE}; + } last; }; } + + return undef; } -sub -FRM_STEPPER_Get +sub FRM_STEPPER_Get { - my ($hash, @a) = @_; - return "Need at least one parameters" if(@a < 2); - shift @a; - my $name = $hash->{NAME}; - my $command = shift @a; - return "Unknown argument $command, choose one of " . join(" ", sort keys %gets) unless defined($gets{$command}); + my ($hash, $name, $cmd, @a) = @_; + + return "get command missing" if(!defined($cmd)); + return "unknown get command '$cmd', choose one of " . join(":noArg ", sort keys %gets) . ":noArg" if(!defined($gets{$cmd})); + + GETHANDLER: { + $cmd eq 'position' and do { + return $hash->{POSITION}; + }; + } + + return undef; } -sub FRM_STEPPER_State($$$$) +sub FRM_STEPPER_State { - my ($hash, $tim, $sname, $sval) = @_; - -STATEHANDLER: { - $sname eq "value" and do { - if (AttrVal($hash->{NAME},"restoreOnStartup","on") eq "on") { - FRM_STEPPER_Set($hash,$hash->{NAME},$sval); - } - last; - } - } + my ($hash, $tim, $sname, $sval) = @_; + + STATEHANDLER: { + $sname eq "value" and do { + if (AttrVal($hash->{NAME},"restoreOnStartup","on") eq "on") { + FRM_STEPPER_Set($hash,$hash->{NAME},$sval); + } + last; + } + } } -sub -FRM_STEPPER_Attr($$$$) { +sub FRM_STEPPER_Attr +{ my ($command,$name,$attribute,$value) = @_; my $hash = $main::defs{$name}; eval { @@ -219,115 +287,183 @@ FRM_STEPPER_Attr($$$$) { } }; if ($@) { - $@ =~ /^(.*)( at.*FHEM.*)$/; - $hash->{STATE} = "error setting $attribute to $value: ".$1; - return "cannot $command attribute $attribute to $value for $name: ".$1; + my $ret = FRM_Catch($@); + $hash->{STATE} = "$command $attribute error: " . $ret; + return $hash->{STATE}; } } 1; =pod + +=head1 CHANGES + + 05.09.2020 jensb + o check for IODev install error in Init and Set + o prototypes removed + o get position implemented + o set argument verifier improved + o module help updated + o moved define argument verification and decoding from Init to Define + + 22.10.2020 jensb + o annotaded module help of attributes for FHEMWEB + +=cut + + +=pod + +=head1 FHEM COMMANDREF METADATA + +=over + +=item device + +=item summary Firmata: rotary encoder input + +=item summary_DE Firmata: Drehgeber Eingang + +=back + +=head1 INSTALLATION AND CONFIGURATION + =begin html

    FRM_STEPPER

      - represents a stepper-motor attached to digital-i/o pins of an Arduino running Firmata
      - Requires a defined FRM-device to work.

      - + represents a stepper-motor attached to digital-i/o pins of an Arduino + running Firmata
      + Requires a defined FRM-device to work.

      + Define
        - define <name> FRM_STEPPER [DRIVER|TWO_WIRE|FOUR_WIRE] <directionPin> <stepPin> [motorPin3 motorPin4] stepsPerRev [stepper-id]
        - Defines the FRM_STEPPER device. -
      • [DRIVER|TWO_WIRE|FOUR_WIRE] defines the control-sequence being used to drive the motor. -
          -
        • DRIVER: motor is attached via a smart circuit that is controlled via two lines: 1 line defines the direction to turn, the other triggers one step per impluse.
        • -
        • FOUR_WIRE: motor is attached via four wires each driving one coil individually.
        • -
        • TWO_WIRE: motor is attached via two wires. This mode makes use of the fact that at any time two of the four motor -coils are the inverse of the other two so by using an inverting circuit to drive the motor the number of control connections can be reduced from 4 to 2.
        • -
        -
      • -
      • -
          -
        • The sequence of control signals for 4 control wires is as follows:
          -
          - -Step C0 C1 C2 C3
          - 1 1 0 1 0
          - 2 0 1 1 0
          - 3 0 1 0 1
          - 4 1 0 0 1
          -
          -
        • -
        • The sequence of controls signals for 2 control wires is as follows:
          -(columns C1 and C2 from above):
          -
          - -Step C0 C1
          - 1 0 1
          - 2 1 1
          - 3 1 0
          - 4 0 0
          -
          -
        • -
        -
      • -
      • - If your stepper-motor does not move or does move but only in a single direction you will have to rearrage the pin-numbers to match the control sequence.
        - that can be archived either by rearranging the physical connections, or by mapping the connection to the pin-definitions in FRM_STEPPERS define:
        - e.g. the widely used cheap 28byj-48 you can get for few EUR on eBay including a simple ULN2003 driver interface may be defined by
        - define stepper FRM_STEPPER FOUR_WIRE 7 5 6 8 64 0
        - when being connected to the arduio with:
        - motor pin1 <-> arduino pin5
        - motor pin2 <-> arduino pin6
        - motor pin3 <-> arduino pin7
        - motor pin4 <-> arduino pin8
        - motor pin5 <-> ground

        -
      • -
      - -
      + define <name> FRM_STEPPER [DRIVER|TWO_WIRE|FOUR_WIRE] <directionPin> <stepPin> [motorPin3 motorPin4] stepsPerRev [stepper-id]
      + Defines the FRM_STEPPER device. +
    • [DRIVER|TWO_WIRE|FOUR_WIRE] defines the control-sequence being used to drive the motor. +
        +
      • DRIVER: motor is attached via a smart circuit that is controlled via two lines: 1 line defines the + direction to turn, the other triggers one step per impluse. +
      • +
      • FOUR_WIRE: motor is attached via four wires each driving one coil individually.
      • +
      • TWO_WIRE: motor is attached via two wires. This mode makes use of the fact that at any time two of + the four motor coils are the inverse of the other two so by using an inverting circuit to drive the motor + the number of control connections can be reduced from 4 to 2. +
      • +
      +
    • +
    • +
        +
      • The sequence of control signals for 4 control wires is as follows:

        + + + Step C0 C1 C2 C3
        + 1 1 0 1 0
        + 2 0 1 1 0
        + 3 0 1 0 1
        + 4 1 0 0 1
        +
        +
      • +
      • The sequence of controls signals for 2 control wires is as follows:
        + (columns C1 and C2 from above):

        + + + Step C0 C1
        + 1 0 1
        + 2 1 1
        + 3 1 0
        + 4 0 0
        +
        +
      • +
      +
    • +
    • + If your stepper-motor does not move or does move but only in a single direction you will have to rearrage + the pin-numbers to match the control sequence. That can be archived either by rearranging the physical + connections, or by mapping the connection to the pin-definitions in FRM_STEPPERS define:
      + e.g. the widely used cheap 28byj-48 you can get for few EUR on eBay including a simple ULN2003 driver + interface may be defined by
      + define stepper FRM_STEPPER FOUR_WIRE 7 5 6 8 64 0
      + when being connected to the arduio with:

      + + + motor pin1 <-> arduino pin5
      + motor pin2 <-> arduino pin6
      + motor pin3 <-> arduino pin7
      + motor pin4 <-> arduino pin8
      + motor pin5 <-> ground +
      +

    • +

    + Set
      - set <name> reset -
    • resets the reading 'position' to 0 without moving the motor
    • -
      - set <name> position <position> [speed] [acceleration] [deceleration] -
    • moves the motor to the absolute position specified. positive or negative integer
      - speed (10 * revolutions per minute, optional), defaults to 30, higher numbers are faster) At 2048 steps per revolution (28byj-48) a speed of 30 results in 3 rev/min
      - acceleration and deceleration are optional.
      -
    • -
      - set <name> step <stepstomove> [speed] [accel] [decel] -
    • moves the motor the number of steps specified. positive or negative integer
      - speed, accelleration and deceleration are optional.
      -
    • -
    + set <name> reset +
  • resets the reading 'position' to 0 without moving the motor

  • + + set <name> position <position> [speed] [acceleration] [deceleration] +
  • moves the motor to the absolute position specified. positive or negative integer
    + speed (10 * revolutions per minute, optional), defaults to 30, higher numbers are faster. + At 2048 steps per revolution (28byj-48) a speed of 30 results in 3 rev/min
    + acceleration and deceleration are optional.
    +

  • + + set <name> step <stepstomove> [speed] [accel] [decel] +
  • moves the motor the number of steps specified. positive or negative integer
    + speed, accelleration and deceleration are optional.
    +
  • +
    + Get
      - N/A + get <position> +
    • returns the current position value

    + Attributes
      -
    • restoreOnStartup <on|off>
    • -
    • restoreOnReconnect <on|off>
    • -
    • IODev
      - Specify which FRM to use. (Optional, only required if there is more - than one FRM-device defined.) -
    • -
    • >speed (same meaning as in 'set position')
    • -
    • acceleration (same meaning as in 'set position')
    • -
    • deceleration (same meaning as in 'set position')
    • -
    • eventMap
    • -
    • readingFnAttributes
    • -
    + +
  • restoreOnStartup <on|off>
  • + +
    +
  • restoreOnReconnect <on|off>
  • + +
    +
  • IODev
    + Specify which FRM to use. Only required if there is more than one FRM-device defined. +
  • + + +
  • >speed (same meaning as in 'set position')
  • + +
    +
  • acceleration (same meaning as in 'set position')
  • + +
    +
  • deceleration (same meaning as in 'set position')
  • + +
  • global attributes
  • + +
  • readingFnAttributes
  • -
    +
    =end html + +=begin html_DE + + +

    FRM_STEPPER

    +
      + Die Modulbeschreibung von FRM_STEPPER gibt es nur auf Englisch.
      +

    + +=end html_DE + =cut diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index e8b4bb87b..5cf898768 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -118,15 +118,14 @@ FHEM/18_CUL_HOERMANN.pm rudolfkoenig SlowRF FHEM/19_Revolt.pm yoda_gh SlowRF FHEM/19_VBUSIF.pm Tobias/pejonp Sonstige Systeme FHEM/20_FRM_AD.pm jensb Sonstige Systeme -FHEM/20_FRM_I2C.pm ntruchsess Sonstige Systeme +FHEM/20_FRM_I2C.pm jensb Sonstige Systeme FHEM/20_FRM_IN.pm jensb Sonstige Systeme -FHEM/20_FRM_LCD.pm ntruchsess Sonstige Systeme (deprecated) FHEM/20_FRM_OUT.pm jensb Sonstige Systeme FHEM/20_FRM_PWM.pm jensb Sonstige Systeme -FHEM/20_FRM_RGB.pm ntruchsess Sonstige Systeme -FHEM/20_FRM_ROTENC.pm ntruchsess Sonstige Systeme -FHEM/20_FRM_SERVO.pm ntruchsess Sonstige Systeme -FHEM/20_FRM_STEPPER.pm ntruchsess Sonstige Systeme +FHEM/20_FRM_RGB.pm jensb Sonstige Systeme +FHEM/20_FRM_ROTENC.pm jensb Sonstige Systeme +FHEM/20_FRM_SERVO.pm jensb Sonstige Systeme +FHEM/20_FRM_STEPPER.pm jensb Sonstige Systeme FHEM/20_GUEST.pm loredo Automatisierung FHEM/20_N4HBUS.pm okoerber Sonstige Systeme FHEM/20_OWFS.pm mfr69bs 1Wire (deprecated) @@ -618,6 +617,7 @@ contrib/AttrTemplate/* Beta-User (depends on attrTemplate) contrib/commandref* rudolfkoenig Sonstiges contrib/pre-commit rudolfkoenig Sonstiges contrib/DEBIAN/* betateilchen Sonstiges +contrib/deprecated/20_FRM_LCD.pm ntruchsess (deprecated) contrib/deprecated/70_Pushalot.pm Talkabout (deprecated) contrib/deprecated/95_PachLog.pm rudolfkoenig/orphan (deprecated) contrib/DoorPi/70_DoorPi.pm pahenning Automatisierung