homie-mqtt 1.4.5 → 1.5.0

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: c95ecceb8af9d97949e7ee75cccf878f80270ff703c07ee957b9b276c96f6538
4
- data.tar.gz: 0162763dce328e5c4a43045740ca5e907d0506de4e8fc05592c524763274e965
3
+ metadata.gz: 8552d2aca44043208c3b3cd06fff9d5376515103d7de423926713441acecd6e0
4
+ data.tar.gz: a27b5fe2fe50e2975708c308ed9a0f9b5b7d2469667741fe092210adcda8582a
5
5
  SHA512:
6
- metadata.gz: 53500d60691a22a98a46c81e0f5c6627bcc356503186f5275b4c643321254719ac6effbb094241f1f37800859f80f6ec4b9b5c2749e91f4169ec38e1073d9595
7
- data.tar.gz: 4ee3a6ba42de0f71746c1211e4d9e543d3587d7f7d79524667e71b40a61e3a65a5a23ec90a8e09dc318399528b65345d863df423b3a5f5cd44cd3378e2cbcb9f
6
+ metadata.gz: b10c773d61781b4fb2e5595056e1b5e41373ec579bcf0a152f0c5dc763c3973ad09914964caaaf9c619b079e6cf15f4dc923122caf214f571741b8b6934a8317
7
+ data.tar.gz: dd8069070124dea54d81f3fc0095b9cce1d28d77ed655d7162182c0a11cdfecbb93cc40c97ea3778b3ebd35508cf960fdfcdd2594cac87f3621fdf11e64845c0
data/lib/homie-mqtt.rb CHANGED
@@ -4,13 +4,13 @@ module MQTT
4
4
  module Homie
5
5
  class << self
6
6
  def escape_id(id)
7
- id.downcase.gsub(/[^a-z0-9\-]/, '-').sub(/^[^a-z0-9]+/, '')
7
+ id.downcase.gsub(/[^a-z0-9\-]/, "-").sub(/^[^a-z0-9]+/, "")
8
8
  end
9
9
  end
10
10
  end
11
11
  end
12
12
 
13
- require 'mqtt/homie/base'
14
- require 'mqtt/homie/device'
15
- require 'mqtt/homie/node'
16
- require 'mqtt/homie/property'
13
+ require "mqtt/homie/base"
14
+ require "mqtt/homie/device"
15
+ require "mqtt/homie/node"
16
+ require "mqtt/homie/property"
@@ -9,18 +9,19 @@ module MQTT
9
9
 
10
10
  def initialize(id, name)
11
11
  raise ArgumentError, "Invalid Homie ID '#{id}'" unless id.is_a?(String) && id =~ Regexp.new("^#{REGEX}$")
12
+
12
13
  @id = id
13
14
  @name = name
14
15
  end
15
16
 
16
17
  def name=(value)
17
- if name != value
18
- name = value
19
- if @published
20
- device.init do
21
- mqtt.publish("#{topic}/$name", name, retain: true, qos: 1)
22
- end
23
- end
18
+ return if name == value
19
+
20
+ name = value
21
+ return unless @published
22
+
23
+ device.init do
24
+ mqtt.publish("#{topic}/$name", name, retain: true, qos: 1)
24
25
  end
25
26
  end
26
27
  end
@@ -1,18 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'mqtt'
4
- require 'ruby2_keywords'
3
+ require "mqtt"
4
+ require "ruby2_keywords"
5
5
 
6
6
  module MQTT
7
7
  module Homie
8
8
  class Device < Base
9
+ # the Homie spec version
10
+ VERSION = "4.0.0"
11
+
9
12
  attr_reader :root_topic, :state, :mqtt
10
- attr_accessor :logger
11
- attr_accessor :out_of_band_topic_proc
13
+ attr_accessor :logger, :out_of_band_topic_proc
12
14
 
13
15
  def initialize(id, name, root_topic: nil, mqtt: nil, clear_topics: true, &block)
14
16
  super(id, name)
15
- @root_topic = @root_topic || "homie"
17
+ @root_topic = root_topic || "homie"
16
18
  @state = :init
17
19
  @nodes = {}
18
20
  @published = false
@@ -23,9 +25,7 @@ module MQTT
23
25
 
24
26
  @mqtt.on_reconnect do
25
27
  each do |node|
26
- node.each do |property|
27
- property.subscribe
28
- end
28
+ node.each(&:subscribe)
29
29
  end
30
30
  mqtt.publish("#{topic}/$state", :init, retain: true, qos: 1)
31
31
  mqtt.publish("#{topic}/$state", state, retain: true, qos: 1) unless state == :init
@@ -35,6 +35,10 @@ module MQTT
35
35
  self.clear_topics if clear_topics
36
36
  end
37
37
 
38
+ def inspect
39
+ "#<MQTT::Homie::Device #{topic} name=#{name.inspect}, $state=#{state.inspect}>"
40
+ end
41
+
38
42
  def device
39
43
  self
40
44
  end
@@ -61,6 +65,7 @@ module MQTT
61
65
 
62
66
  def remove_node(id)
63
67
  return false unless (node = @nodes[id])
68
+
64
69
  init do
65
70
  node.unpublish
66
71
  @nodes.delete(id)
@@ -77,11 +82,15 @@ module MQTT
77
82
  @nodes.each_value(&block)
78
83
  end
79
84
 
85
+ def count
86
+ @nodes.count
87
+ end
88
+
80
89
  def publish
81
90
  return if @published
82
91
 
83
92
  mqtt.batch_publish do
84
- mqtt.publish("#{topic}/$homie", "4.0.0", retain: true, qos: 1)
93
+ mqtt.publish("#{topic}/$homie", VERSION, retain: true, qos: 1)
85
94
  mqtt.publish("#{topic}/$name", name, retain: true, qos: 1)
86
95
  mqtt.publish("#{topic}/$state", @state.to_s, retain: true, qos: 1)
87
96
 
@@ -106,6 +115,9 @@ module MQTT
106
115
 
107
116
  mqtt.publish("#{topic}/$nodes", @nodes.keys.join(","), retain: true, qos: 1)
108
117
  @nodes.each_value(&:publish)
118
+
119
+ yield if block_given?
120
+
109
121
  mqtt.publish("#{topic}/$state", (@state = :ready).to_s, retain: true, qos: 1)
110
122
  end
111
123
 
@@ -126,17 +138,15 @@ module MQTT
126
138
  end
127
139
 
128
140
  def init
129
- if state == :init
130
- return yield state
131
- end
141
+ return yield state if state == :init
132
142
 
133
143
  prior_state = state
134
- mqtt.publish("#{topic}/$state", (state = :init).to_s, retain: true, qos: 1)
144
+ mqtt.publish("#{topic}/$state", (self.state = :init).to_s, retain: true, qos: 1)
135
145
  result = nil
136
146
  mqtt.batch_publish do
137
147
  result = yield prior_state
138
148
  end
139
- mqtt.publish("#{topic}/$state", (state = :ready).to_s, retain: true, qos: 1)
149
+ mqtt.publish("#{topic}/$state", (self.state = :ready).to_s, retain: true, qos: 1)
140
150
  result
141
151
  end
142
152
 
@@ -145,7 +155,7 @@ module MQTT
145
155
 
146
156
  @mqtt.subscribe("#{topic}/#")
147
157
  @mqtt.unsubscribe("#{topic}/#", wait_for_ack: true)
148
- while !@mqtt.queue_empty?
158
+ until @mqtt.queue_empty?
149
159
  packet = @mqtt.get
150
160
  @mqtt.publish(packet.topic, retain: true, qos: 0)
151
161
  end
@@ -13,6 +13,16 @@ module MQTT
13
13
  @published = false
14
14
  end
15
15
 
16
+ def inspect
17
+ "#<MQTT::Homie::Node #{topic} name=#{full_name.inspect}, type=#{type.inspect}>"
18
+ end
19
+
20
+ def full_name
21
+ return name if device.count == 1
22
+
23
+ "#{device.name} #{name}"
24
+ end
25
+
16
26
  def topic
17
27
  "#{device.topic}/#{id}"
18
28
  end
@@ -21,6 +31,7 @@ module MQTT
21
31
  device.init do |prior_state|
22
32
  property = Property.new(self, *args, &block)
23
33
  raise ArgumentError, "Property '#{property.id}' already exists on '#{id}'" if @properties.key?(property.id)
34
+
24
35
  @properties[property.id] = property
25
36
  property.publish if prior_state == :ready
26
37
  property
@@ -29,6 +40,7 @@ module MQTT
29
40
 
30
41
  def remove_property(id)
31
42
  return false unless (property = @properties[id])
43
+
32
44
  init do
33
45
  property.unpublish
34
46
  @properties.delete(id)
@@ -73,6 +85,7 @@ module MQTT
73
85
 
74
86
  def unpublish
75
87
  return unless @published
88
+
76
89
  @published = false
77
90
 
78
91
  mqtt.publish("#{topic}/$name", retain: true, qos: 0)
@@ -6,19 +6,26 @@ module MQTT
6
6
  attr_reader :node, :datatype, :format, :unit, :value
7
7
 
8
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 duration].include?(datatype)
9
+ raise ArgumentError, "Invalid Homie datatype" unless %i[string integer float boolean enum color datetime
10
+ duration].include?(datatype)
10
11
  raise ArgumentError, "retained must be boolean" unless [true, false].include?(retained)
12
+
11
13
  format = format.join(",") if format.is_a?(Array) && datatype == :enum
12
- if %i{integer float}.include?(datatype) && format.is_a?(Range)
14
+ if %i[integer float].include?(datatype) && format.is_a?(Range)
13
15
  raise ArgumentError "only inclusive ranges are supported" if format.exclude_end?
16
+
14
17
  format = "#{format.begin}:#{format.end}"
15
18
  end
16
19
  raise ArgumentError, "format must be nil or a string" unless format.nil? || format.is_a?(String)
17
20
  raise ArgumentError, "unit must be nil or a string" unless unit.nil? || unit.is_a?(String)
18
21
  raise ArgumentError, "format is required for enums" if datatype == :enum && format.nil?
19
22
  raise ArgumentError, "format is required for colors" if datatype == :color && format.nil?
20
- raise ArgumentError, "format must be either rgb or hsv for colors" if datatype == :color && !%w{rgb hsv}.include?(format.to_s)
21
- raise ArgumentError, "an initial value cannot be provided for a non-retained property" if !value.nil? && !retained
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
+ if !value.nil? && !retained
27
+ raise ArgumentError, "an initial value cannot be provided for a non-retained property"
28
+ end
22
29
 
23
30
  super(id, name)
24
31
 
@@ -32,6 +39,24 @@ module MQTT
32
39
  @block = block
33
40
  end
34
41
 
42
+ def inspect
43
+ result = +"#<MQTT::Homie::Property #{topic} name=#{full_name.inspect}, datatype=#{datatype.inspect}"
44
+ result << ", format=#{format.inspect}" if format
45
+ result << ", unit=#{unit.inspect}" if unit
46
+ result << ", settable=true" if settable?
47
+ result << if retained?
48
+ ", value=#{value.inspect}"
49
+ else
50
+ ", retained=false"
51
+ end
52
+ result << ">"
53
+ result.freeze
54
+ end
55
+
56
+ def full_name
57
+ "#{node.full_name} #{name}"
58
+ end
59
+
35
60
  def device
36
61
  node.device
37
62
  end
@@ -49,39 +74,41 @@ module MQTT
49
74
  end
50
75
 
51
76
  def value=(value)
52
- if @value != value
53
- @value = value if retained?
54
- publish_value if @published
55
- end
77
+ return if @value == value
78
+
79
+ @value = value if retained?
80
+ publish_value if @published
56
81
  end
57
82
 
58
83
  def unit=(unit)
59
- if unit != @unit
60
- @unit = unit
61
- if @published
62
- device.init do
63
- mqtt.publish("#{topic}/$unit", unit.to_s, retain: true, qos: 1)
64
- end
65
- end
84
+ return if unit == @unit
85
+
86
+ @unit = unit
87
+ return unless @published
88
+
89
+ device.init do
90
+ mqtt.publish("#{topic}/$unit", unit.to_s, retain: true, qos: 1)
66
91
  end
67
92
  end
68
93
 
69
94
  def format=(format)
70
- if format != @format
71
- @format = format
72
- if @published
73
- device.init do
74
- mqtt.publish("#{topic}/$format", format.to_s, retain: true, qos: 1)
75
- end
76
- end
95
+ return if format == @format
96
+
97
+ @format = format
98
+ return unless @published
99
+
100
+ device.init do
101
+ mqtt.publish("#{topic}/$format", format.to_s, retain: true, qos: 1)
77
102
  end
78
103
  end
79
104
 
80
105
  def range
106
+ return nil unless format
107
+
81
108
  case datatype
82
- when :enum; format.split(',')
83
- when :integer; Range.new(*format.split(':').map(&:to_i))
84
- when :float; Range.new(*format.split(':').map(&:to_f))
109
+ when :enum then format.split(",")
110
+ when :integer then Range.new(*format.split(":").map(&:to_i))
111
+ when :float then Range.new(*format.split(":").map(&:to_f))
85
112
  else; raise MethodNotImplemented
86
113
  end
87
114
  end
@@ -89,24 +116,29 @@ module MQTT
89
116
  def set(value)
90
117
  case datatype
91
118
  when :boolean
92
- return unless %w{true false}.include?(value)
93
- value = value == 'true'
119
+ return unless %w[true false].include?(value)
120
+
121
+ value = value == "true"
94
122
  when :integer
95
- return unless value =~ /^-?\d+$/
123
+ return unless /^-?\d+$/.match?(value)
124
+
96
125
  value = value.to_i
97
- return unless range.include?(value) if format
126
+ return if format && !range.include?(value)
98
127
  when :float
99
- return unless value =~ /^-?(?:\d+|\d+\.|\.\d+|\d+\.\d+)(?:[eE]-?\d+)?$/
128
+ return unless /^-?(?:\d+|\d+\.|\.\d+|\d+\.\d+)(?:[eE]-?\d+)?$/.match?(value)
129
+
100
130
  value = value.to_f
101
- return unless range.include?(value) if format
131
+ return if format && !range.include?(value)
102
132
  when :enum
103
133
  return unless range.include?(value)
104
134
  when :color
105
- return unless value =~ /^\d{1,3},\d{1,3},\d{1,3}$/
106
- value = value.split(',').map(&:to_i)
107
- if format == 'rgb'
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"
108
140
  return if value.max > 255
109
- elsif format == 'hsv'
141
+ when "hsv"
110
142
  return if value.first > 360 || value[1..2].max > 100
111
143
  end
112
144
  when :datetime
@@ -153,6 +185,7 @@ module MQTT
153
185
 
154
186
  def unpublish
155
187
  return unless @published
188
+
156
189
  @published = false
157
190
 
158
191
  mqtt.publish("#{topic}/$name", retain: true, qos: 0)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module MQTT
4
4
  module Homie
5
- VERSION = '1.4.5'
5
+ VERSION = "1.5.0"
6
6
  end
7
7
  end
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.5
4
+ version: 1.5.0
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-09-04 00:00:00.000000000 Z
11
+ date: 2021-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mqtt-ccutrer
@@ -72,6 +72,48 @@ dependencies:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
74
  version: '13.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rubocop
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1.23'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.23'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rubocop-performance
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.12'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.12'
103
+ - !ruby/object:Gem::Dependency
104
+ name: rubocop-rake
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0.6'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.6'
75
117
  description:
76
118
  email: cody@cutrer.com'
77
119
  executables: []
@@ -87,7 +129,8 @@ files:
87
129
  homepage: https://github.com/ccutrer/homie-mqtt
88
130
  licenses:
89
131
  - MIT
90
- metadata: {}
132
+ metadata:
133
+ rubygems_mfa_required: 'true'
91
134
  post_install_message:
92
135
  rdoc_options: []
93
136
  require_paths:
@@ -96,7 +139,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
96
139
  requirements:
97
140
  - - ">="
98
141
  - !ruby/object:Gem::Version
99
- version: '0'
142
+ version: '2.5'
100
143
  required_rubygems_version: !ruby/object:Gem::Requirement
101
144
  requirements:
102
145
  - - ">="