AnyEvent-Onkyo

 view release on metacpan or  search on metacpan

lib/AnyEvent/Onkyo.pm  view on Meta::CPAN

use strict;
use warnings;
package AnyEvent::Onkyo;
{
  $AnyEvent::Onkyo::VERSION = '1.130220';
}
use base 'Device::Onkyo';
use AnyEvent::Handle;
use AnyEvent::SerialPort;
use Carp qw/croak carp/;
use Sub::Name;
use Scalar::Util qw/weaken/;

use constant {
  DEBUG => $ENV{ANYEVENT_ONKYO_DEBUG},
};


# ABSTRACT: AnyEvent module for controlling Onkyo/Integra AV equipment


sub new {
  my ($pkg, %p) = @_;
  croak $pkg.'->new: callback parameter is required' unless ($p{callback});
  my $self = $pkg->SUPER::new(device => 'discover', %p);
  $self;
}


sub command {
  my $self = shift;
  my $cv = AnyEvent->condvar;
  my $weak_cv = $cv;
  weaken $weak_cv;
  $self->SUPER::command(@_, subname 'command_cb' => sub {
                          $weak_cv->send() if ($weak_cv);
                        });
  return $cv;
}

sub _open {
  my $self = shift;
  $self->SUPER::_open($self->_open_condvar);
  return 1;
}

sub _open_tcp_port {
  my ($self, $cv) = @_;
  my $dev = $self->{device};
  print STDERR "Opening $dev as tcp socket\n" if DEBUG;
  my ($host, $port) = split /:/, $dev, 2;
  $port = $self->{port} unless (defined $port);
  $self->{handle} =
    AnyEvent::Handle->new(connect => [$host, $port],
                          on_connect => subname('tcp_connect_cb' => sub {
                            my ($hdl, $h, $p) = @_;
                            warn ref $self, " connected to $h:$p\n" if DEBUG;
                            $cv->send();
                          }),
                          on_connect_error =>
                          subname('tcp_connect_error_cb' => sub {
                            my ($hdl, $msg) = @_;
                            my $err =
                              (ref $self).": Can't connect to $dev: $msg";
                            warn "Connect error: $err\n" if DEBUG;
                            $self->cleanup($err);
                            $cv->croak;
                          }));
  return $cv;
}

sub _open_serial_port {
  my ($self, $cv) = @_;
  $self->{handle} =
    AnyEvent::SerialPort->new(serial_port =>
                              [ $self->device,
                                [ baudrate => $self->baud ] ]);
  $cv->send();
  return $cv;
}

sub _handle_setup {
  my $self = shift;
  my $handle = $self->{handle};
  my $weak_self = $self;
  weaken $weak_self;

  $handle->on_error(subname('on_error' => sub {
                              my ($hdl, $fatal, $msg) = @_;
                              print STDERR $hdl.": error $msg\n" if DEBUG;
                              $hdl->destroy;
                              if ($fatal) {
                                $weak_self->cleanup($msg);
                              }
                            }));

  $handle->on_eof(subname('on_eof' => sub {
                            my ($hdl) = @_;
                            print STDERR $hdl.": eof\n" if DEBUG;
                            $weak_self->cleanup('connection closed');
                          }));

  $handle->on_read(subname 'on_read_cb' => sub {
    my ($hdl) = @_;
    $hdl->push_read(ref $self => $self,
                    subname 'push_read_cb' => sub {
                      $weak_self->{callback}->(@_);
                      $weak_self->_write_now();
                      return 1;
                    });
  });

  $self->{handle}->on_timeout($self->{on_timeout}) if ($self->{on_timeout});
  $self->{handle}->timeout($self->{timeout}) if ($self->{timeout});
  1;
}

sub DESTROY {
  $_[0]->cleanup;
}


sub cleanup {
  my ($self, $error) = @_;
  print STDERR $self."->cleanup\n" if DEBUG;
  $self->{handle}->destroy if ($self->{handle});
  delete $self->{handle};
}

sub _open_condvar {
  my $self = shift;
  print STDERR $self."->open_condvar\n" if DEBUG;
  my $cv = AnyEvent->condvar;
  my $weak_self = $self;
  weaken $weak_self;

  $cv->cb(subname 'open_cb' => sub {
            print STDERR "start cb ", $weak_self->{handle}, " @_\n" if DEBUG;
            $weak_self->_handle_setup();
            $weak_self->_write_now();
          });
  $weak_self->{_waiting} = ['fake for async open'];
  return $cv;
}

sub _real_write {
  my ($self, $str, $desc, $cb) = @_;
  print STDERR "Sending: ", $desc, "\n" if DEBUG;
  $self->{handle}->push_write($str);
}

sub _time_now {
  AnyEvent->now;
}


sub anyevent_read_type {
  my ($handle, $cb, $self) = @_;

  my $weak_self = $self;
  weaken $weak_self;

  subname 'anyevent_read_type_reader' => sub {
    my ($handle) = @_;
    my $rbuf = \$handle->{rbuf};
    while (1) { # read all message from the buffer
      print STDERR "Before: ", (unpack 'H*', $$rbuf||''), "\n" if DEBUG;
      my $res = $weak_self->read_one($rbuf);
      return unless ($res);
      print STDERR "After: ", (unpack 'H*', $$rbuf), "\n" if DEBUG;
      $res = $cb->($res);
    }
  }
}

1;

__END__
=pod

=head1 NAME

AnyEvent::Onkyo - AnyEvent module for controlling Onkyo/Integra AV equipment

=head1 VERSION

version 1.130220

=head1 SYNOPSIS

  use AnyEvent;
  use AnyEvent::Onkyo;
  $| = 1;
  my $cv = AnyEvent->condvar;
  my $onkyo = AnyEvent::Onkyo->new(device => 'discover',
                                   callback => sub {
                                     my $cmd = shift;
                                     print "$cmd\n";
                                     unless ($cmd =~ /^NLS/) {
                                       $cv->send;
                                     }
                                   });
  $onkyo->command('volume up');
  $cv->recv;

=head1 DESCRIPTION

AnyEvent module for controlling Onkyo/Integra AV equipment.

B<IMPORTANT:> This is an early release and the API is still subject to
change. The serial port usage is entirely untested.

=head1 METHODS

=head2 C<new(%params)>

Constructs a new AnyEvent::Onkyo object.  The supported parameters are:

=over

=item device



( run in 1.999 second using v1.01-cache-2.11-cpan-5735350b133 )