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 +4 -4
- data/lib/homie-mqtt.rb +1 -14
- data/lib/mqtt/homie/device.rb +13 -7
- data/lib/mqtt/homie/node.rb +7 -3
- data/lib/mqtt/homie/property.rb +79 -58
- data/lib/mqtt/homie/version.rb +1 -1
- data/lib/mqtt/homie.rb +16 -0
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 20210b0338d6fa60e38cea5fbb830a26ae4c9d3a919ab8e7c1d373893afbc792
|
4
|
+
data.tar.gz: 479bbfe1fad83e2d872b319ab5deb22b394ed3ab846876acce0ae46e71b06435
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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"
|
data/lib/mqtt/homie/device.rb
CHANGED
@@ -19,8 +19,10 @@ module MQTT
|
|
19
19
|
@nodes = {}
|
20
20
|
@published = false
|
21
21
|
@out_of_band_topic_proc = block
|
22
|
-
|
23
|
-
|
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
|
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
|
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", (
|
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", (
|
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
|
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)
|
data/lib/mqtt/homie/node.rb
CHANGED
@@ -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
|
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
|
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
|
91
|
+
return unless published?
|
88
92
|
|
89
93
|
@published = false
|
90
94
|
|
data/lib/mqtt/homie/property.rb
CHANGED
@@ -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,
|
9
|
-
|
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
|
-
|
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
|
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
|
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
|
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 :
|
119
|
-
|
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
|
-
|
166
|
+
casted_value = nil if format && !range.include?(casted_value)
|
134
167
|
when :color
|
135
|
-
|
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
|
-
|
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
|
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
|
208
|
+
return unless published?
|
188
209
|
|
189
210
|
@published = false
|
190
211
|
|
data/lib/mqtt/homie/version.rb
CHANGED
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.
|
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:
|
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.
|
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.
|
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.
|
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.
|