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   70437 use strict;
  2         6  
  2         57  
3 2     2   11 use warnings;
  2         4  
  2         132  
4             require Net::MQTT::Simple; #skip import
5              
6             our $VERSION = '0.02';
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         75  
43 2     2   12 use warnings;
  2         4  
  2         260  
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 Net::MQTT::Simple;
52 2     2   15 use strict;
  2         4  
  2         45  
53 2     2   9 use warnings;
  2         19  
  2         57  
54 2     2   624 use Time::HiRes qw{};
  2         1520  
  2         531  
55              
56             sub one_shot {
57 0     0 0   my $self = shift; #isa Net::MQTT::Simple or Net::MQTT::Simple::SSL
58 0 0         my $topic_sub = shift or die('Error: subscribe topic is required');
59 0 0         my $topic_pub = shift or die('Error: publish topic is required');
60 0           my $message = shift;
61 0 0         $message = '' unless defined $message; #default '', allow 0 and support perl 5.8
62 0   0       my $timeout = shift || 1.5; #seconds
63              
64 0           my $found = 0; #anonymous sub updates these variables
65 0           my $topic_out = $topic_sub;
66 0           my $message_out = '';
67              
68             $self->subscribe($topic_sub => sub {
69 0 0   0     unless ($found) { #stop after first found but we get multiple calls per tick
70 0           $found = 1;
71 0           $topic_out = shift;
72 0           $message_out = shift;
73             }
74             }
75 0           );
76              
77 0           my $timer = Time::HiRes::time();
78 0           $self->publish($topic_pub => $message);
79              
80 0           my $future = Time::HiRes::time() + $timeout;
81 0           while (Time::HiRes::time() < $future) {
82 0           $self->tick($timeout); #it takes a few ticks to clear out LWT
83 0 0         last if $found;
84             }
85 0           $timer = Time::HiRes::time() - $timer;
86 0 0         my $error = $found ? '' : sprintf('subscribe timeout (%0.1f s)', $timer);
87              
88 0           $self->unsubscribe($topic_sub); #must unsubscribe to do one_shot back to back
89 0           return bless {
90             error => $error,
91             topic => $topic_out,
92             message => $message_out,
93             time => $timer,
94             }, 'Net::MQTT::Simple::One_Shot_Loader::Response';
95             }
96             }
97              
98             =head1 SEE ALSO
99              
100             L
101              
102             =head1 AUTHOR
103              
104             Michael R. Davis
105              
106             =head1 COPYRIGHT AND LICENSE
107              
108             MIT License
109              
110             Copyright (c) 2023 Michael R. Davis
111              
112             =cut
113              
114             1;