#!/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 [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{ ^ (? [^=]+) = (? .*) $ }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 B<-h|--host> I [B] I=I [B] I=I =head1 VERSION version 0.01 =head1 DESCRIPTION B publishes multiple MQTT messages (usually for different topics) using a single connection. This makes it faster than a series of B invocations. Its non-option arguments consist of the I to publish and the B and B keywords specifying how exactly the messages should be published. =head1 ARGUMENTS =over =item B All messages specified after B 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 All messages specified after B 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=I Publish (and optionally retain, see above) I as a message for I. If I 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 being parsed as a filename, prefix it with an additional slash character: Use BI<...> instead of BI<...>. This additional character will not be part of the published message. =back =head1 OPTIONS =over =item B<-h>|B<--host> I Connect to I. 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 Ederf@finalrewind.orgE =head1 LICENSE This software is licensed under the same terms as Perl itself.