Class: E3DCMqtt::MqttClient

Inherits:
Object
  • Object
show all
Defined in:
lib/e3dc_mqtt/mqtt_client.rb

Overview

Thin wrapper around the ‘mqtt` gem with domain-specific publishing: delta-based publishes, JSON payloads, LWT, and typed `publish_status` / `publish_daily_statistics` / `publish_battery_data` helpers.

Defined Under Namespace

Classes: Error

Constant Summary collapse

ONLINE_TOPIC =
"online".freeze
DEFAULT_KEEPALIVE =
60

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url:, root:, prefix:, client_id: "e3dc-mqtt-ruby", username: "", password: "", tls_verify: true) ⇒ MqttClient

Returns a new instance of MqttClient.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/e3dc_mqtt/mqtt_client.rb', line 19

def initialize(url:, root:, prefix:, client_id: "e3dc-mqtt-ruby",
               username: "", password: "", tls_verify: true)
  uri = URI.parse(url)
  ssl = case uri.scheme
        when "ssl", "mqtts" then true
        when "tcp", "mqtt"  then false
        when "unix"         then raise Error, "unix:// MQTT not yet supported"
        else raise Error, "unsupported MQTT scheme: #{uri.scheme.inspect}"
        end

  @root = "#{root}/#{prefix}"
  @last_values = {}
  @client = build_client(uri, ssl, tls_verify, client_id, username, password)
  connect!
end

Instance Attribute Details

#rootObject (readonly)

Returns the value of attribute root.



17
18
19
# File 'lib/e3dc_mqtt/mqtt_client.rb', line 17

def root
  @root
end

Instance Method Details

#disconnectObject



35
36
37
38
39
40
# File 'lib/e3dc_mqtt/mqtt_client.rb', line 35

def disconnect
  publish(ONLINE_TOPIC, "false", retain: true)
  @client.disconnect
rescue StandardError
  # best-effort on shutdown
end

#publish(topic, value, retain: false, qos: 0) ⇒ Object

—– primitive publish helpers —–



44
45
46
47
# File 'lib/e3dc_mqtt/mqtt_client.rb', line 44

def publish(topic, value, retain: false, qos: 0)
  full = "#{@root}/#{topic}"
  @client.publish(full, format_value(value), retain, qos)
end

#publish_battery_data(batteries) ⇒ Object



115
116
117
# File 'lib/e3dc_mqtt/mqtt_client.rb', line 115

def publish_battery_data(batteries)
  batteries.each { |b| publish_one_battery(b) }
end

#publish_daily_statistics(stats) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/e3dc_mqtt/mqtt_client.rb', line 97

def publish_daily_statistics(stats)
  publish("status_sums/time", stats.timestamp.utc.iso8601)

  {
    "status_sums/autarky_today"              => stats.autarky,
    "status_sums/self_consumption_today"     => stats.consumed_production,
    "status_sums/solar_production_today"     => stats.solar_production,
    "status_sums/house_consumption_today"    => stats.consumption,
    "status_sums/battery_charge_today"       => stats.bat_power_in,
    "status_sums/battery_discharge_today"    => stats.bat_power_out,
    "status_sums/consumption_from_grid_today" => stats.grid_power_out,
    "status_sums/export_to_grid_today"       => stats.grid_power_in,
    "status_sums/state_of_charge_today"      => stats.state_of_charge,
    "status_sums/start"                      => stats.start.utc.iso8601,
    "status_sums/timespan"                   => iso8601_duration(stats.timespan)
  }.each { |topic, value| publish_if_changed(topic, value) }
end

#publish_if_changed(topic, value) ⇒ Object



49
50
51
52
53
54
# File 'lib/e3dc_mqtt/mqtt_client.rb', line 49

def publish_if_changed(topic, value)
  return if @last_values[topic] == value

  publish(topic, value)
  @last_values[topic] = value
end

#publish_json(topic, data, retain: false) ⇒ Object



56
57
58
59
# File 'lib/e3dc_mqtt/mqtt_client.rb', line 56

def publish_json(topic, data, retain: false)
  full = "#{@root}/#{topic}"
  @client.publish(full, JSON.generate(json_safe(data)), retain, 0)
end

#publish_json_if_changed(topic, data) ⇒ Object



61
62
63
64
65
66
67
68
# File 'lib/e3dc_mqtt/mqtt_client.rb', line 61

def publish_json_if_changed(topic, data)
  payload = JSON.generate(json_safe(data))
  key = [:json, topic]
  return if @last_values[key] == payload

  @client.publish("#{@root}/#{topic}", payload, false, 0)
  @last_values[key] = payload
end

#publish_status(status) ⇒ Object

—– domain publishers —–



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/e3dc_mqtt/mqtt_client.rb', line 72

def publish_status(status)
  publish("status/time", status.timestamp.utc.iso8601)

  battery_charge, battery_discharge = split_val(status.power_battery)
  consumption_from_grid, export_to_grid = split_val(status.power_grid)

  mapping = {
    "status/additional"              => -status.power_add.to_f,
    "status/autarky"                 => status.autarky,
    "status/battery_charge"          => battery_charge,
    "status/battery_discharge"       => battery_discharge,
    "status/battery_consumption"     => status.power_battery,
    "status/consumption_from_grid"   => consumption_from_grid,
    "status/export_to_grid"          => export_to_grid,
    "status/grid_production"         => status.power_grid,
    "status/house_consumption"       => status.power_home,
    "status/self_consumption"        => status.self_consumption,
    "status/solar_production"        => status.power_pv,
    "status/solar_production_excess" => status.power_pv - status.power_home,
    "status/state_of_charge"         => status.battery_soc,
    "status/wb_consumption"          => status.power_wb
  }
  mapping.each { |topic, value| publish_if_changed(topic, value) }
end