begin require "mqtt" rescue LoadError # Fallback to mosquitto_pub when the mqtt gem is not available. end module MqttClient class << self def publish(topic, payload, retain: false, qos: 0) return false if topic.nil? || topic.empty? if defined?(MQTT) publish_with_ruby_mqtt(topic, payload, retain, qos) else publish_with_mosquitto_pub(topic, payload, retain, qos) end rescue StandardError => e warn("[MqttClient] publish failed: #{e.class}: #{e.message}") false end private def publish_with_ruby_mqtt(topic, payload, retain, qos) options = { host: mqtt_host, port: mqtt_port, keep_alive: 30 } options[:username] = mqtt_username if mqtt_username options[:password] = mqtt_password if mqtt_password MQTT::Client.connect(options) do |client| client.publish(topic, payload.to_s, retain, qos) end true end def publish_with_mosquitto_pub(topic, payload, retain, qos) command = [ "mosquitto_pub", "-h", mqtt_host, "-p", mqtt_port.to_s, "-q", qos.to_s, "-t", topic, "-m", payload.to_s ] command << "-r" if retain ok = system(*command) warn("[MqttClient] mosquitto_pub failed") unless ok ok end def mqtt_host ENV.fetch("MQTT_HOST", "localhost") end def mqtt_port Integer(ENV.fetch("MQTT_PORT", "1883")) rescue ArgumentError 1883 end def mqtt_username v = ENV["MQTT_USERNAME"] v.nil? || v.strip.empty? ? nil : v end def mqtt_password ENV["MQTT_PASSWORD"] end end end