From ccdb488cfe6b2c186021309d2dbc5da7bddedcee Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Sun, 14 Dec 2008 11:04:44 +0000 Subject: [PATCH] Added handling of groups of aliases. Fixed internal bug when using multiple tellsticks. --- tellstickcontroller/Readme | 23 +- .../examples/power_off_all_lights | 6 +- .../examples/power_on_all_lights | 8 +- .../examples/tellstickController.conf | 47 ++-- tellstickcontroller/tellstickController | 238 ++++++++++++++---- tellstickcontroller/tellstickController.conf | 21 +- 6 files changed, 261 insertions(+), 82 deletions(-) diff --git a/tellstickcontroller/Readme b/tellstickcontroller/Readme index 473453bb..ee799365 100644 --- a/tellstickcontroller/Readme +++ b/tellstickcontroller/Readme @@ -1,3 +1,4 @@ + NAME tellstickController @@ -14,17 +15,18 @@ A small database is used for keeping track of device states between every execution of tellstickController. - -a, --aliases List of aliases for devices. - -c, --check Check content of configuration file. + -h, --help Show this help text. + -v, --verbose Show extra information. -d, --daemon Starts in daemon mode. -f, --file F Set configfile to file F. - -g, --get D Get state for device D. - -h, --help Print this help text. - -l, --list List states for all devices. - -s, --set D S Set device D to state S. + -c, --check Check content of configuration file. -t, --test Test mode, no real devices will used. - -v, --verbose Print extra information. - -x, --swap D Swap state for device D. + -a, --aliases List of aliases for devices/groups. + -l, --list List states for all devices/groups. + -s, --set D S Set device D to state S. + -g, --get D Get state for device/group D. + -w, --swapfirst G Swap states for group G based on first device state. + -x, --swap D Swap state for device/group D. EXAMPLES tellstickController -l @@ -52,6 +54,8 @@ The aliases configures device name, channel, code, etc. + The groups configures a list of devices and a delay time. + The rules can be written as a string containing two parts. The first part is optional and ends with an '/'. It can contain one or more of these keywords 'Weekend', 'Workweek', 'Monday', @@ -71,11 +75,10 @@ AUTHOR Original version written by Rickard Andersson - Improvments added by ... LICENSE Copyright (C) 2008 Rickard Andersson - Version 1.4.2 + Version 1.5.0 This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions; See license file for details. diff --git a/tellstickcontroller/examples/power_off_all_lights b/tellstickcontroller/examples/power_off_all_lights index a1598783..17b7b533 100755 --- a/tellstickcontroller/examples/power_off_all_lights +++ b/tellstickcontroller/examples/power_off_all_lights @@ -7,7 +7,9 @@ tellstickController --set bedroom_mythtv_leds off tellstickController --set bedroom_window off tellstickController --set kitchen_cabinets off tellstickController --set livingroom_cabinets off -tellstickController --set livingroom_uplight off -tellstickController --set livingroom_walls off tellstickController --set livingroom_mythtv_leds off +tellstickController --set livingroom_uplight off +tellstickController --set livingroom_wall_north off +tellstickController --set livingroom_wall_east off +tellstickController --set livingroom_window off diff --git a/tellstickcontroller/examples/power_on_all_lights b/tellstickcontroller/examples/power_on_all_lights index 97a2fd6e..0a4f12d4 100755 --- a/tellstickcontroller/examples/power_on_all_lights +++ b/tellstickcontroller/examples/power_on_all_lights @@ -1,13 +1,15 @@ #!/bin/sh # -# Power off every device that control light sources. +# Power on every device that control light sources. # tellstickController --set bedroom_mythtv_leds on tellstickController --set bedroom_window on tellstickController --set kitchen_cabinets on tellstickController --set livingroom_cabinets on -tellstickController --set livingroom_uplight on -tellstickController --set livingroom_walls on tellstickController --set livingroom_mythtv_leds on +tellstickController --set livingroom_uplight on +tellstickController --set livingroom_wall_north on +tellstickController --set livingroom_wall_east on +tellstickController --set Livingroom_window on diff --git a/tellstickcontroller/examples/tellstickController.conf b/tellstickcontroller/examples/tellstickController.conf index e7e1ab86..b26ec0b5 100644 --- a/tellstickcontroller/examples/tellstickController.conf +++ b/tellstickcontroller/examples/tellstickController.conf @@ -3,7 +3,7 @@ # Configuration file for tellstickController # # Copyright (C) 2008 Rickard Andersson (ran42ran@gmail.com) -# Version: 1.4.0 +# Version: 1.4 # ################################################################################ # @@ -49,14 +49,31 @@ Set logfile /var/log/tellstickController.log # Alias MythTv_Backlight /dev/ttyUSB0 No Sartano 000000001 # # -Alias kitchen_cabinets /dev/tellstick0 No Nexa G 2 -Alias Bedroom_window /dev/tellstick0 No Nexa G 1 +Alias Bedroom_window /dev/tellstick0 No Nexa G 10 Alias Bedroom_mythtv_power /dev/tellstick0 No Nexa G 6 Alias Bedroom_mythtv_leds /dev/tellstick0 No Nexa G 7 +Alias Kitchen_cabinets /dev/tellstick0 No Nexa G 2 +Alias Kitchen_window /dev/tellstick0 No Nexa G 11 Alias Livingroom_cabinets /dev/tellstick0 No Nexa G 3 -Alias Livingroom_walls /dev/tellstick0 No Nexa G 4 +Alias Livingroom_wall_north /dev/tellstick0 No Nexa G 4 +Alias Livingroom_wall_east /dev/tellstick0 No Nexa G 9 Alias Livingroom_uplight /dev/tellstick0 Yes Nexa G 5 Alias Livingroom_mythtv_leds /dev/tellstick0 No Nexa G 8 +Alias Livingroom_window /dev/tellstick0 No Nexa G 1 +Alias Study_window /dev/tellstick0 No Nexa G 12 + + +# Group aliases uses for handling a group of aliases +# +# Usage examples: +# Group Name Delay List of Aliases +# Group Livingroom_walls 60 Livingroom_wall_north Livingroom_wall_east +# Group Kitchen 5 Kitchen_cabinets Kitchen_window +# +# +Group Christmas_lights 20 Bedroom_window Kitchen_window Study_window +Group Background_lights 60 Kitchen_cabinets Livingroom_window Livingroom_wall_east Livingroom_wall_north +Group All_lights 10 Bedroom_mythtv_leds Bedroom_window Kitchen_cabinets Kitchen_window Livingroom_cabinets Livingroom_mythtv_leds Livingroom_uplight Livingroom_wall_north Livingroom_wall_east Livingroom_window Study_window # Timer rules for reciever devices. @@ -68,18 +85,12 @@ Alias Livingroom_mythtv_leds /dev/tellstick0 No Nexa G 8 # Rule MythTv_Backlight Weekend/20:00 Weekend/Sunset # # -Rule Bedroom_window Workweek/sunrise+02:00 Workweek/Sunrise+03:00 -Rule Bedroom_window Sunset-Random(00:30) 00:00+Random(00:30) -Rule Bedroom_window No 02:00 -Rule Bedroom_mythtv_leds No 02:00 -Rule Kitchen_cabinets Sunrise+01:00 Sunrise+03:00 -Rule Kitchen_cabinets Workweek/Sunset-01:00 Workweek/00:00+Random(00:30) -Rule Kitchen_cabinets Weekend/Sunset-01:00 Weekend/00:30+Random(00:30) -Rule Kitchen_cabinets No 02:00 -Rule Livingroom_cabinets Sunset-Random(00:30) 00:15+Random(00:30) -Rule Livingroom_cabinets No 02:00 -Rule Livingroom_walls Sunset-Random(00:30) 00:15+Random(00:30) -Rule Livingroom_walls No 02:00 -Rule Livingroom_uplight No 02:00 -Rule Livingroom_mythtv_leds No 02:00 +Rule Background_lights Workweek/Sunset+Random(01:00) Workweek/00:00+Random(00:30) +Rule Background_lights Weekend/Sunset-Random(00:30) Weekend/00:45+Random(00:30) +Rule Christmas_lights 07:00+Random(00:30) 09:00+Random(00:30) +Rule Christmas_lights 14:30+Random(00:30) 00:30+Random(00:30) + +Rule Kitchen_cabinets Sunrise-01:30 Sunrise+01:00 + +Rule All_lights No 02:00 diff --git a/tellstickcontroller/tellstickController b/tellstickcontroller/tellstickController index 18479f7c..01947e62 100755 --- a/tellstickcontroller/tellstickController +++ b/tellstickcontroller/tellstickController @@ -4,7 +4,7 @@ # tellstickController program # # Copyright (C) 2008 Rickard Andersson (ran42ran@gmail.com) -# Version 1.4.2 +# Version 1.5.0 # ###################################################################### # @@ -52,6 +52,7 @@ my %cfg_set = ( "longitude" => "15.31", "timezone" => "Europe/Stockholm", ); +my %cfg_group = (); my %cfg_alias = (); my %cfg_dimmer = (); my @cfg_rule = (); @@ -64,6 +65,7 @@ my %option = ( "list" => 0, "device" => "", "state" => "", + "swapfirst" => 0, "swap" => 0, "test" => 0, "verbose" => 0, @@ -121,6 +123,12 @@ sub check_args(@) { $option{"state"} = lc($args[0]); shift(@args); next; + } elsif ($args[0] =~ /^-(w|-swapfirst)$/) { + shift(@args); + $option{"swapfirst"} = 1; + $option{"device"} = lc($args[0]); + shift(@args); + next; } elsif ($args[0] =~ /^-(x|-swap)$/) { shift(@args); $option{"swap"} = 1; @@ -316,6 +324,7 @@ sub read_config($) { = split(/\s+/, $line); if (defined($suffix)) { println "Wrong argument '$suffix' in line '$line'!"; +println $addr; } else { $name = lc($name); $sender = $sender; @@ -355,6 +364,30 @@ sub read_config($) { next; } + if ($line =~ /^Group\s+/i) { + println $line if ($option{"verbose"}); + my (undef, $name, $delay, $aliases) = split(/\s+/, $line, 4); + $name = lc($name); + $delay = lc($delay); + if ($delay =~ /^\d+$/) { + my $error = 0; + $aliases = lc($aliases); + my @aliaslist = split(/\s+/, $aliases); + foreach my $alias (@aliaslist) { + if (! exists($cfg_alias{$alias})) { + println "Wrong alias '$alias' in line '$line'!"; + $error += 1; + } + } + if ($error == 0) { + $cfg_group{$name} = "$delay $aliases"; + } + } else { + println "Wrong delay time '$delay' in line '$line'!"; + } + next; + } + if ($line =~ /^Rule\s+/i) { #println $line if ($option{"verbose"}); my (undef, $alias, $on, $off, $suffix) = split(/\s+/, $line); @@ -364,7 +397,6 @@ sub read_config($) { $alias = lc($alias); $on = lc($on); $off = lc($off); - if (exists($cfg_alias{$alias})) { if ($on eq "") { println "Wrong on time '$on' in line '$line'!"; } elsif ($off eq "") { @@ -374,24 +406,26 @@ sub read_config($) { println "Wrong on time '$on' in line '$line'!"; } elsif ($off !~ /[\w\/\+\-\:\$\(\)]+/) { println "Wrong off time '$off' in line '$line'!"; - } else { + } + } + if (exists($cfg_alias{$alias})) { + push @cfg_rule, [$alias, $on, $off]; + } elsif (exists($cfg_group{$alias})) { push @cfg_rule, [$alias, $on, $off]; - } - } } else { println "Wrong alias '$alias' in line '$line'!"; - } - } + } + } next; - } + } if ($line =~ /^#/) { next; - } + } if ($line =~ /^\s*/) { next; - } + } println "Unhandled config line: '$line'!" if ($option{"check"}); } @@ -414,6 +448,18 @@ sub read_config($) { println $text if ($option{"check"}); printlogger $text; } + $text = "=== Group ==="; + println $text if ($option{"check"}); + printlogger $text; + foreach my $key (sort keys %cfg_group) { + my $val = $cfg_group{$key}; + my ($delay, $aliases) = split(/\s+/, $val, 2); + $text = "$key ="; + $text .= " delay time $delay seconds"; + $text .= " between aliases ($aliases)"; + println $text if ($option{"check"}); + printlogger $text; + } $text = "=== Rule ==="; println $text if ($option{"check"}); printlogger $text; @@ -480,10 +526,31 @@ sub load_device_rules () { foreach my $rule (@cfg_rule) { my ($alias, $on, $off) = @$rule; - my $time = get_rule_datetime($on, $today, $sunrise, $sunset); - push @device_on, [$time, $alias] if (defined($time) && ($now <= $time)); - $time = get_rule_datetime($off, $today, $sunrise, $sunset); - push @device_off, [$time, $alias] if (defined($time) && ($now <= $time)); + if (exists($cfg_alias{$alias})) { + my $time = get_rule_datetime($on, $today, $sunrise, $sunset); + push @device_on, [$time, $alias] if (defined($time) && ($now <= $time)); + $time = get_rule_datetime($off, $today, $sunrise, $sunset); + push @device_off, [$time, $alias] if (defined($time) && ($now <= $time)); + } else { + foreach my $val ($cfg_group{$alias}) { + my ($delay, $aliases) = split(/\s+/, $val, 2); + my $timedelay = 0; + my $ontime = get_rule_datetime($on, $today, $sunrise, $sunset); + my $offtime = get_rule_datetime($off, $today, $sunrise, $sunset); + my (@aliaslist) = split(/\s+/, $aliases); + foreach my $device (@aliaslist) { + if (defined($ontime)) { + my $time = $ontime->clone->add(seconds => $timedelay); + push @device_on, [$time, $device] if (defined($time) && ($now <= $time)); + } + if (defined($offtime)) { + my $time = $offtime->clone->add(seconds => $timedelay); + push @device_off, [$time, $device] if (defined($time) && ($now <= $time)); + } + $timedelay += $delay; + } + } + } } @device_on = sort { $a->[0] cmp $b->[0] } @device_on; @@ -517,7 +584,7 @@ sub load_device_rules () { sub get_rule_datetime($$$$) { my ($rule, $now, $sunrise, $sunset) = @_; -# println "Rule='$rule'"; +#println "Rule='$rule'"; my ($date, $time) = split(/\//, $rule); if (not defined($time)) { @@ -590,7 +657,7 @@ sub get_rule_datetime($$$$) { my $op = ""; my $rest = ""; my $mins = 0; -# println "rule='$time'"; +#println "rule='$time'"; while ($time ne "") { $op = $lastOp; @@ -625,13 +692,13 @@ sub get_rule_datetime($$$$) { $expr = ""; } -# println "expr='$expr', op='$op', rest='$rest'"; +#println "expr='$expr', op='$op', rest='$rest'"; if ($op eq "+") { $mins += $expr; } elsif ($op eq "-") { $mins -= $expr; } -# println "mins='$mins'"; +#println "mins='$mins'"; } if ($mins <= 0) { $mins = $mins % (24*60); @@ -646,9 +713,9 @@ sub get_rule_datetime($$$$) { # UTC ? $time = get_datetime_now(); $time->set(hour => 0, minute => 0, second => 0); - $time->add(hours => $hours+24*$days, minutes => $minutes); -# println "days='$days', hours='$hours', minutes='$minutes'"; -# println "time='$time'"; + $time->add(hours => $hours+24*$days, minutes => $minutes, seconds => 0); +#println "days='$days', hours='$hours', minutes='$minutes'"; +#println "time='$time'"; } else { $time = undef; } @@ -713,8 +780,17 @@ sub get_device_state() { my $state = $device_state{$device}; println "$state" if (not $option{"verbose"}); println "Device $device = $state" if ($option{"verbose"}); + } elsif (exists($cfg_group{$device})) { + my $val = $cfg_group{$device}; + my ($delay, $aliases) = split(/\s+/, $val, 2); + my $states = ""; + my (@aliaslist) = split(/\s+/, $aliases); + foreach my $alias (@aliaslist) { + $states .= " $device_state{$alias}"; + } + println "Group $device =$states"; } else { - println "No device found with alias '$device'!"; + println "No alias or group found with name '$device'!"; } } } @@ -723,16 +799,58 @@ sub get_device_state() { sub set_device_state() { if ($option{"device"}) { if ($option{"state"}) { + my $state = $option{"state"}; + if ($state !~ /^(on|off)$/i) { + println "No state found with name '$state'!"; + } my $device = $option{"device"}; if ($device_state{$device}) { - my $state = $option{"state"}; - if ($state =~ /^(on|off)$/i) { - change_device_state($device, $state); - } else { - println "No state found with name '$state'!"; + change_device_state($device, $state); + } elsif (exists($cfg_group{$device})) { + my $val = $cfg_group{$device}; + my ($delay, $aliases) = split(/\s+/, $val, 2); + my (@aliaslist) = split(/\s+/, $aliases); + foreach my $alias (@aliaslist) { + change_device_state($alias, $state); + sleep(1); } } else { - println "No device found with alias '$device'!"; + println "No alias or group found with name '$device'!"; + } + } + } +} + + +sub swap_first_device_state() { + if ($option{"device"}) { + if ($option{"swapfirst"}) { + my $device = $option{"device"}; + if ($device_state{$device}) { + my $state = $device_state{$device}; + if ($state =~ /^off$/i) { + $state = 'on'; + } else { + $state = 'off'; + } + change_device_state($device, $state); + } elsif (exists($cfg_group{$device})) { + my $val = $cfg_group{$device}; + my ($delay, $aliases) = split(/\s+/, $val, 2); + my ($alias) = split(/\s+/, $aliases); + my $state = $device_state{$alias}; + if ($state =~ /^off$/i) { + $state = 'on'; + } else { + $state = 'off'; + } + my (@aliaslist) = split(/\s+/, $aliases); + foreach my $alias (@aliaslist) { + change_device_state($alias, $state); + sleep(1); + } + } else { + println "No alias or group found with name '$device'!"; } } } @@ -746,12 +864,27 @@ sub swap_device_state() { if ($device_state{$device}) { my $state = $device_state{$device}; if ($state =~ /^off$/i) { - change_device_state($device, 'on'); + $state = 'on'; } else { - change_device_state($device, 'off'); + $state = 'off'; + } + change_device_state($device, $state); + } elsif (exists($cfg_group{$device})) { + my $val = $cfg_group{$device}; + my ($delay, $aliases) = split(/\s+/, $val, 2); + my (@aliaslist) = split(/\s+/, $aliases); + foreach my $alias (@aliaslist) { + my $state = $device_state{$alias}; + if ($state =~ /^off$/i) { + $state = 'on'; + } else { + $state = 'off'; + } + change_device_state($alias, $state); + sleep(1); } } else { - println "No device found with alias '$device'!"; + println "No alias or group found with name '$device'!"; } } } @@ -762,6 +895,16 @@ sub list_all_devices() { foreach my $key (sort keys %device_state) { println "Device $key = $device_state{$key}"; } + foreach my $key (sort keys %cfg_group) { + my $val = $cfg_group{$key}; + my ($delay, $aliases) = split(/\s+/, $val, 2); + my $states = ""; + my (@aliaslist) = split(/\s+/, $aliases); + foreach my $device (@aliaslist) { + $states .= " $device_state{$device}"; + } + println "Group $key =$states"; + } } @@ -769,7 +912,12 @@ sub list_all_aliases { foreach my $key (sort keys %cfg_alias) { my $dimmer = ""; $dimmer = " dimmer" if $cfg_dimmer{$key} eq "yes"; - println "Device $key is an alias for$dimmer receiver $cfg_alias{$key}"; + println "Alias $key = receiver ($cfg_alias{$key}$dimmer)"; + } + foreach my $key (sort keys %cfg_group) { + my $val = $cfg_group{$key}; + my ($delay, $aliases) = split(/\s+/, $val, 2); + println "Group $key = delay $delay seconds, aliases ($aliases)"; } } @@ -784,7 +932,7 @@ sub daemon_loop() { $next_day->set( hour => 0, minute => 0, second => 0 ); println "Next reload of device rules = $next_day" if ($option{"verbose"}); - my $wait_time = 60; + my $wait_time = 5; my $loop = 1; check_device_rules($now); @@ -811,6 +959,8 @@ sub perform_action() { get_device_state(); } elsif ($option{"set"}) { set_device_state(); + } elsif ($option{"swapfirst"}) { + swap_first_device_state(); } elsif ($option{"swap"}) { swap_device_state(); } elsif ($option{"list"}) { @@ -869,17 +1019,18 @@ __DATA__ #- A small database is used for keeping track of device states #- between every execution of tellstickController. #- -#- -a, --aliases List of aliases for devices. -#- -c, --check Check content of configuration file. +#- -h, --help Show this help text. +#- -v, --verbose Show extra information. #- -d, --daemon Starts in daemon mode. #- -f, --file F Set configfile to file F. -#- -g, --get D Get state for device D. -#- -h, --help Print this help text. -#- -l, --list List states for all devices. -#- -s, --set D S Set device D to state S. +#- -c, --check Check content of configuration file. #- -t, --test Test mode, no real devices will used. -#- -v, --verbose Print extra information. -#- -x, --swap D Swap state for device D. +#- -a, --aliases List of aliases for devices/groups. +#- -l, --list List states for all devices/groups. +#- -s, --set D S Set device D to state S. +#- -g, --get D Get state for device/group D. +#- -w, --swapfirst G Swap states for group G based on first device state. +#- -x, --swap D Swap state for device/group D. #- #- EXAMPLES #- tellstickController -l @@ -907,6 +1058,8 @@ __DATA__ #- #- The aliases configures device name, channel, code, etc. #- +#- The groups configures a list of devices and a delay time. +#- #- The rules can be written as a string containing two parts. #- The first part is optional and ends with an '/'. It can contain #- one or more of these keywords 'Weekend', 'Workweek', 'Monday', @@ -926,11 +1079,10 @@ __DATA__ #- #- AUTHOR #- Original version written by Rickard Andersson -#- Improvments added by ... #- #- LICENSE #- Copyright (C) 2008 Rickard Andersson -#- Version 1.4.2 +#- Version 1.5.0 #- This program comes with ABSOLUTELY NO WARRANTY. #- This is free software, and you are welcome to redistribute it #- under certain conditions; See license file for details. diff --git a/tellstickcontroller/tellstickController.conf b/tellstickcontroller/tellstickController.conf index bf6708c8..4d5310b1 100644 --- a/tellstickcontroller/tellstickController.conf +++ b/tellstickcontroller/tellstickController.conf @@ -3,7 +3,7 @@ # Configuration file for tellstickController # # Copyright (C) 2008 Rickard Andersson (ran42ran@gmail.com) -# Version: 1.4.2 +# Version: 1.5.0 # ################################################################################ # @@ -29,7 +29,6 @@ # # Set Timezone Europe/Stockholm # -# Set Timezone Europe/Stockholm Set Latitude 58.24 Set Longitude 15.31 @@ -53,8 +52,18 @@ Set logfile /var/log/tellstickController.log # # Alias MythTv_Backlight /dev/ttyUSB0 No Sartano 000000001 # +Alias device1 /dev/tellstick No Nexa A 1 +Alias device2 /dev/tellstick No Nexa A 2 + + +# Groups used for handling a list of aliases. # -Alias device1 /dev/tellstick No Nexa A 1 +# Usage examples: +# Group Name Delay Aliases +# Group Livingroom_walls 60 Livingroom_wall_north Livingroom_wall_east +# Group Kitchen 5 Kitchen_cabinets Kitchen_window +# +Group allDevices 10 Device1 Device2 # Timer rules for reciever devices. @@ -68,7 +77,7 @@ Alias device1 /dev/tellstick No Nexa A 1 # Rule device42 2008-03-##/18:45 2008-03-##/22:11 # Rule device42 No 02:00 # -# -Rule device1 weekend/sunrise weekend/08:45 -Rule device1 sunset+00:15 23:30+Random(00:20) +Rule device1 weekend/sunrise weekend/08:45 +Rule device1 sunset+00:15 23:30+Random(00:20) +Rule allDevices 16:45 23:45