diff --git a/lib/Huawei/Hilink.pm b/lib/Huawei/Hilink.pm
new file mode 100755
index 0000000000000000000000000000000000000000..79ee3d268bfbef45ef4a2add1b4685ee3720c069
--- /dev/null
+++ b/lib/Huawei/Hilink.pm
@@ -0,0 +1,252 @@
+#!/usr/bin/perl
+
+package Huawei::Hilink;
+
+use strict;
+
+use Data::Dumper;
+$Data::Dumper::Indent= 1;
+
+use HTTP::Request;
+use LWP::UserAgent;
+use XML::Simple;
+
+my $verbose= 0;
+my $debug= 0;
+
+my %queries=
+(
+  'information'        => { api => '/api/device/information',   auth => 'none' },
+  'signal'             => { api => '/api/device/signal',        auth => 'none' },
+  'module-switch'      => { api => '/api/global/module-switch', auth => 'none' },
+  'current-plmn'       => { api => '/api/net/current-plmn',     auth => 'none' },
+  'net-mode'           => { api => '/api/net/net-mode',         auth => 'none' },
+  'basic-settings'     => { api => '/api/wlan/basic-settings',  auth => 'none' },
+  'converged-status'   => { api => '/api/monitoring/converged-status',        auth => 'none' },
+
+  'traffic-statistics' => { api => '/api/monitoring/traffic-statistics', auth => 'token' },
+  'status'             => { api => '/api/monitoring/status',             auth => 'token' },
+
+  # does not work
+  'sms-list'           => { api => '/api/sms/sms-list',             auth => 'token' },
+  'sms' => { api => '/api/sms', auth => 'none' },
+  'ussd'             => { api => '/api/ussd/get',             auth => 'none' },
+  'sdc'             => { api => '/api/sdcard/sdcard',             auth => 'none' },
+);
+
+__PACKAGE__->main() unless caller();
+
+sub new
+{
+  my $class= shift;
+  my @pars= @_;
+
+  my $self= bless ({}, $class);
+  $self->set(@_);
+}
+
+sub set
+{
+  my $self= shift;
+  my %par= @_;
+  foreach my $par (keys %par) { $self->{$par}= $par{$par} }
+  $self;
+}
+
+sub get_session
+{
+  my $self= shift;
+
+  return ($self->{_session_id}, $self->{_token}) if (exists($self->{_session_id}) && exists ($self->{_token}));
+ 
+  my $url= 'http://' . $self->{gwip} . '/api/webserver/SesTokInfo';
+  my $ua= $self->{_ua} || ($self->{_ua}= new LWP::UserAgent());
+  my $resp= $ua->get($url);
+
+  unless ($resp->is_success)
+  {
+    print __LINE__, " ua failed: ", $resp->status_line, "\n";
+    return (undef, undef);
+  }
+  my $sess= $resp->decoded_content;
+
+  # print __LINE__, " sess=[$sess]\n" if ($debug);
+  my $ref= XMLin($sess);
+  # print __LINE__, " ref: ", Dumper($ref);
+  return ($self->{_session_id}= $ref->{SesInfo}, $self->{_token}= $ref->{TokInfo}) if (defined ($ref));
+
+  (undef, undef);
+}
+
+sub send_sms
+{
+  my $self= shift;
+  my $phone= shift;
+  my $message= shift;
+
+  my ($session_id, $token)= $self->get_session();
+  unless (defined ($session_id))
+  {
+    print __LINE__, " no session_id obtained\n" if ($debug);
+    return undef;
+  }
+
+  print __LINE__, " send-sms phone=[$phone] message=[$message]\n" if ($debug);
+
+  my $url= 'http://'. $self->{gwip}. '/api/sms/send-sms';
+
+=begin comment
+
+  my @cmd= ('curl', $url,
+            '-H', 'Cookie: '. $session_id, '-H', '__RequestVerificationToken: ' . $token,
+            '--data', "<?xml version='1.0' encoding='UTF-8'?><request><Index>-1</Index><Phones><Phone>$phone</Phone></Phones><Sca></Sca><Content>$message</Content><Length>-1</Length><Reserved>1</Reserved><Date>-1</Date></request>");
+  push (@cmd, '-v') if ($debug);
+
+  print __LINE__, " cmd: ", join(' ', @cmd), "\n" if ($debug);
+  system(@cmd);
+
+=end comment
+=cut
+
+  my $submit_data= "<?xml version='1.0' encoding='UTF-8'?><request><Index>-1</Index><Phones><Phone>$phone</Phone></Phones><Sca></Sca><Content>$message</Content><Length>-1</Length><Reserved>1</Reserved><Date>-1</Date></request>";
+
+  my $ua= $self->{_ua} || ($self->{_ua}= new LWP::UserAgent());
+  $ua->default_header('Cookie', $session_id);
+  $ua->default_header(':__RequestVerificationToken', $token); # prevent header name canonication, see perldoc HTTP::Headers under NON-CANONICALIZED FIELD NAMES
+
+  my $resp= $ua->post($url, 'Content-Type' => 'text/html', 'Content' => $submit_data);
+  unless ($resp->is_success)
+  {
+    print __LINE__, " ua failed: ", $resp->status_line, "\n";
+    return (undef, undef);
+  }
+  my $result_data= $resp->decoded_content;
+  my $ref= XMLin($result_data);
+  print __LINE__, " ref: ", Dumper($ref);
+
+  1;
+}
+
+sub list_sms
+{
+  my $self= shift;
+  my $page_index= shift || 1;
+
+  # see: python/dopstar-huawei-modem-python-api-client-02f93dc
+  my $url= 'http://'. $self->{gwip} . '/api/sms/sms-list';
+  my $ua= $self->{_ua} || ($self->{_ua}= new LWP::UserAgent());
+
+    my ($session_id, $token)= $self->get_session();
+    $ua->default_header('Cookie', $session_id);
+    $ua->default_header(':__RequestVerificationToken', $token);
+    $ua->default_header('X-Requested-With', 'XMLHttpRequest'); # 
+
+  my $submit_data= <<"EOX";
+<request>
+  <PageIndex>1</PageIndex>
+  <ReadCount>10</ReadCount>
+  <BoxType>1</BoxType>
+  <SortType>0</SortType>
+  <Ascending>0</Ascending>
+  <UnreadPreferred>1</UnreadPreferred>
+</request>
+EOX
+  my $resp= $ua->post($url, 'Content-Type' => 'text/html', 'Content' => $submit_data);
+  unless ($resp->is_success)
+  {
+    print __LINE__, " ua failed: ", $resp->status_line, "\n";
+    return (undef, undef);
+  }
+  my $result_data= $resp->decoded_content;
+  my $ref= XMLin($result_data);
+  print __LINE__, " ref: ", Dumper($ref);
+
+}
+
+sub query
+{
+  my $self= shift;
+  my $op_code= shift;
+
+  my $info= $queries{$op_code};
+  unless (defined ($info))
+  {
+    print __LINE__, " invalid query\n" if ($debug);
+    return undef;
+  }
+
+  my $url= 'http://'. $self->{gwip} . $info->{api};
+  my $ua= $self->{_ua} || ($self->{_ua}= new LWP::UserAgent());
+
+  if ($info->{auth} eq 'token')
+  {
+    my ($session_id, $token)= $self->get_session();
+    unless (defined ($session_id))
+    {
+      print __LINE__, " no session_id obtained\n" if ($debug);
+      return undef;
+    }
+
+    $ua->default_header('Cookie', $session_id);
+    $ua->default_header(':__RequestVerificationToken', $token);
+  # $ua->default_header('X-Requested-With', 'XMLHttpRequest');
+  }
+
+# push (@cmd, '-v') if ($debug);
+# print __LINE__, " cmd: ", join(' ', @cmd), "\n" if ($debug);
+# system(@cmd);
+
+  my $resp= $ua->get($url);
+  unless ($resp->is_success)
+  {
+    print __LINE__, " ua failed: ", $resp->status_line, "\n";
+    return (undef, undef);
+  }
+  my $data= $resp->decoded_content;
+  my $ref= XMLin($data);
+  print __LINE__, " ref: ", Dumper($ref);
+
+  1;
+}
+
+sub main
+{
+  my @PARS;
+  my $gwip= '192.168.8.1';
+  while (my $arg= shift (@ARGV))
+  {
+       if ($arg eq '--') {}
+    elsif ($arg =~ /^--(.*)/)
+    { my ($opt, $val)= split('-', $1, 2);
+      if ($opt eq 'debug') { $debug= 1; }
+    }
+    elsif ($arg =~ /^-/) {}
+    else { push (@PARS, $arg); }
+  }
+
+  unless (@PARS)
+  {
+    system('perldoc', $0);
+    exit(1);
+  }
+
+  my $obj= new Huawei::Hilink (gwip => $gwip);
+
+  my $op_code= shift (@PARS);
+  print __LINE__, " op_code=[$op_code]\n" if ($debug);
+     if ($op_code eq 'send-sms') { $obj->send_sms(@PARS); } 
+  elsif ($op_code eq 'list-sms') { $obj->list_sms(@PARS); } 
+  elsif (exists($queries{$op_code})) { $obj->query($op_code); } 
+
+  print __LINE__, " obj: ", Dumper($obj) if ($debug);
+
+  exit(0);
+}
+
+1;
+
+__END__
+
+=head1 AUTHOR
+
+