#!/usr/bin/perl
# Copyright (C) 2017 TTK Ciar
# Available for unlimited distribution and use.
# The copyright is just so someone else cannot claim ownership and sue me for use of my own code.

## Command line utility for controlling the Synaccess NP-05B network power strip.

use strict;
use warnings;
use JSON;
use Device::Power::Synaccess::NP05B;

my @DOCS;
my %OPT = (v => 1);
foreach my $arg (@ARGV) {
    if    ($arg =~ /^\-+(.+?)\=(.*)/) { $OPT{$1} = $2; }
    elsif ($arg =~ /^\-+(v+)$/      ) { $OPT{v}  = length($1) + 1; }
    elsif ($arg =~ /^\-+q$/         ) { $OPT{v}  = 0;  }
    elsif ($arg =~ /^\-+quiet$/     ) { $OPT{v}  = 0;  }
    elsif ($arg =~ /^\-+(.+)/       ) { $OPT{$1} = -1; }
    else { push (@DOCS, $arg); }
}

my $PROJECT_NAME  = 'np05bctl';

exit(main(\@DOCS, \%OPT));

sub main {
    my ($docs_ar, $opt_hr) = @_;
    my $o = Device::Power::Synaccess::NP05B->new(%$opt_hr);
    $docs_ar = ['status'] unless (@$docs_ar);
    my ($ok, @errs) = $o->connect;
    die safely_to_json(['connect', $ok, @errs])."\n" if ($ok eq 'ERROR');
    my $ix = 0;
    foreach my $op (@$docs_ar) {
        $op = lc($op);
        $ok = '';
        if ($op eq 'login') { ($ok, @errs) = $o->login; }
        if ($op eq 'pstat') { ($ok, @errs) = $o->power_status; }
        if ($op eq 'pset' ) { ($ok, @errs) = $o->power_set($docs_ar->[$ix+1], $docs_ar->[$ix+2]); }
        if ($op eq 'stat' ) { ($ok, @errs) = $o->status; }
        print safely_to_json([$op, $ok, @errs])."\n" unless(opt('q') && $ok eq '');
        $ix++;
    }
    $o->logout;
    return 0;
}

sub safely_to_json {
    my ($r) = @_;
    my $js_opt = {canonical => 1, space_after => 1, indent => opt('p',0)};
    my $js;
    eval { $js = JSON::to_json($r, $js_opt) };
    return $js if (defined($js));
    return "JSON encode error ($@)";
}

sub opt {
    my ($name, $default_value, $alt_hr) = @_;
    return def($OPT{$name}, $alt_hr->{$name}, $default_value);
}

sub def {
    foreach my $v (@_) { return $v if (defined($v)); }
    return undef;
}

sub usage {
    print "usage: $0 [options] [command] [parameters] [command ...]\n";
    print "\$ $0 --addr=12.34.56 --user=bob --pass=w00t login pset 3 1\n";
    print "options:\n";
    print "   -p         Pretty-format json output\n";
    print "   -q         Do not print results of command to stdout\n";
    print "   --addr=<#> Dotted IP address of NP-05B (default: 192.168.1.100)\n";
    print "   --user=<s> Username for logging into NP-05B (default: admin)\n";
    print "   --pass=<s> Password for logging into NP-05B (default: admin)\n";
    print "commands:\n";
    print "   login      Authenticate with the NP-05B\n";
    print "   pset X Y   Turn outlet X on (Y=1) or off (Y=2), for X=1..5\n";
    print "   pstat      Show which outlets are on or off (json)\n";
    print "   stat       Dump the NP-05B's system configuration (json)\n";
    return 1;
}

=head1 NAME

np05bctl -- Command line utility for accessing the Synaccess NP-05B networked power strip

=head1 SYNOPSIS

    $ np05bctl --addr=10.1.2.3 --user=bob --pass=w00t login pset 3 1
    ["login", "OK"]
    ["pstat", "OK", {"1": 0, "2": 0, "3": 1, "4": 0, "5": 0}]

    $ np05bctl --addr=10.1.2.3 -p stat
    [
       "stat",
       "OK",
       {
          "eth": "on",
          "gw": "192.168.1.1",
          "ip": "10.1.2.3",
          "mac": "00:90:c2:12:34:56",
          "mask": "255.255.0.0",
          "model": "NP-05B",
          "port_http": "80",
          "port_telnet": "23",
          "power_hr": {
             "1": 0,
             "2": 0,
             "3": 1,
             "4": 0,
             "5": 0
          },
          "s_gw": "192.168.1.1",
          "s_ip": "10.1.2.3",
          "s_mask": "255.255.0.0",
          "source": "static",
          "src_ip": "0.0.0.0"
       }
    ]

=head1 ABSTRACT

C<np05bctl> is a light wrapper around L<Device::Power::Synaccess::NP05B>, providing its functionality as a command line utility.

Please see L<Device::Power::Synaccess::NP05B> for details.

=head1 OPTIONS

   -p         Pretty-format json output
   -q         Do not print results of command to stdout
   --addr=<#> Dotted IP address of NP-05B (default: 192.168.1.100)
   --user=<s> Username for logging into NP-05B (default: admin)
   --pass=<s> Password for logging into NP-05B (default: admin)

=head1 COMMANDS

Command results are written to stdout as JSON of one of the two formats:

    [<operation>, "OK", <optional JSON object>]
    [<operation>, "ERROR", <error description>]

Commands may be stacked.  For instance, to authenticate and then turn on ports 3 and 4:

    np05bctl login pset 3 1 pset 4 1
    ["login", "OK"]
    ["pstat", "OK", {"1": 0, "2": 0, "3": 1, "4": 0, "5": 0}]
    ["pstat", "OK", {"1": 0, "2": 0, "3": 1, "4": 1, "5": 0}]

=over 4

=item login

Authenticate with the C<NP-05B>.  Depending on your configuration this might not be necessary.

=item pset X Y

Turn outlet I<X> on (I<Y>=1) or off (I<Y>=2).

For the author's five-port device, the valid range for I<X> is 1..5

=item pstat

Show which outlets are on or off.

=item stat

Dump the NP-05B's system configuration.

=back

=head1 BUGS

At the time of this writing, following C<login> with C<stat> does not work correctly. If you have configured your C<NP-05B> to allow unauthenticated commands, C<stat> without C<login> works fine. This is a fairly heinous bug and the author will be fixing it.

=head1 TO DO

Support additional features as they become implemented in L<Device::Power::Synaccess::NP05B> (also developed by this author).

=head1 AUTHOR

TTK Ciar E<lt>ttk@ciar.orgE<gt>

=head1 COPYRIGHT

You may use and distribute this program under the same terms as Perl itself.

=cut
