diff --git a/lib/raZberry.pm b/lib/raZberry.pm index baa45b2c2..cf7eeb6ee 100755 --- a/lib/raZberry.pm +++ b/lib/raZberry.pm @@ -1,41 +1,57 @@ -=head1 B +=head1 B v1.6 =head2 SYNOPSIS In user code: + + use raZberry; + $razberry_controller = new raZberry('10.0.1.1'); + $razberry_comm = new raZberry_comm($razberry_controller); + $room_fan = new raZberry_dimmer($razberry_controller,'2','force_update'); + $room_blind = new raZberry_blind($razberry_controller,'3','digital'); + $front_lock = new raZberry_lock($razberry_controller,'4'); + $thermostat = new raZberry_thermostat($razberry_controller,'5'); + $temp_sensor = new raZberry_temp_sensor($razberry_controller,'5'); + $door_sensor = new raZberry_binary_sensor($razberry_controller,'7'); + $garage_light = new raZberry_switch($razberry_controller,'6'); + $remote_1 = new raZberry_battery($razberry_controller,12); - use raZberry.pm; - $razberry_controller = new raZberry('192.168.0.100',1); - $razberry_comm = new raZberry_comm($razberry_controller); - $family_room_fan = new raZberry_dimmer($razberry_controller,'2','force_update'); - $family_room_blind = new raZberry_blind($razberry_controller,'3'); - $front_lock = new raZberry_blind($razberry_controller,'4'); - $front_door = new raZberry_door($razberry_controller,'5'); - $toilet_window = new raZberry_window($razberry_controller,'6'); -So far only raZberry_dimmer, raZberry_lock and raZberry_blind are working child objects - -raZberry_(,,,); +raZberry_(,,) In items.mht: - TBD - +RAZBERRY_CONTROLLER, ip_address, controller_name, group, options +RAZBERRY_DIMMER, device_id, name, group, controller_name, options +RAZBERRY_SWITCH, device_id, name, group, controller_name, options +RAZBERRY_BLIND, device_id, name, group, controller_name, options +RAZBERRY_LOCK, device_id, name, group, controller_name, options +RAZBERRY_THERMOSTAT, device_id, name, group, controller_name, options +RAZBERRY_TEMP_SENSOR, device_id, name, group, controller_name, options +RAZBERRY_BINARY_SENSOR, device_id, name, group, controller_name, options + +for example: + +RAZBERRY_CONTROLLER, 10.0.1.1, razberry_controller, zwave +RAZBERRY_BLIND, 4, main_blinds, HVAC|zwave, razberry_controller, battery + + =head2 DESCRIPTION -=head3 LINKING ZWAVE devices +=head3 INCLUDING ZWAVE devices -Devices need to first linked inside the razberry using the included web interface. +Devices need to first included inside the razberry zwave network using the included web interface. =head3 STATE REPORTED IN MisterHouse The Razberry is polled on a regular basis in order to update local objects. By default, the razberry is polled every 5 seconds. -Update for local control use the 'nifler' plug in. This saves getting the local device +Update for local control use the 'niffler' plug in. This saves forcing a local device status every poll. =head3 SENSOR STATE CHILD OBJECT @@ -48,6 +64,14 @@ There is also a communication object to allow for alerting and monitoring of the razberry controller. =head2 NOTES +v1.6 +- added in digital blinds, battery item (like a remote) + +v1.5 +- added in binary sensors + +v1.4 +- added in thermostat v1.3 - added in locks @@ -58,6 +82,16 @@ v1.2 - added a check to see if the device is 'dead'. If dead it will attempt a ping for X attempts a Y seconds apart. +OTHER + +Works and tested with v2.0.0. It _should_ also work with v1.7.4. +For later versions, Z_Way has introduced authentication. raZberry will support that at a later time +To get a 2.0+ version to work, anonymous authentication has to be enabled: +- Create a room named devices, and assign all ZWay devices to that room +- Create a user named anonymous with role anonymous +- Edit user anonymous and allow access to room devices + + http calls can cause pauses. There are a few possible options around this; - push output to a file and then read the file. This is generally how other modules work. @@ -84,8 +118,8 @@ use warnings; use LWP::UserAgent; use HTTP::Request::Common qw(POST); -use JSON::XS; -use Data::Dumper; +use JSON qw(decode_json); +#use Data::Dumper; @raZberry::ISA = ('Generic_Item'); @@ -126,11 +160,9 @@ sub new { $self->{data} = undef; $self->{child_object} = undef; $self->{config}->{poll_seconds} = 5; - $self->{config}->{poll_seconds} = $main::config_parms{raZberry_poll_seconds} - if ( defined $main::config_parms{raZberry_poll_seconds} ); + $self->{config}->{poll_seconds} = $main::config_parms{raZberry_poll_seconds} if ( defined $main::config_parms{raZberry_poll_seconds} ); $self->{config}->{poll_seconds} = $poll if ($poll); - $self->{config}->{poll_seconds} = 1 - if ( $self->{config}->{poll_seconds} < 1 ); + $self->{config}->{poll_seconds} = 1 if ( $self->{config}->{poll_seconds} < 1 ); $self->{updating} = 0; $self->{data}->{retry} = 0; my ( $host, $port ) = ( split /:/, $addr )[ 0, 1 ]; @@ -148,6 +180,7 @@ sub new { $self->{timer} = new Timer; $self->start_timer; + &main::print_log("[raZberry] Controller initialized."); return $self; } @@ -167,53 +200,49 @@ sub poll { } for my $dev ( keys %{ $self->{data}->{ping} } ) { - &main::print_log("[raZberry] Keep_alive: Pinging device $dev...") - ; # if ($self->{debug}); - &main::print_log("ping_dev $dev"); # if ($self->{debug}); + &main::print_log("[raZberry] Keep_alive: Pinging device $dev..."); # if ($self->{debug}); + &main::print_log("[raZberry] ping_dev $dev"); # if ($self->{debug}); #$self->ping_dev($dev); } - my ( $isSuccessResponse1, $devices ) = - _get_JSON_data( $self, 'devices', $cmd ); - print Dumper $devices if ( $self->{debug} > 1 ); + my ( $isSuccessResponse1, $devices ) = _get_JSON_data( $self, 'devices', $cmd ); +# print Dumper $devices if ( $self->{debug} > 1 ); if ($isSuccessResponse1) { $self->{lastupdate} = $devices->{data}->{updateTime}; foreach my $item ( @{ $devices->{data}->{devices} } ) { - &main::print_log( "Found:" - . $item->{id} - . " with level " - . $item->{metrics}->{level} - . " and updated " - . $item->{updateTime} - . "." ) - if ( $self->{debug} ); + &main::print_log( "[raZberry] Found:" . $item->{id} . " with level " . $item->{metrics}->{level} . " and updated " . $item->{updateTime} . "." ) if ( $self->{debug} ); my ($id) = ( split /_/, $item->{id} )[2]; - #print "id=$id\n" if ($self->{debug} > 1); - $self->{data}->{devices}->{$id}->{level} = - $item->{metrics}->{level}; + print "id=$id\n" if ($self->{debug} > 1); + my $battery_dev = 0; + $battery_dev = 1 if ($id =~ m/-0-128$/); + if ($battery_dev) { #for a battery, set a different object + $self->{data}->{devices}->{$id}->{battery_level} = $item->{metrics}->{level}; + } else { + $self->{data}->{devices}->{$id}->{level} = $item->{metrics}->{level}; + } $self->{data}->{devices}->{$id}->{updateTime} = $item->{updateTime}; $self->{data}->{devices}->{$id}->{devicetype} = $item->{deviceType}; $self->{data}->{devices}->{$id}->{location} = $item->{location}; - $self->{data}->{devices}->{$id}->{title} = - $item->{metrics}->{title}; + $self->{data}->{devices}->{$id}->{title} = $item->{metrics}->{title}; $self->{data}->{devices}->{$id}->{icon} = $item->{metrics}->{icon}; + #thermostat data items + $self->{data}->{devices}->{$id}->{units} = $item->{metrics}->{scaleTitle} if (defined $item->{metrics}->{scaleTitle}); + $self->{data}->{devices}->{$id}->{temp_min} = $item->{metrics}->{min} if (defined $item->{metrics}->{min}); + $self->{data}->{devices}->{$id}->{temp_max} = $item->{metrics}->{max} if (defined $item->{metrics}->{max}); + $self->{status} = "online"; if ( defined $self->{child_object}->{$id} ) { - &main::print_log( - "[raZberry] Child object detected: Controller Level:[" - . $item->{metrics}->{level} - . "] Child Level:[" - . $self->{child_object}->{$id}->level() - . "]" ) - if ( $self->{debug} > 1 ); - $self->{child_object}->{$id} - ->set( $item->{metrics}->{level}, 'poll' ) - if ( $self->{child_object}->{$id}->level() ne - $item->{metrics}->{level} ); + if ($battery_dev) { + &main::print_log("[raZberry] Child object detected: Battery Level:[" . $item->{metrics}->{level} . "] Child Level:[" . $self->{child_object}->{$id}->battery_level() . "]" ) if ( $self->{debug} > 1 ); + $self->{child_object}->{$id}->update_data ($self->{data}->{devices}->{$id}); #be able to push other data to objects for actions + } else { + &main::print_log("[raZberry] Child object detected: Controller Level:[" . $item->{metrics}->{level} . "] Child Level:[" . $self->{child_object}->{$id}->level() . "]" ) if ( $self->{debug} > 1 ); + $self->{child_object}->{$id}->set( $item->{metrics}->{level}, 'poll' ) if (( $self->{child_object}->{$id}->level() ne $item->{metrics}->{level} ) and !($id =~ m/-0-128$/)); + $self->{child_object}->{$id}->update_data ($self->{data}->{devices}->{$id}); #be able to push other data to objects for actions + } } - } } else { @@ -246,7 +275,7 @@ sub set_dev { return ('0'); } - print Dumper $status if ( $self->{debug} > 1 ); +# print Dumper $status if ( $self->{debug} > 1 ); } } @@ -273,7 +302,7 @@ sub ping_dev { sub isfailed_dev { - #"http://192.168.0.155:8083/ZWaveAPI/Run/devices[x].data.isFailed.value" + #"http://mhip:8083/ZWaveAPI/Run/devices[x].data.isFailed.value" my ( $self, $device ) = @_; my ( $devid, $instance, $class ) = ( split /-/, $device )[ 0, 1, 2 ]; &main::print_log("[raZberry] Checking $device ($devid)...") @@ -333,19 +362,14 @@ sub _get_JSON_data { my $params = ""; $params = $cmd if ($cmd); my $method = "ZAutomation/api/v1"; - $method = "ZWaveAPI/Run" - if ( ( $mode eq "force_update" ) + $method = "ZWaveAPI/Run" if ( ( $mode eq "force_update" ) or ( $mode eq "ping" ) or ( $mode eq "isfailed" ) or ( $mode eq "usercode" ) or ( $mode eq "usercode_data" ) ); - &main::print_log( - "[raZberry] contacting http://$host:$port/$method/$rest{$mode}$params" - ) if ( $self->{debug} ); + &main::print_log("[raZberry] contacting http://$host:$port/$method/$rest{$mode}$params") if ( $self->{debug} ); - my $request = - HTTP::Request->new( - GET => "http://$host:$port/$method/$rest{$mode}$params" ); + my $request = HTTP::Request->new( GET => "http://$host:$port/$method/$rest{$mode}$params" ); $request->content_type("application/x-www-form-urlencoded"); my $responseObj = $ua->request($request); @@ -364,9 +388,11 @@ sub _get_JSON_data { if ( defined $self->{child_object}->{comm} ) { if ( $self->{status} eq "online" ) { $self->{status} = "offline"; - &main::print_log("Communication Tracking object found. Updating from " + main::print_log + "[raZberry] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() - . " to offline...") if ( $self->{loglevel} ); + . " to offline..." + if ( $self->{loglevel} ); $self->{child_object}->{comm}->set( "offline", 'poll' ); } } @@ -375,9 +401,11 @@ sub _get_JSON_data { if ( defined $self->{child_object}->{comm} ) { if ( $self->{status} eq "offline" ) { $self->{status} = "online"; - main::print_log( "Communication Tracking object found. Updating from " + main::print_log + "[raZberry] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() - . " to online...") if ( $self->{loglevel} ); + . " to online..." + if ( $self->{loglevel} ); $self->{child_object}->{comm}->set( "online", 'poll' ); } } @@ -387,7 +415,15 @@ sub _get_JSON_data { or ( $mode eq "usercode" ) ) ; #these come backs as nulls which crashes JSON::XS, so just return. return ( $responseObj->content ) if ( $mode eq "isfailed" ); - my $response = JSON::XS->new->decode( $responseObj->content ); +# my $response = JSON::XS->new->decode( $responseObj->content ); + my $response; + eval { + $response = decode_json($responseObj->content); #HP, wrap this in eval to prevent MH crashes + }; + if ($@) { + &main::print_log("[raZberry]: WARNING: decode_json failed for returned data"); + return ("0",""); + } return ( $isSuccessResponse, $response ) } @@ -454,19 +490,20 @@ sub register { $self->{child_object}->{'comm'} = $object; } else { - &main::print_log("[raZberry] Registering Device ID $dev to controller"); +#TODO + my $type = $object->{type}; + $type = "Digital " . $type if ((defined $options) and ($options =~ m/digital/)); + &main::print_log("[raZberry] Registering " . $type . " Device ID $dev to controller"); $self->{child_object}->{$dev} = $object; if ( defined $options ) { if ( $options =~ m/force_update/ ) { $self->{data}->{force_update}->{$dev} = 1; - &main::print_log( - "[raZberry] Forcing Controller to contact Device $dev at each poll" + &main::print_log("[raZberry] Forcing Controller to contact Device $dev at each poll" ); } if ( $options =~ m/keep_alive/ ) { $self->{data}->{ping}->{$dev} = 1; - &main::print_log( - "[raZberry] Forcing Controller to ping Device $dev at each poll" + &main::print_log("[raZberry] Forcing Controller to ping Device $dev at each poll" ); } } @@ -491,6 +528,7 @@ sub new { $$self{master_object} = $object; $devid = $devid . $zway_suffix unless ( $devid =~ m/-\d+-\d+$/ ); $$self{devid} = $devid; + $$self{type} = "Dimmer"; $object->register( $self, $devid, $options ); #$self->set($object->get_dev_status,$devid,'poll'); @@ -573,9 +611,98 @@ sub isfailed { $$self{master_object}->isfailed_dev( $$self{devid} ); } +sub update_data { + my ($self,$data) = @_; +} + +package raZberry_switch; + +@raZberry_switch::ISA = ('Generic_Item'); + +sub new { + my ( $class, $object, $devid, $options ) = @_; + + my $self = {}; + bless $self, $class; + push(@{ $$self{states} }, 'off', 'on', ); + + $$self{master_object} = $object; + $devid = $devid . "-0-37" unless ( $devid =~ m/-\d+-\d+$/ ); + $$self{devid} = $devid; + $$self{type} = "Switch"; + $object->register( $self, $devid, $options ); + + #$self->set($object->get_dev_status,$devid,'poll'); + $self->{level} = ""; + $self->{debug} = $object->{debug}; + return $self; + +} + +sub set { + my ( $self, $p_state, $p_setby ) = @_; + + if ( $p_setby eq 'poll' ) { + if (lc $p_state eq "on" ) { + $self->{level} = 100; + } + elsif (lc $p_state eq "off" ) { + $self->{level} = 0; + } + + main::print_log("[raZberry_switch] Setting value to $p_state. Level is " . $self->{level} ) if ( $self->{debug} ); + $self->SUPER::set($p_state); + } + else { + if ( ( lc $p_state eq "off" ) or ( lc $p_state eq "on" ) ) { + $$self{master_object}->set_dev( $$self{devid}, $p_state ); + } + else { + main::print_log( + "[raZberry_switch] Error. Unknown set state $p_state"); + } + } +} + +sub level { + my ($self) = @_; + + return ( $self->{level} ); +} + +sub ping { + my ($self) = @_; + + $$self{master_object}->ping_dev( $$self{devid} ); +} + +sub isfailed { + my ($self) = @_; + + $$self{master_object}->isfailed_dev( $$self{devid} ); +} + +sub update_data { + my ($self,$data) = @_; +} + package raZberry_blind; -#only tested with Somfy ZRTSI module +#tested with somfy zrtsi and somfy zwave blinds +# To pair a somfy zwave blind: +# https://www.youtube.com/watch?v=8mTF8uF7jnE +# 1. Put the shade in pairing mode. Hold down motor button until flashes, green, then amber +# then the shade will jog. +# 2. On the razberry start inclusion mode +# 3. On the blind, press and hold the motor button until flashing green then let go +# 4. The shade will jog, and then be included. +# ------- +# Then add the zwave battery remotes as a secondary for local control. +# 1. On the razberry start inclusion mode +# 2. with a paperclip press and hold the button in the hole in the back until the remote lights flash +# 3. At the blind, use the paperclip to do a 'quick tap' in the hole in the back of the remote. The light should flash +# 4. On the blind, press and hold the motor button until flashing green then let go +# 5. The shade will jog, and the remote will control the blind. @raZberry_blind::ISA = ('Generic_Item'); @@ -584,17 +711,37 @@ sub new { my $self = {}; bless $self, $class; - push( @{ $$self{states} }, 'up', 'down', 'stop' ); $$self{master_object} = $object; + my $devid_battery = $devid . "-0-128"; $devid = $devid . $zway_suffix unless ( $devid =~ m/-\d+-\d+$/ ); $$self{devid} = $devid; - + $$self{type} = "Blind"; $object->register( $self, $devid, $options ); - #$self->set($object->get_dev_status,$devid,'poll'); $self->{level} = ""; $self->{debug} = $object->{debug}; + $self->{digital} = 0; + $self->{digital} = 1 if ((defined $options) and ($options =~ m/digital/i)); + if ($self->{digital} ) { + push( @{ $$self{states} }, 'down', '10%','20%','30%','40%','50%','60%','70%','80%','90%','up' ); + } else { + push( @{ $$self{states} }, 'down', 'stop', 'up' ); + } + $self->{battery} = 1 if ((defined $options) and ($options =~ m/battery/i)); + if ($self->{battery} ) { + $$self{battery_level} = ""; + $$self{devid_battery} = $devid_battery; + $$self{type} = "Blind.Battery"; + + $object->register( $self, $devid_battery, $options ); + + $self->{battery_alert} = 0; + $self->{battery_poll_seconds} = 12 * 60 * 60; + $self->{battery_timer} = new Timer; + $self->_battery_timer; + } + return $self; } @@ -602,37 +749,48 @@ sub new { sub set { my ( $self, $p_state, $p_setby ) = @_; - if ( $p_setby eq 'poll' ) { + if ( defined $p_setby && $p_setby eq 'poll' ) { $self->{level} = $p_state; my $n_state; if ( $p_state == 0 ) { $n_state = "down"; - } - elsif ( $p_state > 0 ) { - $n_state = "up"; - } + } else { + if ($self->{digital}) { + if ($p_state >= 99) { + $n_state = "up"; + } else { + $n_state = "$p_state%"; + } + } else { + $n_state = "up"; + } + } # stop level? - main::print_log( "[raZberry_blind] Setting value to $n_state. Level is " - . $self->{level} ) - if ( $self->{debug} ); - - $self->SUPER::set($n_state); + main::print_log( "[raZberry_blind] Setting value to $n_state. Level is " . $self->{level} )if ( $self->{debug} ); + $self->SUPER::set($n_state); } else { - if ( ( lc $p_state eq "up" ) - or ( lc $p_state eq "down" ) - or ( lc $p_state eq "stop" ) ) - { + if ($self->{digital}) { + if ( lc $p_state eq "down" ) { + $$self{master_object}->set_dev( $$self{devid}, $p_state ); + } + elsif ( lc $p_state eq "up" ) { + $$self{master_object}->set_dev( $$self{devid}, "level=100" ); + } + elsif (($p_state eq "100%") or ($p_state =~ m/^\d{1,2}\%$/)) { + my ($n_state) = ($p_state =~ /(\d+)%/); + $$self{master_object}->set_dev($$self{devid},"level=$n_state"); + } + else { + main::print_log("[raZberry_blind] Error. Unknown set state $p_state"); + } + } + elsif (( lc $p_state eq "up" ) or ( lc $p_state eq "down" ) or ( lc $p_state eq "stop" )) { $$self{master_object}->set_dev( $$self{devid}, $p_state ); - - #} elsif (($p_state eq "100%") or ($p_state =~ m/^\d{1,2}\%$/)) { - # my ($n_state) = ($p_state =~ /(\d+)%/); - # $$self{master_object}->set_dev($$self{devid},"level=$n_state"); } else { - main::print_log( - "[raZberry_blind] Error. Unknown set state $p_state"); + main::print_log("[raZberry_blind] Error. Unknown set state $p_state"); } } } @@ -655,12 +813,54 @@ sub isfailed { $$self{master_object}->isfailed_dev( $$self{devid} ); } +sub update_data { + my ($self,$data) = @_; + if (defined $data->{battery_level}) { + &main::print_log( "[raZberry_blind] Setting battery value to " . $data->{battery_level} . ".") if ( $self->{debug} ); + $self->{battery_level} = $data->{battery_level}; + } +} + +sub battery_check { + my ($self) = @_; + unless ($self->{battery}) { + main::print_log("[raZberry_blind] ERROR, battery option not defined on this object"); + return; + } + + if ( $self->{battery_level} eq "" ) { + main::print_log("[raZberry_blind] INFO Battery level currently undefined"); + return; + } + main::print_log("[raZberry_blind] INFO Battery currently at " . $self->{battery_level} . "%" ); + if ( ( $self->{battery_level} < 30 ) and ( $self->{battery_alert} == 0 ) ) { + $self->{battery_alert} = 1; + main::speak("Warning, Zwave blind battery has less than 30% charge"); + } + else { + $self->{battery_alert} = 0; + } +} + +sub _battery_timer { + my ($self) = @_; + + $self->{battery_timer}->set( $self->{battery_poll_seconds}, + sub { &raZberry_blind::battery_check($self) }, -1 ); +} + +sub battery_level { + my ($self) = @_; + + return ( $self->{battery_level} ); +} + package raZberry_lock; #only tested with Kwikset 914 @raZberry_lock::ISA = ('Generic_Item'); -use Data::Dumper; +#use Data::Dumper; sub new { my ( $class, $object, $devid, $options ) = @_; @@ -674,12 +874,14 @@ sub new { $devid = $devid . "-0-98"; $$self{devid} = $devid; $$self{devid_battery} = $devid_battery; + $$self{type} = "Lock.Battery"; $object->register( $self, $devid, $options ); $object->register( $self, $devid_battery, $options ); #$self->set($object->get_dev_status,$devid,'poll'); $self->{level} = ""; + $self->{battery_level} = ""; $self->{user_data_delay} = 10; $self->{battery_alert} = 0; $self->{battery_poll_seconds} = 12 * 60 * 60; @@ -695,6 +897,8 @@ sub set { # if level is open/closed its the state. if level is a number its the battery # object states are locked and unlocked, but zwave sees close and open my %map_states; + $p_state = "locked" if (lc $p_state eq "lock"); + $p_state = "unlocked" if (lc $p_state eq "unlock"); $map_states{close} = "locked"; $map_states{open} = "unlocked"; $map_states{locked} = "close"; @@ -703,8 +907,8 @@ sub set { if ( $p_setby eq 'poll' ) { main::print_log( "[raZberry_lock] Setting value to $p_state: " . $map_states{$p_state} - . ". Level is " - . $self->{level} ) + . ". Battery Level is " + . $self->{battery_level} ) if ( $self->{debug} ); if ( ( $p_state eq "open" ) or ( $p_state eq "close" ) ) { $self->SUPER::set( $map_states{$p_state} ); @@ -736,6 +940,12 @@ sub level { return ( $self->{level} ); } +sub battery_level { + my ($self) = @_; + + return ( $self->{battery_level} ); +} + sub ping { my ($self) = @_; @@ -748,18 +958,24 @@ sub isfailed { $$self{master_object}->isfailed_dev( $$self{devid} ); } +sub update_data { + my ($self,$data) = @_; + if (defined $data->{battery_level}) { + &main::print_log( "[raZberry_lock] Setting battery value to " . $data->{battery_level} . ".") if ( $self->{debug} ); + $self->{battery_level} = $data->{battery_level}; + } +} + sub battery_check { my ($self) = @_; - if ( $self->{level} eq "" ) { - main::print_log( - "[raZberry_lock] INFO Battery level currently undefined"); + if ( $self->{battery_level} eq "" ) { + &main::print_log("[raZberry_lock] INFO Battery level currently undefined"); return; } - main::print_log( - "[raZberry_lock] INFO Battery currently at " . $self->{level} . "%" ); - if ( ( $self->{level} < 30 ) and ( $self->{battery_alert} == 0 ) ) { + &main::print_log("[raZberry_lock] INFO Battery currently at " . $self->{battery_level} . "%" ); + if ( ( $self->{battery_level} < 30 ) and ( $self->{battery_alert} == 0 ) ) { $self->{battery_alert} = 1; - main::speak("Warning, Zwave lock battery has less than 30% charge"); + &main::speak("Warning, Zwave lock battery has less than 30% charge"); } else { $self->{battery_alert} = 0; @@ -876,7 +1092,7 @@ sub _update_users { $self->{data}->{retry}++; return ('0'); } - print Dumper $response if ( $self->{debug} > 1 ); +# print Dumper $response if ( $self->{debug} > 1 ); foreach my $key ( keys %{$response} ) { if ( $key =~ m/^[0-9]*$/ ) { #a number, so a user code $self->{users}->{"$key"}->{status} = @@ -913,6 +1129,154 @@ sub set { } } +sub update_data { + my ($self,$data) = @_; +} + +package raZberry_thermostat; + +@raZberry_thermostat::ISA = ('Generic_Item'); + +sub new { + my ( $class, $object, $devid, $options, $deg ) = @_; + + my $self = {}; + bless $self, $class; + if ((defined $deg) and (lc $deg eq "f")) { + push(@{ $$self{states} },60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80); + $self->{units} = "F"; + $self->{min_temp} = 58; + $self->{max_temp} = 80; + + } else { + push(@{ $$self{states} },12,13,14,15,16,17,18,19,20,21,22,23,24,25,16,27,28,29,30); + $self->{units} = "C"; + $self->{min_temp} = 10; + $self->{max_temp} = 30; + } + + $$self{master_object} = $object; + $devid = $devid . "-0-67" unless ( $devid =~ m/-\d+-\d+$/ ); + $$self{devid} = $devid; + $$self{type} = "Thermostat"; + + $object->register( $self, $devid, $options ); + + $self->{level} = ""; + + $self->{debug} = $object->{debug}; + return $self; + +} + +sub set { + my ( $self, $p_state, $p_setby ) = @_; + if ( $p_setby eq 'poll' ) { + $self->{level} = $p_state; + $self->SUPER::set($p_state); + } + else { + if (($p_state < $self->{min_temp}) or ($p_state > $self->{max_temp})) { + main::pring_log("[raZberry]: WARNING not setting level to $p_state since out of bounds " . $self->{min_temp} . ":" . $self->{max_temp}); + } else { + $$self{master_object}->set_dev( $$self{devid}, "level=$p_state" ); + } + } +} + +sub level { + my ($self) = @_; + + return ( $self->{level} ); +} + +sub ping { + my ($self) = @_; + + $$self{master_object}->ping_dev( $$self{devid} ); +} + +sub get_units { + my ($self) = @_; + + return ( $self->{units} ); +} + +sub isfailed { + my ($self) = @_; + + $$self{master_object}->isfailed_dev( $$self{devid} ); +} + +sub update_data { + my ($self,$data) = @_; + #if units is F then rescale states + + if ($data->{units} =~ m/F/) { + @{ $$self{states} } = ( + 58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80 + ); + } + $self->{min_temp} = $data->{temp_min}; + $self->{max_temp} = $data->{temp_max}; + main::print_log("In set, units = " . $data->{units} . " max = " . $data->{temp_max} . " min = " . $data->{temp_min}) if ($self->{debug}); + +} + +package raZberry_temp_sensor; + +@raZberry_temp_sensor::ISA = ('Generic_Item'); + +sub new { + my ( $class, $object, $devid, $options ) = @_; + + my $self = {}; + bless $self, $class; + + $$self{master_object} = $object; + $devid = $devid . "-0-49-1" unless ( $devid =~ m/-\d+-\d+$/ ); + $$self{devid} = $devid; + $$self{type} = "Thermostat Sensor"; + + $object->register( $self, $devid, $options ); + + $self->{debug} = $object->{debug}; + return $self; + +} + +sub set { + my ( $self, $p_state, $p_setby ) = @_; + if ( $p_setby eq 'poll' ) { + $self->{level} = $p_state; + + $self->SUPER::set($p_state); + } +} + +sub level { + my ($self) = @_; + + return ( $self->{level} ); +} + +sub ping { + my ($self) = @_; + + $$self{master_object}->ping_dev( $$self{devid} ); +} + + +sub isfailed { + my ($self) = @_; + + $$self{master_object}->isfailed_dev( $$self{devid} ); +} + +sub update_data { + my ($self,$data) = @_; +} + package raZberry_binary_sensor; @raZberry_binary_sensor::ISA = ('Generic_Item'); @@ -921,10 +1285,11 @@ sub new { my $self = {}; bless $self, $class; - push( @{ $$self{states} }, 'on', 'off'); + #push( @{ $$self{states} }, 'on', 'off'); I'm not sure we should set the states here, since it's not a controlable item? $$self{master_object} = $object; $devid = $devid . "-0-48-1"; + $$self{type} = "Binary Sensor"; $$self{devid} = $devid; $object->register( $self, $devid, $options ); @@ -935,20 +1300,12 @@ sub new { } -sub z_log { - my $self = shift; - my $who = ref ($self) . " (" . $self->{devid}.")"; - main::print_log("[$who] @_"); -} - - sub level { my ($self) = @_; return ( $self->{level} ); } - sub ping { my ($self) = @_; @@ -961,6 +1318,10 @@ sub isfailed { $$self{master_object}->isfailed_dev( $$self{devid} ); } +sub update_data { + my ($self,$data) = @_; +} + package raZberry_openclose; @raZberry_openclose::ISA = ('raZberry_binary_sensor'); @@ -968,8 +1329,8 @@ sub new { my ( $class, $object, $devid, $options ) = @_; my $self = $class->SUPER::new($object, $devid, $options); - $$self{states} = (); - push( @{ $$self{states} }, 'open', 'closed'); + #$$self{states} = (); + #push( @{ $$self{states} }, 'open', 'closed'); return $self; } @@ -985,34 +1346,82 @@ sub set { else { $n_state = "closed"; } - $self->z_log("Setting value to $n_state. Level is " . $self->{level} ) if ( $self->{debug} ); + main::print_log("[raZberry] Setting openclose value to $n_state. Level is " . $self->{level} ) if ( $self->{debug} ); $self->SUPER::set($n_state); } else { - $self->z_log("Error. Can not set state $p_state for sensors"); + main::print_log("[raZberry] ERROR Can not set state $p_state for openclose"); } } -package raZberry_door; -@raZberry_door::ISA = ('raZberry_openclose'); +package raZberry_battery; + +@raZberry_battery::ISA = ('Generic_Item'); + sub new { my ( $class, $object, $devid, $options ) = @_; - my $self =$class->SUPER::new($object, $devid, $options); - $self->set_fp_icon_set('door2'); - $self->z_log("created..."); + my $self = {}; + bless $self, $class; + push( @{ $$self{states} }, 'locked', 'unlocked' ); + + $$self{master_object} = $object; + $devid = $devid . "-0-128"; + $$self{devid} = $devid; + $$self{type} = "Battery"; + + $object->register( $self, $devid, $options ); + + #$self->set($object->get_dev_status,$devid,'poll'); + $self->{battery_level} = ""; + $self->{battery_alert} = 0; + $self->{battery_poll_seconds} = 12 * 60 * 60; + $self->{battery_timer} = new Timer; + $self->{debug} = $object->{debug}; +# $self->_battery_timer; return $self; } -package raZberry_window; -@raZberry_window::ISA = ('raZberry_openclose'); -sub new { - my ( $class, $object, $devid, $options ) = @_; +sub battery_level { + my ($self) = @_; - my $self =$class->SUPER::new($object, $devid, $options); - $self->set_fp_icon_set('window'); - $self->z_log("created..."); - return $self; + return ( $self->{battery_level} ); +} + +sub ping { + my ($self) = @_; + + $$self{master_object}->ping_dev( $$self{devid} ); +} + +sub isfailed { + my ($self) = @_; + + $$self{master_object}->isfailed_dev( $$self{devid} ); +} + +sub update_data { + my ($self,$data) = @_; + + $self->{battery_level} = $data->{battery_level}; + $self->SUPER::set($self->{battery_level}); + +} + +sub battery_check { + my ($self) = @_; + if ( $self->{battery_level} eq "" ) { + main::print_log("[raZberry_battery] INFO Battery level currently undefined"); + return; + } + main::print_log("[raZberry_battery] INFO Battery currently at " . $self->{battery_level} . "%" ); + if ( ( $self->{battery_level} < 30 ) and ( $self->{battery_alert} == 0 ) ) { + $self->{battery_alert} = 1; + main::speak("Warning, Zwave battery has less than 30% charge"); + } + else { + $self->{battery_alert} = 0; + } } 1; diff --git a/lib/read_table_A.pl b/lib/read_table_A.pl index cf5838080..b4cc1070c 100644 --- a/lib/read_table_A.pl +++ b/lib/read_table_A.pl @@ -1386,7 +1386,165 @@ sub read_table_A { $code .= "use Philips_Hue;\n"; } } + #-------------- RaZberry Objects ----------------- + elsif ( $type eq "RAZBERRY_CONTROLLER" ) { + ($address, $name, $grouplist, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + if ($other) { + $object = "raZberry('$address','$other')"; + } + else { + $object = "raZberry('$address')"; + } + $code .= "use raZberry;\n"; + } + elsif ( $type eq "RAZBERRY_COMM" ) { + my ($controller); + ($name, $controller, $grouplist ) = @item_info; + $object = "raZberry_comm(\$" . $controller . ")"; + } + elsif ( $type eq "RAZBERRY_DIMMER" ) { + my ($devid, $controller); + ($devid, $name, $grouplist, $controller, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + if ($other) { + $object = "raZberry_dimmer(\$" . $controller . ",'$devid','$other')"; + } + else { + $object = "raZberry_dimmer(\$" . $controller . ",'$devid')"; + } + } + elsif ( $type eq "RAZBERRY_SWITCH" ) { + my ($devid, $controller); + ($devid, $name, $grouplist, $controller, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + if ($other) { + $object = "raZberry_switch(\$" . $controller . ",'$devid','$other')"; + } + else { + $object = "raZberry_switch(\$" . $controller . ",'$devid')"; + } + } + elsif ( $type eq "RAZBERRY_BLIND" ) { + my ($devid, $controller); + ($devid, $name, $grouplist, $controller, $other ) = @item_info; + #$other = join ', ', ( map { "'$_'" } @other ); # Quote data + if ($other) { + $object = "raZberry_blind(\$" . $controller . ",'$devid','$other')"; + } + else { + $object = "raZberry_blind(\$" . $controller . ",'$devid')"; + } + } + elsif ( $type eq "RAZBERRY_LOCK" ) { + my ($devid, $controller); + ($devid, $name, $grouplist, $controller, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + if ($other) { + $object = "raZberry_lock(\$" . $controller . ",'$devid','$other')"; + } + else { + $object = "raZberry_lock(\$" . $controller . ",'$devid')"; + } + } + elsif ( $type eq "RAZBERRY_THERMOSTAT" ) { + my ($devid, $controller); + ($devid, $name, $grouplist, $controller, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + if ($other) { + $object = "raZberry_thermostat(\$" . $controller . ",'$devid','$other')"; + } + else { + $object = "raZberry_thermostat(\$" . $controller . ",'$devid')"; + } + } + elsif ( $type eq "RAZBERRY_TEMP_SENSOR" ) { + my ($devid, $controller); + ($devid, $name, $grouplist, $controller, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + if ($other) { + $object = "raZberry_temp_sensor(\$" . $controller . ",'$devid','$other')"; + } + else { + $object = "raZberry_temp_sensor(\$" . $controller . ",'$devid')"; + } + } + elsif ( $type eq "RAZBERRY_BINARY_SENSOR" ) { + my ($devid, $controller); + ($devid, $name, $grouplist, $controller, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + if ($other) { + $object = "raZberry_binary_sensor(\$" . $controller . ",'$devid','$other')"; + } + else { + $object = "raZberry_binary_sensor(\$" . $controller . ",'$devid')"; + } + } + elsif ( $type eq "RAZBERRY_BATTERY" ) { + my ($devid, $controller); + ($devid, $name, $grouplist, $controller, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + if ($other) { + $object = "raZberry_battery(\$" . $controller . ",'$devid','$other')"; + } + else { + $object = "raZberry_battery(\$" . $controller . ",'$devid')"; + } + } + #-------------- End of RaZberry Objects ----------------- + + # -[ MySensors ]------------------------------------------------------ + elsif ( $type eq "MYS_INTERFACE" ) { + require 'MySensors.pm'; + my ( $gw_type, $long_name, $port ); + ( $name, $long_name, $gw_type, $port, $grouplist, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + $object = "MySensors::Interface('$gw_type', '$port', '$long_name', $other)"; + } + elsif ( $type eq "MYS_NODE" ) { + require 'MySensors.pm'; + my ( $parent, $long_name ); + ( $address, $name, $long_name, $parent, $grouplist, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + $object = "MySensors::Node($address, '$long_name', $parent, $other)"; + } + elsif ( $type eq "MYS_BINARY" ) { + require 'MySensors.pm'; + my ( $parent, $long_name ); + ( $address, $name, $long_name, $parent, $grouplist, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + $object = "MySensors::Binary($address, '$long_name', $parent, $other)"; + } + elsif ( $type eq "MYS_DOOR" ) { + require 'MySensors.pm'; + my ( $parent, $long_name ); + ( $address, $name, $long_name, $parent, $grouplist, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + $object = "MySensors::Door($address, '$long_name', $parent, $other)"; + } + elsif ( $type eq "MYS_MOTION" ) { + require 'MySensors.pm'; + my ( $parent, $long_name ); + ( $address, $name, $long_name, $parent, $grouplist, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + $object = "MySensors::Motion($address, '$long_name', $parent, $other)"; + } + elsif ( $type eq "MYS_TEMPERATURE" ) { + require 'MySensors.pm'; + my ( $parent, $long_name ); + ( $address, $name, $long_name, $parent, $grouplist, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + $object = "MySensors::Temperature($address, '$long_name', $parent, $other)"; + } + elsif ( $type eq "MYS_HUMIDITY" ) { + require 'MySensors.pm'; + my ( $parent, $long_name ); + ( $address, $name, $long_name, $parent, $grouplist, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + $object = "MySensors::Humidity($address, '$long_name', $parent, $other)"; + } + #-------------- AD2 Objects ----------------- elsif ( $type eq "AD2_INTERFACE" ) { require AD2; @@ -1466,56 +1624,6 @@ sub read_table_A { &::MainLoop_pre_add_hook( \&Wink::GetDevicesAndStatus, 1 ); } } - # -[ MySensors ]------------------------------------------------------ - elsif ( $type eq "MYS_INTERFACE" ) { - require 'MySensors.pm'; - my ( $gw_type, $long_name, $port ); - ( $name, $long_name, $gw_type, $port, $grouplist, @other ) = @item_info; - $other = join ', ', ( map { "'$_'" } @other ); # Quote data - $object = "MySensors::Interface('$gw_type', '$port', '$long_name', $other)"; - } - elsif ( $type eq "MYS_NODE" ) { - require 'MySensors.pm'; - my ( $parent, $long_name ); - ( $address, $name, $long_name, $parent, $grouplist, @other ) = @item_info; - $other = join ', ', ( map { "'$_'" } @other ); # Quote data - $object = "MySensors::Node($address, '$long_name', $parent, $other)"; - } - elsif ( $type eq "MYS_BINARY" ) { - require 'MySensors.pm'; - my ( $parent, $long_name ); - ( $address, $name, $long_name, $parent, $grouplist, @other ) = @item_info; - $other = join ', ', ( map { "'$_'" } @other ); # Quote data - $object = "MySensors::Binary($address, '$long_name', $parent, $other)"; - } - elsif ( $type eq "MYS_DOOR" ) { - require 'MySensors.pm'; - my ( $parent, $long_name ); - ( $address, $name, $long_name, $parent, $grouplist, @other ) = @item_info; - $other = join ', ', ( map { "'$_'" } @other ); # Quote data - $object = "MySensors::Door($address, '$long_name', $parent, $other)"; - } - elsif ( $type eq "MYS_MOTION" ) { - require 'MySensors.pm'; - my ( $parent, $long_name ); - ( $address, $name, $long_name, $parent, $grouplist, @other ) = @item_info; - $other = join ', ', ( map { "'$_'" } @other ); # Quote data - $object = "MySensors::Motion($address, '$long_name', $parent, $other)"; - } - elsif ( $type eq "MYS_TEMPERATURE" ) { - require 'MySensors.pm'; - my ( $parent, $long_name ); - ( $address, $name, $long_name, $parent, $grouplist, @other ) = @item_info; - $other = join ', ', ( map { "'$_'" } @other ); # Quote data - $object = "MySensors::Temperature($address, '$long_name', $parent, $other)"; - } - elsif ( $type eq "MYS_HUMIDITY" ) { - require 'MySensors.pm'; - my ( $parent, $long_name ); - ( $address, $name, $long_name, $parent, $grouplist, @other ) = @item_info; - $other = join ', ', ( map { "'$_'" } @other ); # Quote data - $object = "MySensors::Humidity($address, '$long_name', $parent, $other)"; - } else { print "\nUnrecognized .mht entry: $record\n"; return;