From b85e5211edaa4c4ba72b2eb0dfe25129115d0858 Mon Sep 17 00:00:00 2001 From: ntruchsess <> Date: Thu, 7 Nov 2013 23:28:30 +0000 Subject: [PATCH] Merge branch 'frm_rgb' git-svn-id: https://svn.fhem.de/fhem/trunk@4172 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/10_FRM.pm | 2 +- fhem/FHEM/20_FRM_RGB.pm | 317 ++++++++++++++++++++++++++++++++++++++++ fhem/FHEM/Color.pm | 61 ++++++++ 3 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 fhem/FHEM/20_FRM_RGB.pm diff --git a/fhem/FHEM/10_FRM.pm b/fhem/FHEM/10_FRM.pm index e73b3a11b..094b32ce1 100755 --- a/fhem/FHEM/10_FRM.pm +++ b/fhem/FHEM/10_FRM.pm @@ -38,7 +38,7 @@ sub FRM_Initialize($) { require "$main::attr{global}{modpath}/FHEM/DevIo.pm"; # Provider - $hash->{Clients} = ":FRM_IN:FRM_OUT:FRM_AD:FRM_PWM:FRM_I2C:FRM_SERVO:OWX:FRM_LCD:"; + $hash->{Clients} = ":FRM_IN:FRM_OUT:FRM_AD:FRM_PWM:FRM_I2C:FRM_SERVO:OWX:FRM_LCD:FRM_RGB:"; $hash->{ReadyFn} = "FRM_Ready"; $hash->{ReadFn} = "FRM_Read"; diff --git a/fhem/FHEM/20_FRM_RGB.pm b/fhem/FHEM/20_FRM_RGB.pm new file mode 100644 index 000000000..781eb8bb8 --- /dev/null +++ b/fhem/FHEM/20_FRM_RGB.pm @@ -0,0 +1,317 @@ +############################################# +package main; + +use vars qw{%attr %defs $readingFnAttributes}; +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 /; +use Color qw/ :all /; +use SetExtensions qw/ :all /; + +##################################### + +my %gets = ( + "rgb" => 0, + "RGB" => 0, + "pct" => 0, + "devStateIcon" => 0, +); + +my %sets = ( + "on" => 0, + "off" => 0, + "toggle" => 0, + "rgb:colorpicker,RGB" => 1, + "pct:slider,0,1,100" => 1, + "fadeTo" => 2, + "dimUp" => 0, + "dimDown" => 0, +); + +sub +FRM_RGB_Initialize($) +{ + my ($hash) = @_; + + $hash->{SetFn} = "FRM_RGB_Set"; + $hash->{GetFn} = "FRM_RGB_Get"; + $hash->{DefFn} = "FRM_RGB_Define"; + $hash->{InitFn} = "FRM_RGB_Init"; + $hash->{UndefFn} = "FRM_Client_Undef"; + $hash->{AttrFn} = "FRM_RGB_Attr"; + $hash->{StateFn} = "FRM_RGB_State"; + + $hash->{AttrList} = "restoreOnReconnect:on,off restoreOnStartup:on,off IODev loglevel:0,1,2,3,4,5 $readingFnAttributes"; + + LoadModule("FRM"); + FHEM_colorpickerInit(); +} + +sub +FRM_RGB_Define($$) +{ + my ($hash, $def) = @_; + $attr{$hash->{NAME}}{webCmd} = "rgb:rgb ff0000:rgb 00ff00:rgb 0000ff:toggle:on:off"; + return FRM_Client_Define($hash,$def); +} + +sub +FRM_RGB_Init($$) +{ + my ($hash,$args) = @_; + my $name = $hash->{NAME}; + my $ret = FRM_Init_Pin_Client($hash,$args,PIN_PWM); + return $ret if (defined $ret); + my @pins = (); + eval { + my $firmata = FRM_Client_FirmataDevice($hash); + $hash->{PIN} = ""; + foreach my $pin (@{$args}) { + $firmata->pin_mode($pin,PIN_PWM); + push @pins,{ + pin => $pin, + "shift" => defined $firmata->{metadata}{pwm_resolutions} ? $firmata->{metadata}{pwm_resolutions}{$pin}-8 : 0, + }; + $hash->{PIN} .= $hash->{PIN} eq "" ? $pin : " $pin"; + } + $hash->{PINS} = \@pins; + if (! (defined AttrVal($name,"stateFormat",undef))) { + $attr{$name}{"stateFormat"} = "value"; + } + my $value = ReadingsVal($name,"rgb",undef); + if (defined $value and AttrVal($hash->{NAME},"restoreOnReconnect","on") eq "on") { + FRM_RGB_Set($hash,$name,"rgb",$value); + } + }; + return $@ if $@; + $hash->{toggle} = "off"; + $hash->{dim} = { + bri => 50, + channels => [(255) x @{$hash->{PINS}}], + }; + readingsSingleUpdate($hash,"state","Initialized",1); + return undef; +} + +sub +FRM_RGB_Set($@) +{ + my ($hash, $name, $cmd, @a) = @_; + + my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets ); + #-- check argument + return SetExtensions($hash, join(" ", keys %sets), $name, $cmd, @a) unless @match == 1; + return "$cmd expects $sets{$match[0]} parameters" unless (@a eq $sets{$match[0]}); + + SETHANDLER: { + $cmd eq "on" and do { + FRM_RGB_SetChannels($hash,(0xFF) x scalar(@{$hash->{PINS}})); + $hash->{toggle} = "on"; + last; + }; + $cmd eq "off" and do { + FRM_RGB_SetChannels($hash,(0x00) x scalar(@{$hash->{PINS}})); + $hash->{toggle} = "off"; + last; + }; + $cmd eq "toggle" and do { + my $toggle = $hash->{toggle}; + TOGGLEHANDLER: { + $toggle eq "off" and do { + $hash->{toggle} = "up"; + FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{dim})); + last; + }; + $toggle eq "up" and do { + FRM_RGB_SetChannels($hash,(0xFF) x @{$hash->{PINS}}); + $hash->{toggle} = "on"; + last; + }; + $toggle eq "on" and do { + $hash->{toggle} = "down"; + FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{dim})); + last; + }; + $toggle eq "down" and do { + FRM_RGB_SetChannels($hash,(0x0) x @{$hash->{PINS}}); + $hash->{toggle} = "off"; + last; + }; + }; + last; + }; + $cmd eq "rgb" and do { + my $arg = $a[0]; + my $numPins = scalar(@{$hash->{PINS}}); + my $nybles = $numPins << 1; + my @channels = RgbToChannels($arg,$numPins); + FRM_RGB_SetChannels($hash,@channels); + RGBHANDLER: { + $arg =~ /^0{$nybles}$/ and do { + $hash->{toggle} = "off"; + last; + }; + $arg =~ /^f{$nybles}$/i and do { + $hash->{toggle} = "on"; + last; + }; + $hash->{toggle} = "up"; + }; + $hash->{dim} = ChannelsToBrightness(@channels); + last; + }; + $cmd eq "pct" and do { + $hash->{dim}->{bri} = $a[0]; + FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{dim})); + last; + }; + $cmd eq "dimUp" and do { + $hash->{dim}->{bri} = $hash->{dim}->{bri} > 90 ? 100 : $hash->{dim}->{bri}+10; + FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{dim})); + last; + }; + $cmd eq "dimDown" and do { + $hash->{dim}->{bri} = $hash->{dim}->{bri} < 10 ? 0 : $hash->{dim}->{bri}-10; + FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{dim})); + last; + }; + } + return undef; +} + +sub +FRM_RGB_Get($@) +{ + my ($hash, $name, $cmd, @a) = @_; + + return "FRM_RGB: Get with unknown argument $cmd, choose one of ".join(" ", sort keys %gets) + unless defined($gets{$cmd}); + + GETHANDLER: { + $cmd eq 'rgb' and do { + return ReadingsVal($name,"rgb",undef); + }; + $cmd eq 'RGB' and do { + return ChannelsToRgb(@{$hash->{dim}->{channels}}); + }; + $cmd eq 'pct' and do { + return $hash->{dim}->{bri}; + return undef; + }; + } +} + +sub +FRM_RGB_SetChannels($$) +{ + my ($hash,@channels) = @_; + + my $firmata = FRM_Client_FirmataDevice($hash); + my @pins = @{$hash->{PINS}}; + my @values = @channels; + + while(@values) { + my $pin = shift @pins; + my $value = shift @values; + if ($pin->{"shift"} < 0) { + $value >>= -$pin->{"shift"}; + } else { + $value <<= $pin->{"shift"}; + } + $firmata->analog_write($pin->{pin},$value); + }; + readingsSingleUpdate($hash,"rgb",ChannelsToRgb(@channels),1); +} + +sub FRM_RGB_State($$$$) +{ + my ($hash, $tim, $sname, $sval) = @_; + +STATEHANDLER: { + $sname eq "value" and do { + if (AttrVal($hash->{NAME},"restoreOnStartup","on") eq "on") { + FRM_RGB_Set($hash,$hash->{NAME},$sval); + } + last; + } + } +} + +sub +FRM_RGB_Attr($$$$) { + my ($command,$name,$attribute,$value) = @_; + if ($command eq "set") { + ARGUMENT_HANDLER: { + $attribute eq "IODev" and do { + my $hash = $main::defs{$name}; + if (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $value) { + $hash->{IODev} = $defs{$value}; + FRM_Init_Client($hash) if (defined ($hash->{IODev})); + } + last; + }; + $main::attr{$name}{$attribute}=$value; + } + } +} + +1; + +=pod +=begin html + + +

FRM_PWM

+ +
+ +=end html +=cut diff --git a/fhem/FHEM/Color.pm b/fhem/FHEM/Color.pm index f2ca9736c..98e645e04 100644 --- a/fhem/FHEM/Color.pm +++ b/fhem/FHEM/Color.pm @@ -54,4 +54,65 @@ FHEM_colorpickerFn($$$) } } +package Color; +require Exporter; +our @ISA = qw(Exporter); +our %EXPORT_TAGS = (all => [qw(RgbToChannels ChannelsToRgb ChannelsToBrightness BrightnessToChannels)]); +Exporter::export_tags('all'); + +sub +RgbToChannels($$) { + my ($rgb,$numChannels) = @_; + my $nybles = $numChannels << 1; + die "$rgb is not the right format" unless( $rgb =~ /^[\da-f]{$nybles}$/i ); + my @channels = (); + foreach my $channel (unpack("(A2)[$numChannels]",$rgb)) { + push @channels,hex($channel); + } + return @channels; +} + +sub +ChannelsToRgb(@) { + my @channels = @_; + return sprintf("%02X" x @_, @_); +} + +sub +ChannelsToBrightness(@) { + my (@channels) = @_; + + my $max = 0; + foreach my $value (@channels) { + $max = $value if ($max < $value); + } + + return { + bri => 0, + channels => \(255 x @channels), + } unless ($max > 0); + + my @bri = (); + my $norm = 255/$max; + foreach my $value (@channels) { + push @bri,int($value*$norm); + } + + return { + bri => int($max/2.55), + channels => \@bri, + } +} + +sub +BrightnessToChannels($) { + my $arg = shift; + my @channels = (); + my $bri = $arg->{bri}; + foreach my $value (@{$arg->{channels}}) { + push @channels,$value*$bri/100; + } + return @channels; +} + 1;