############################################################################### # $Id$ package main; use strict; use warnings; use vars qw(%data); use HttpUtils; use Encode; use Data::Dumper; use FHEM::Meta; # initialize ################################################################## sub THINKINGCLEANER_Initialize($) { my ($hash) = @_; Log3 $hash, 5, "THINKINGCLEANER_Initialize: Entering"; my $webhookFWinstance = join( ",", devspec2array('TYPE=FHEMWEB:FILTER=TEMPORARY!=1') ); $hash->{DefFn} = "THINKINGCLEANER_Define"; $hash->{UndefFn} = "THINKINGCLEANER_Undefine"; $hash->{SetFn} = "THINKINGCLEANER_Set"; $hash->{AttrFn} = "THINKINGCLEANER_Attr"; $hash->{parseParams} = 1; $hash->{AttrList} = "disable:0,1 disabledForIntervals timeout:1,2,3,4,5 pollInterval:30,45,60,75,90 pollMultiplierWebhook pollMultiplierCleaning model webhookHttpHostname webhookPort webhookFWinstance:$webhookFWinstance restart:noArg " . $readingFnAttributes; # 98_powerMap.pm support $hash->{powerMap} = { model => 'modelid', # fallback to attribute modelid => { 'Roomba_700_Series' => { rname_E => 'energy', rname_P => 'consumption', map => { presence => { absent => 0, }, deviceStatus => { base => 0.1, plug => 0.1, base_recon => 33, plug_recon => 33, base_full => 33, plug_full => 33, base_trickle => 5, plug_trickle => 5, base_wait => 0.1, plug_wait => 0.1, '*' => 0, }, }, }, }, }; return FHEM::Meta::InitMod( __FILE__, $hash ); } # regular Fn ################################################################## sub THINKINGCLEANER_Define($$$) { my ( $hash, $a, $h ) = @_; my $name = $hash->{NAME}; my $infix = "THINKINGCLEANER"; Log3 $name, 5, "THINKINGCLEANER $name: called function THINKINGCLEANER_Define()"; eval { require JSON; import JSON qw( decode_json ); }; return "Please install Perl JSON to use module THINKINGCLEANER" if ($@); if ( int(@$a) < 2 ) { my $msg = "Wrong syntax: define THINKINGCLEANER "; Log3 $name, 4, $msg; return $msg; } $hash->{TYPE} = "THINKINGCLEANER"; # Initialize the device return $@ unless ( FHEM::Meta::SetInternals($hash) ); my $address = @$a[2]; $hash->{DeviceName} = $address; # set reverse pointer $modules{THINKINGCLEANER}{defptr}{$name} = \$hash; # set default settings on first define if ( $init_done && !defined( $hash->{OLDDEF} ) ) { $attr{$name}{cmdIcon} = 'on-max:text_max on-spot:refresh on-delayed:time_timer dock:measure_battery_50 locate:rc_SEARCH'; $attr{$name}{devStateIcon} = 'on-delayed:rc_STOP@green:off on-max:rc_BLUE@green:off on-spot:rc_GREEN@red:off on.*:rc_GREEN@green:off dock:rc_GREEN@orange:off off:rc_STOP:on standby|remote:rc_YELLOW:on locate:rc_YELLOW .*:rc_RED'; $attr{$name}{icon} = 'scene_cleaning'; $attr{$name}{webCmd} = 'on-max:on-spot:on-delayed:dock:locate'; } if ( THINKINGCLEANER_addExtension( $name, "THINKINGCLEANER_CGI", $infix ) ) { $hash->{fhem}{infix} = $infix; } $hash->{WEBHOOK_REGISTER} = "unregistered"; # start the status update timer THINKINGCLEANER_GetStatus( $hash, 2 ); return undef; } sub THINKINGCLEANER_Undefine($$$) { my ( $hash, $a, $h ) = @_; my $name = $hash->{NAME}; if ( defined( $hash->{fhem}{infix} ) ) { THINKINGCLEANER_removeExtension( $hash->{fhem}{infix} ); } Log3 $name, 5, "THINKINGCLEANER $name: called function THINKINGCLEANER_Undefine()"; # Stop the internal GetStatus-Loop and exit RemoveInternalTimer($hash); # release reverse pointer delete $modules{THINKINGCLEANER}{defptr}{$name}; return undef; } sub THINKINGCLEANER_Set($$$); sub THINKINGCLEANER_Set($$$) { my ( $hash, $a, $h ) = @_; my $name = $hash->{NAME}; my $state = ReadingsVal( $name, "state", "absent" ); my $deviceStatus = ReadingsVal( $name, "deviceStatus", "off" ); my $presence = ReadingsVal( $name, "presence", "absent" ); my $power = ReadingsVal( $name, "power", "off" ); Log3 $name, 5, "THINKINGCLEANER $name: called function THINKINGCLEANER_Set()"; return "Argument is missing" if ( int(@$a) < 1 ); my $usage = "Unknown argument " . @$a[1] . ", choose one of statusRequest:noArg toggle:noArg on:noArg on-spot:noArg on-max:noArg off:noArg power:off,on dock:noArg undock:noArg locate:noArg on-delayed:noArg cleaningDelay remoteControl:forward,backward,left,left-spin,right,right-spin,stop,drive scheduleAdd name damageProtection:off,on reboot:noArg autoUpdate:on,off vacuumDrive:off,on restartAC:on,off alwaysMAX:on,off autoDock:on,off keepAwakeOnDock:on,off songSubmit songReset:noArg dockAt stopAt"; my $cmd = ''; my $result; # find existing schedules and offer set commands my $sd0 = ReadingsVal( $name, "schedule0", "" ); my $sd1 = ReadingsVal( $name, "schedule1", "" ); my $sd2 = ReadingsVal( $name, "schedule2", "" ); my $sd3 = ReadingsVal( $name, "schedule3", "" ); my $sd4 = ReadingsVal( $name, "schedule4", "" ); my $sd5 = ReadingsVal( $name, "schedule5", "" ); my $sd6 = ReadingsVal( $name, "schedule6", "" ); my $schedules = ""; my $si = "0"; foreach ( $sd0, $sd1, $sd2, $sd3, $sd4, $sd5, $sd6 ) { if ( $_ ne "" ) { $schedules .= "," if ( $schedules ne "" ); $_ =~ s/(\d+)_(\d{2}:\d{2}:\d{2})_(([A-Za-z]+),?)/$si\_$1_$2_$3/g; $schedules .= $_; } $si++; } $usage .= " scheduleDel:$schedules scheduleMod:$schedules" if ( $schedules ne "" ); # statusRequest if ( lc( @$a[1] ) eq "statusrequest" ) { Log3 $name, 3, "THINKINGCLEANER set $name " . @$a[1]; THINKINGCLEANER_GetStatus($hash); } # scheduleAdd elsif ( lc( @$a[1] ) eq "scheduleadd" ) { if ( $state ne "absent" ) { Log3 $name, 3, "THINKINGCLEANER set $name " . @$a[1] . " " . @$a[2] . " " . @$a[3] . " " . @$a[4]; return "Missing arguments. Usage: scheduleAdd