Class: MQTT::Homie::Property

Inherits:
Base
  • Object
show all
Defined in:
lib/mqtt/homie/property.rb

Constant Summary

Constants inherited from Base

Base::REGEX

Instance Attribute Summary collapse

Attributes inherited from Base

#id, #name

Instance Method Summary collapse

Constructor Details

#initialize(node, id, name, datatype, value = nil, format: nil, retained: true, unit: nil, non_standard_value_check: nil, &block) ⇒ Property

Returns a new instance of Property.

Raises:

  • (ArgumentError)


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/mqtt/homie/property.rb', line 10

def initialize(node,
               id,
               name,
               datatype,
               value = nil,
               format: nil,
               retained: true,
               unit: nil,
               non_standard_value_check: nil,
               &block)
  raise ArgumentError, "Invalid Homie datatype" unless %i[string
                                                          integer
                                                          float
                                                          boolean
                                                          enum
                                                          color
                                                          datetime
                                                          duration].include?(datatype)
  raise ArgumentError, "retained must be boolean" unless [true, false].include?(retained)
  raise ArgumentError, "unit must be nil or a string" unless unit.nil? || unit.is_a?(String)
  if !value.nil? && !retained
    raise ArgumentError, "an initial value cannot be provided for a non-retained property"
  end

  super(id, name)

  @node = node
  @datatype = datatype
  self.format = format
  @retained = retained
  @unit = unit
  @value = value
  @published = false
  @non_standard_value_check = non_standard_value_check
  @block = block
end

Instance Attribute Details

#datatypeObject (readonly)

Returns the value of attribute datatype.



8
9
10
# File 'lib/mqtt/homie/property.rb', line 8

def datatype
  @datatype
end

#formatObject

Returns the value of attribute format.



8
9
10
# File 'lib/mqtt/homie/property.rb', line 8

def format
  @format
end

#nodeObject (readonly)

Returns the value of attribute node.



8
9
10
# File 'lib/mqtt/homie/property.rb', line 8

def node
  @node
end

#unitObject

Returns the value of attribute unit.



8
9
10
# File 'lib/mqtt/homie/property.rb', line 8

def unit
  @unit
end

#valueObject

Returns the value of attribute value.



8
9
10
# File 'lib/mqtt/homie/property.rb', line 8

def value
  @value
end

Instance Method Details

#deviceObject



65
66
67
# File 'lib/mqtt/homie/property.rb', line 65

def device
  node.device
end

#full_nameObject



61
62
63
# File 'lib/mqtt/homie/property.rb', line 61

def full_name
  "#{node.full_name} #{name}"
end

#inspectObject



47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/mqtt/homie/property.rb', line 47

def inspect
  result = +"#<MQTT::Homie::Property #{topic} name=#{full_name.inspect}, datatype=#{datatype.inspect}"
  result << ", format=#{format.inspect}" if format
  result << ", unit=#{unit.inspect}" if unit
  result << ", settable=true" if settable?
  result << if retained?
              ", value=#{value.inspect}"
            else
              ", retained=false"
            end
  result << ">"
  result.freeze
end

#mqttObject



178
179
180
# File 'lib/mqtt/homie/property.rb', line 178

def mqtt
  node.mqtt
end

#publishObject



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/mqtt/homie/property.rb', line 186

def publish
  return if published?

  mqtt.batch_publish do
    mqtt.publish("#{topic}/$name", name, retain: true, qos: 1)
    mqtt.publish("#{topic}/$datatype", datatype.to_s, retain: true, qos: 1)
    mqtt.publish("#{topic}/$format", format, retain: true, qos: 1) if format
    mqtt.publish("#{topic}/$settable", "true", retain: true, qos: 1) if settable?
    mqtt.publish("#{topic}/$retained", "false", retain: true, qos: 1) unless retained?
    mqtt.publish("#{topic}/$unit", unit, retain: true, qos: 1) if unit
    publish_value unless value.nil?
    subscribe
  end

  @published = true
end

#published?Boolean

Returns:

  • (Boolean)


182
183
184
# File 'lib/mqtt/homie/property.rb', line 182

def published?
  @published
end

#rangeObject



126
127
128
129
130
131
132
133
134
135
# File 'lib/mqtt/homie/property.rb', line 126

def range
  return nil unless format

  case datatype
  when :enum then format.split(",")
  when :integer then Range.new(*format.split(":").map(&:to_i))
  when :float then Range.new(*format.split(":").map(&:to_f))
  else; raise MethodNotImplemented
  end
end

#retained?Boolean

Returns:

  • (Boolean)


73
74
75
# File 'lib/mqtt/homie/property.rb', line 73

def retained?
  @retained
end

#set(value) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/mqtt/homie/property.rb', line 137

def set(value)
  casted_value = case datatype
                 when :boolean
                   %w[true false].include?(value) ? value == "true" : nil
                 when :integer
                   /^-?\d+$/.match?(value) && value.to_i
                 when :float
                   /^-?(?:\d+|\d+\.|\.\d+|\d+\.\d+)(?:[eE]-?\d+)?$/.match?(value) && value.to_f
                 when :enum
                   value
                 when :color
                   /^\d{1,3},\d{1,3},\d{1,3}$/.match?(value) && value = value.split(",").map(&:to_i)
                 when :datetime
                   begin
                     value = Time.parse(value)
                   rescue ArgumentError
                     nil
                   end
                 when :duration
                   begin
                     value = ActiveSupport::Duration.parse(value)
                   rescue ActiveSupport::Duration::ISO8601Parser::ParsingError
                     nil
                   end
                 end
  case datatype
  when :integer, :float
    casted_value = nil if format && !range.cover?(casted_value)
  when :enum
    casted_value = nil if format && !range.include?(casted_value)
  when :color
    casted_value = nil if (format == "rgb" && value.max > 255) ||
                          (format == "hsb" && (value.first > 360 || value[1..2].max > 100))
  end

  casted_value = @non_standard_value_check&.call(value) if casted_value.nil?
  return if casted_value.nil?

  @block.arity == 2 ? @block.call(casted_value, self) : @block.call(casted_value)
end

#settable?Boolean

Returns:

  • (Boolean)


77
78
79
# File 'lib/mqtt/homie/property.rb', line 77

def settable?
  !!@block
end

#subscribeObject



203
204
205
# File 'lib/mqtt/homie/property.rb', line 203

def subscribe
  mqtt.subscribe("#{topic}/set") if settable?
end

#topicObject



69
70
71
# File 'lib/mqtt/homie/property.rb', line 69

def topic
  "#{node.topic}/#{id}"
end

#unpublishObject



207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/mqtt/homie/property.rb', line 207

def unpublish
  return unless published?

  @published = false

  mqtt.publish("#{topic}/$name", retain: true, qos: 0)
  mqtt.publish("#{topic}/$datatype", retain: true, qos: 0)
  mqtt.publish("#{topic}/$format", retain: true, qos: 0) if format
  mqtt.publish("#{topic}/$settable", retain: true, qos: 0) if settable?
  mqtt.publish("#{topic}/$retained", retain: true, qos: 0) unless retained?
  mqtt.publish("#{topic}/$unit", retain: true, qos: 0) if unit
  mqtt.unsubscribe("#{topic}/set") if settable?
  mqtt.publish(topic, retain: retained?, qos: 0) if !value.nil? && retained?
end