summaryrefslogtreecommitdiff
path: root/bin/mqtt-multipub
blob: 2881a2a4005542e340be4549784af02f37eb6e25 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#!/usr/bin/env perl

use strict;
use warnings;
use 5.020;

our $VERSION = '0.01';

use File::Slurp qw(read_file);
use Getopt::Long;
use Net::MQTT::Simple;
use Time::HiRes qw(usleep);

my $mqtt_host;

GetOptions(
	'h|host=s' => \$mqtt_host,
);

if ( not defined $mqtt_host or not length($mqtt_host) ) {
	die("Usage: $0 -h <hostname> [topic=value ...]\n");
}

my $mqtt   = Net::MQTT::Simple->new($mqtt_host);
my $retain = 0;

sub parse_content_string {
	my ($raw_content) = @_;
	if ( $raw_content =~ m{ ^ / [^/] }x ) {
		my $content = read_file( $raw_content, { err_mode => 'carp' } );
		if ( defined $content ) {
			chomp $content;
		}
		return $content;
	}
	# Allow //foo as escape for literal /foo messages
	$raw_content =~ s{ ^ / / }{/}x;
	return $raw_content;
}

for my $arg (@ARGV) {
	if ( $arg eq 'publish' ) {
		$retain = 0;
	}
	elsif ( $arg eq 'retain' ) {
		$retain = 1;
	}
	elsif ( $arg =~ m{ ^ (?<topic> [^=]+) = (?<content> .*) $ }x ) {
		my $content = parse_content_string( $+{content} );
		if ($retain) {
			$mqtt->retain( $+{topic}, $content );
		}
		else {
			$mqtt->publish( $+{topic}, $content );
		}
	}
}

# XXX Net::MQTT::Simple passes data to the socket layer, but does not wait for
# it to be successfully sent. So (especially on fast systems, e.g. modern
# Core i5/i7 CPUs) we might terminate before all data was transmitted, leading
# to loss of some messages.
#
# Fixing this probably requires a dive into Net::MQTT::Simple and possibly
# changes in its API, so let's dance the workaround dance for now.

usleep(100_000);

__END__

=head1 NAME

mqtt-multipub - Publish multiple MQTT messages at once

=head1 SYNOPSIS

B<mqtt-multipub> B<-h|--host> I<hostname> [B<publish>] I<topic>=I<value ...> [B<retain>] I<topic>=I<value ...>

=head1 VERSION

version 0.01

=head1 DESCRIPTION

B<mqtt-multipub> publishes multiple MQTT messages (usually for different
topics) using a single connection. This makes it faster than a series of
B<mosquitto_pub> invocations.

Its non-option arguments consist of the I<messages> to publish and the
B<publish> and B<retain> keywords specifying how exactly the messages should
be published.


=head1 ARGUMENTS

=over

=item B<publish>

All messages specified after B<publish> will be published without retain flag.
I.e., they will be forwarded to clients subscribed to the topic, but they will
not be retained for clients subscribing at a later time.

This is the default behaviour.

=item B<retain>

All messages specified after B<retain> will be published and retained on the
MQTT broker as "last known good" value. So, clients which subscribe at a later
time will still get the value published this way -- unless it is updated or
deleted in the meantime.

=item I<topic>=I<value>

Publish (and optionally retain, see above) I<value> as a message for I<topic>.

If I<value> begins with a B</> (slash), it is interpreted as a filename
and the file content published instead. If the file contains a trailing
newline character, it will be left out of the message body.

To avoid I<value> being parsed as a filename, prefix it with an additional
slash character: Use B<//>I<...> instead of B</>I<...>. This additional
character will not be part of the published message.

=back

=head1 OPTIONS

=over

=item B<-h>|B<--host> I<hostname>

Connect to I<hostname>. Mandatory.

=back

=head1 EXIT STATUS

Zero unless things went wrong.

=head1 CONFIGURATION

None.

=head1 DEPENDENCIES

=over

=item * File::Slurp

=item * Net::MQTT::Simple

=back

=head1 BUGS AND LIMITATIONS

Probably many.

=head1 AUTHOR

Copyright (C) 2017 by Daniel Friesel E<lt>derf@finalrewind.orgE<gt>

=head1 LICENSE

This software is licensed under the same terms as Perl itself.