homie-mqtt 1.4.5 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="