File Coverage

blib/lib/Net/MQTT/Simple/One_Shot_Loader.pm
Criterion Covered Total %
statement 21 49 42.8
branch 0 12 0.0
condition 0 2 0.0
subroutine 7 13 53.8
pod 0 1 0.0
total 28 77 36.3


line stmt bran cond sub pod time code
1             package Net::MQTT::Simple::One_Shot_Loader;
2 2     2   70817 use strict;
  2         4  
  2         57  
3 2     2   11 use warnings;
  2         4  
  2         112  
4             require Net::MQTT::Simple; #skip import
5              
6             our $VERSION = '0.03';
7              
8             =head1 NAME
9              
10             Net::MQTT::Simple::One_Shot_Loader - Perl package to add one_shot method to Net::MQTT::Simple
11              
12             =head1 SYNOPSIS
13              
14             require Net::MQTT::Simple::One_Shot_Loader;
15             use Net::MQTT::Simple; #or Net::MQTT::Simple::SSL
16             my $mqtt = Net::MQTT::Simple->new($host);
17             my $obj = $mqtt->one_shot($topic_sub, $topic_pub, $message_pub, $timeout_seconds); #isa Net::MQTT::Simple::One_Shot_Loader::Response
18             my $value = $obj->message;
19              
20             =head1 DESCRIPTION
21              
22             This package loads the C method into the L name space to provide a well tested remote procedure call (RPC) via MQTT. Many IoT devices only support MQTT as a protocol so, in order to query state or settings these properties need to be requested by sending a message on one queue and receiving a response on another queue.
23              
24             Due to the way L was implemented as a super class of L and since the author of L did not want to implement this method in his package (ref L), we implemented this method in a method loader package.
25              
26             =head1 METHODS
27              
28             =head2 one_shot
29              
30             Returns an object representing the first message that matches the subscription topic after publishing the message on the message topic. Returns an object with the error set to a true value on error like timeout.
31              
32             my $response = $mqtt->one_shot($topic_sub, $topic_pub, $message_pub, $timeout_seconds);
33              
34             if (not $response->error) {
35             my $message = $response->message;
36             }
37              
38             =cut
39              
40             {
41             package Net::MQTT::Simple::One_Shot_Loader::Response;
42 2     2   11 use strict;
  2         4  
  2         64  
43 2     2   9 use warnings;
  2         4  
  2         273  
44 0     0     sub error {shift->{'error'}};
45 0     0     sub topic {shift->{'topic'}};
46 0     0     sub message {shift->{'message'}};
47 0     0     sub time {shift->{'time'}};
48             }
49              
50             {
51             package #hide from CPAN
52             Net::MQTT::Simple;
53 2     2   13 use strict;
  2         5  
  2         54  
54 2     2   11 use warnings;
  2         9  
  2         53  
55 2     2   590 use Time::HiRes qw{};
  2         1418  
  2         519  
56              
57             sub one_shot {
58 0     0 0   my $self = shift; #isa Net::MQTT::Simple or Net::MQTT::Simple::SSL
59 0 0         my $topic_sub = shift or die('Error: subscribe topic is required');
60 0 0         my $topic_pub = shift or die('Error: publish topic is required');
61 0           my $message = shift;
62 0 0         $message = '' unless defined $message; #default '', allow 0 and support perl 5.8
63 0   0       my $timeout = shift || 1.5; #seconds
64              
65 0           my $found = 0; #anonymous sub updates these variables
66 0           my $topic_out = $topic_sub;
67 0           my $message_out = '';
68              
69             $self->subscribe($topic_sub => sub {
70 0 0   0     unless ($found) { #stop after first found but we get multiple calls per tick
71 0           $found = 1;
72 0           $topic_out = shift;
73 0           $message_out = shift;
74             }
75             }
76 0           );
77              
78 0           my $timer = Time::HiRes::time();
79 0           $self->publish($topic_pub => $message);
80              
81 0           my $future = Time::HiRes::time() + $timeout;
82 0           while (Time::HiRes::time() < $future) {
83 0           $self->tick($timeout); #it takes a few ticks to clear out LWT
84 0 0         last if $found;
85             }
86 0           $timer = Time::HiRes::time() - $timer;
87 0 0         my $error = $found ? '' : sprintf('subscribe timeout (%0.1f s)', $timer);
88              
89 0           $self->unsubscribe($topic_sub); #must unsubscribe to do one_shot back to back
90 0           return bless {
91             error => $error,
92             topic => $topic_out,
93             message => $message_out,
94             time => $timer,
95             }, 'Net::MQTT::Simple::One_Shot_Loader::Response';
96             }
97             }
98              
99             =head1 SEE ALSO
100              
101             L
102              
103             =head1 AUTHOR
104              
105             Michael R. Davis
106              
107             =head1 COPYRIGHT AND LICENSE
108              
109             MIT License
110              
111             Copyright (c) 2023 Michael R. Davis
112              
113             =cut
114              
115             1;