homie-mqtt 1.5.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8552d2aca44043208c3b3cd06fff9d5376515103d7de423926713441acecd6e0
4
- data.tar.gz: a27b5fe2fe50e2975708c308ed9a0f9b5b7d2469667741fe092210adcda8582a
3
+ metadata.gz: 20210b0338d6fa60e38cea5fbb830a26ae4c9d3a919ab8e7c1d373893afbc792
4
+ data.tar.gz: 479bbfe1fad83e2d872b319ab5deb22b394ed3ab846876acce0ae46e71b06435
5
5
  SHA512:
6
- metadata.gz: b10c773d61781b4fb2e5595056e1b5e41373ec579bcf0a152f0c5dc763c3973ad09914964caaaf9c619b079e6cf15f4dc923122caf214f571741b8b6934a8317
7
- data.tar.gz: dd8069070124dea54d81f3fc0095b9cce1d28d77ed655d7162182c0a11cdfecbb93cc40c97ea3778b3ebd35508cf960fdfcdd2594cac87f3621fdf11e64845c0
6
+ metadata.gz: 5ef7fddffafe4f8dfc4cd44551f1d3c1c867bc10762c706121571e3dc3207bb25170edb7054f4058f01393707c7772999335bfe2e0fc47806533f0f6ff6229c4
7
+ data.tar.gz: 34a8a0aae900bcdef55a03ad878f5261655cbf45e62758cfb72dc658fd0263f962902fc21db6e083f4bbc486928131b754b43c14cbaaa89773105dabfaaceb00
data/lib/homie-mqtt.rb CHANGED
@@ -1,16 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module MQTT
4
- module Homie
5
- class << self
6
- def escape_id(id)
7
- id.downcase.gsub(/[^a-z0-9\-]/, "-").sub(/^[^a-z0-9]+/, "")
8
- end
9
- end
10
- end
11
- end
12
-
13
- require "mqtt/homie/base"
14
- require "mqtt/homie/device"
15
- require "mqtt/homie/node"
16
- require "mqtt/homie/property"
3
+ require "mqtt/homie"
@@ -19,8 +19,10 @@ module MQTT
19
19
  @nodes = {}
20
20
  @published = false
21
21
  @out_of_band_topic_proc = block
22
- mqtt = MQTT::Client.new(mqtt) if mqtt.is_a?(String)
23
- @mqtt = mqtt || MQTT::Client.new
22
+ # retry forever
23
+ mqtt = MQTT::Client.new(mqtt, reconnect_limit: nil) if mqtt.is_a?(String)
24
+ mqtt = MQTT::Client.new(reconnect_limit: nil, **mqtt) if mqtt.is_a?(Hash)
25
+ @mqtt = mqtt || MQTT::Client.new(reconnect_limit: nil)
24
26
  @mqtt.set_will("#{topic}/$state", "lost", retain: true, qos: 1)
25
27
 
26
28
  @mqtt.on_reconnect do
@@ -69,7 +71,7 @@ module MQTT
69
71
  init do
70
72
  node.unpublish
71
73
  @nodes.delete(id)
72
- mqtt.publish("#{topic}/$nodes", @nodes.keys.join(","), retain: true, qos: 1) if @published
74
+ mqtt.publish("#{topic}/$nodes", @nodes.keys.join(","), retain: true, qos: 1) if published?
73
75
  end
74
76
  true
75
77
  end
@@ -86,8 +88,12 @@ module MQTT
86
88
  @nodes.count
87
89
  end
88
90
 
91
+ def published?
92
+ @published
93
+ end
94
+
89
95
  def publish
90
- return if @published
96
+ return if published?
91
97
 
92
98
  mqtt.batch_publish do
93
99
  mqtt.publish("#{topic}/$homie", VERSION, retain: true, qos: 1)
@@ -141,17 +147,17 @@ module MQTT
141
147
  return yield state if state == :init
142
148
 
143
149
  prior_state = state
144
- mqtt.publish("#{topic}/$state", (self.state = :init).to_s, retain: true, qos: 1)
150
+ mqtt.publish("#{topic}/$state", (@state = :init).to_s, retain: true, qos: 1)
145
151
  result = nil
146
152
  mqtt.batch_publish do
147
153
  result = yield prior_state
148
154
  end
149
- mqtt.publish("#{topic}/$state", (self.state = :ready).to_s, retain: true, qos: 1)
155
+ mqtt.publish("#{topic}/$state", (@state = :ready).to_s, retain: true, qos: 1)
150
156
  result
151
157
  end
152
158
 
153
159
  def clear_topics
154
- raise ArgumentError, "cannot clear topics once published" if @published
160
+ raise ArgumentError, "cannot clear topics once published" if published?
155
161
 
156
162
  @mqtt.subscribe("#{topic}/#")
157
163
  @mqtt.unsubscribe("#{topic}/#", wait_for_ack: true)
@@ -44,7 +44,7 @@ module MQTT
44
44
  init do
45
45
  property.unpublish
46
46
  @properties.delete(id)
47
- mqtt.publish("#{topic}/$properties", @properties.keys.join(","), retain: true, qos: 1) if @published
47
+ mqtt.publish("#{topic}/$properties", @properties.keys.join(","), retain: true, qos: 1) if published?
48
48
  end
49
49
  true
50
50
  end
@@ -70,9 +70,13 @@ module MQTT
70
70
  end
71
71
  end
72
72
 
73
+ def published?
74
+ @published
75
+ end
76
+
73
77
  def publish
74
78
  mqtt.batch_publish do
75
- unless @published
79
+ unless published?
76
80
  mqtt.publish("#{topic}/$name", name, retain: true, qos: 1)
77
81
  mqtt.publish("#{topic}/$type", @type.to_s, retain: true, qos: 1)
78
82
  @published = true
@@ -84,7 +88,7 @@ module MQTT
84
88
  end
85
89
 
86
90
  def unpublish
87
- return unless @published
91
+ return unless published?
88
92
 
89
93
  @published = false
90
94
 
@@ -1,28 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "time"
4
+
3
5
  module MQTT
4
6
  module Homie
5
7
  class Property < Base
6
8
  attr_reader :node, :datatype, :format, :unit, :value
7
9
 
8
- def initialize(node, id, name, datatype, value = nil, format: nil, retained: true, unit: nil, &block)
9
- raise ArgumentError, "Invalid Homie datatype" unless %i[string integer float boolean enum color datetime
10
+ def initialize(node,
11
+ id,
12
+ name,
13
+ datatype,
14
+ value = nil,
15
+ format: nil,
16
+ retained: true,
17
+ unit: nil,
18
+ non_standard_value_check: nil,
19
+ &block)
20
+ raise ArgumentError, "Invalid Homie datatype" unless %i[string
21
+ integer
22
+ float
23
+ boolean
24
+ enum
25
+ color
26
+ datetime
10
27
  duration].include?(datatype)
11
28
  raise ArgumentError, "retained must be boolean" unless [true, false].include?(retained)
12
-
13
- format = format.join(",") if format.is_a?(Array) && datatype == :enum
14
- if %i[integer float].include?(datatype) && format.is_a?(Range)
15
- raise ArgumentError "only inclusive ranges are supported" if format.exclude_end?
16
-
17
- format = "#{format.begin}:#{format.end}"
18
- end
19
- raise ArgumentError, "format must be nil or a string" unless format.nil? || format.is_a?(String)
20
29
  raise ArgumentError, "unit must be nil or a string" unless unit.nil? || unit.is_a?(String)
21
- raise ArgumentError, "format is required for enums" if datatype == :enum && format.nil?
22
- raise ArgumentError, "format is required for colors" if datatype == :color && format.nil?
23
- if datatype == :color && !%w[rgb hsv].include?(format.to_s)
24
- raise ArgumentError, "format must be either rgb or hsv for colors"
25
- end
26
30
  if !value.nil? && !retained
27
31
  raise ArgumentError, "an initial value cannot be provided for a non-retained property"
28
32
  end
@@ -31,11 +35,12 @@ module MQTT
31
35
 
32
36
  @node = node
33
37
  @datatype = datatype
34
- @format = format
38
+ self.format = format
35
39
  @retained = retained
36
40
  @unit = unit
37
41
  @value = value
38
42
  @published = false
43
+ @non_standard_value_check = non_standard_value_check
39
44
  @block = block
40
45
  end
41
46
 
@@ -77,14 +82,14 @@ module MQTT
77
82
  return if @value == value
78
83
 
79
84
  @value = value if retained?
80
- publish_value if @published
85
+ publish_value if published?
81
86
  end
82
87
 
83
88
  def unit=(unit)
84
89
  return if unit == @unit
85
90
 
86
91
  @unit = unit
87
- return unless @published
92
+ return unless published?
88
93
 
89
94
  device.init do
90
95
  mqtt.publish("#{topic}/$unit", unit.to_s, retain: true, qos: 1)
@@ -94,8 +99,24 @@ module MQTT
94
99
  def format=(format)
95
100
  return if format == @format
96
101
 
102
+ format = format.join(",") if format.is_a?(Array) && datatype == :enum
103
+ if %i[integer float].include?(datatype) && format.is_a?(Range)
104
+ raise ArgumentError, "only inclusive ranges are supported" if format.last.is_a?(Float) && format.exclude_end?
105
+
106
+ last = format.end
107
+ last -= 1 if format.exclude_end?
108
+
109
+ format = "#{format.begin}:#{last}"
110
+ end
111
+ raise ArgumentError, "format must be nil or a string" unless format.nil? || format.is_a?(String)
112
+ raise ArgumentError, "format is required for enums" if datatype == :enum && format.nil?
113
+ raise ArgumentError, "format is required for colors" if datatype == :color && format.nil?
114
+ if datatype == :color && !%w[rgb hsv].include?(format.to_s)
115
+ raise ArgumentError, "format must be either rgb or hsv for colors"
116
+ end
117
+
97
118
  @format = format
98
- return unless @published
119
+ return unless published?
99
120
 
100
121
  device.init do
101
122
  mqtt.publish("#{topic}/$format", format.to_s, retain: true, qos: 1)
@@ -114,56 +135,56 @@ module MQTT
114
135
  end
115
136
 
116
137
  def set(value)
138
+ casted_value = case datatype
139
+ when :boolean
140
+ %w[true false].include?(value) ? value == "true" : nil
141
+ when :integer
142
+ /^-?\d+$/.match?(value) && value.to_i
143
+ when :float
144
+ /^-?(?:\d+|\d+\.|\.\d+|\d+\.\d+)(?:[eE]-?\d+)?$/.match?(value) && value.to_f
145
+ when :enum
146
+ value
147
+ when :color
148
+ /^\d{1,3},\d{1,3},\d{1,3}$/.match?(value) && value = value.split(",").map(&:to_i)
149
+ when :datetime
150
+ begin
151
+ value = Time.parse(value)
152
+ rescue ArgumentError
153
+ nil
154
+ end
155
+ when :duration
156
+ begin
157
+ value = ActiveSupport::Duration.parse(value)
158
+ rescue ActiveSupport::Duration::ISO8601Parser::ParsingError
159
+ nil
160
+ end
161
+ end
117
162
  case datatype
118
- when :boolean
119
- return unless %w[true false].include?(value)
120
-
121
- value = value == "true"
122
- when :integer
123
- return unless /^-?\d+$/.match?(value)
124
-
125
- value = value.to_i
126
- return if format && !range.include?(value)
127
- when :float
128
- return unless /^-?(?:\d+|\d+\.|\.\d+|\d+\.\d+)(?:[eE]-?\d+)?$/.match?(value)
129
-
130
- value = value.to_f
131
- return if format && !range.include?(value)
163
+ when :integer, :float
164
+ casted_value = nil if format && !range.cover?(casted_value)
132
165
  when :enum
133
- return unless range.include?(value)
166
+ casted_value = nil if format && !range.include?(casted_value)
134
167
  when :color
135
- return unless /^\d{1,3},\d{1,3},\d{1,3}$/.match?(value)
136
-
137
- value = value.split(",").map(&:to_i)
138
- case format
139
- when "rgb"
140
- return if value.max > 255
141
- when "hsv"
142
- return if value.first > 360 || value[1..2].max > 100
143
- end
144
- when :datetime
145
- begin
146
- value = Time.parse(value)
147
- rescue ArgumentError
148
- return
149
- end
150
- when :duration
151
- begin
152
- value = ActiveSupport::Duration.parse(value)
153
- rescue ActiveSupport::Duration::ISO8601Parser::ParsingError
154
- return
155
- end
168
+ casted_value = nil if (format == "rgb" && value.max > 255) ||
169
+ (format == "hsb" && (value.first > 360 || value[1..2].max > 100))
156
170
  end
157
171
 
158
- @block.arity == 2 ? @block.call(value, self) : @block.call(value)
172
+ casted_value = @non_standard_value_check&.call(value) if casted_value.nil?
173
+ return if casted_value.nil?
174
+
175
+ @block.arity == 2 ? @block.call(casted_value, self) : @block.call(casted_value)
159
176
  end
160
177
 
161
178
  def mqtt
162
179
  node.mqtt
163
180
  end
164
181
 
182
+ def published?
183
+ @published
184
+ end
185
+
165
186
  def publish
166
- return if @published
187
+ return if published?
167
188
 
168
189
  mqtt.batch_publish do
169
190
  mqtt.publish("#{topic}/$name", name, retain: true, qos: 1)
@@ -184,7 +205,7 @@ module MQTT
184
205
  end
185
206
 
186
207
  def unpublish
187
- return unless @published
208
+ return unless published?
188
209
 
189
210
  @published = false
190
211
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module MQTT
4
4
  module Homie
5
- VERSION = "1.5.0"
5
+ VERSION = "1.6.2"
6
6
  end
7
7
  end
data/lib/mqtt/homie.rb ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MQTT
4
+ module Homie
5
+ class << self
6
+ def escape_id(id)
7
+ id.downcase.gsub(/[^a-z0-9\-]/, "-").sub(/^[^a-z0-9]+/, "")
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+ require "mqtt/homie/base"
14
+ require "mqtt/homie/device"
15
+ require "mqtt/homie/node"
16
+ require "mqtt/homie/property"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: homie-mqtt
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Cutrer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-08 00:00:00.000000000 Z
11
+ date: 2022-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mqtt-ccutrer
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '1.0'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 1.0.1
22
+ version: 1.0.3
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: '1.0'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 1.0.1
32
+ version: 1.0.3
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: ruby2_keywords
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -121,6 +121,7 @@ extensions: []
121
121
  extra_rdoc_files: []
122
122
  files:
123
123
  - lib/homie-mqtt.rb
124
+ - lib/mqtt/homie.rb
124
125
  - lib/mqtt/homie/base.rb
125
126
  - lib/mqtt/homie/device.rb
126
127
  - lib/mqtt/homie/node.rb
@@ -146,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
147
  - !ruby/object:Gem::Version
147
148
  version: '0'
148
149
  requirements: []
149
- rubygems_version: 3.1.2
150
+ rubygems_version: 3.3.5
150
151
  signing_key:
151
152
  specification_version: 4
152
153
  summary: Library for publishing devices that conform to the Homie spec.