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, &block) ⇒ Property

Returns a new instance of Property.

Raises:

  • (ArgumentError)


8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/mqtt/homie/property.rb', line 8

def initialize(node, id, name, datatype, value = nil, format: nil, retained: true, unit: nil, &block)
  raise ArgumentError, "Invalid Homie datatype" unless %s{string integer float boolean enum color datetime duration}
  raise ArgumentError, "retained must be boolean" unless [true, false].include?(retained)
  format = format.join(",") if format.is_a?(Array) && datatype == :enum
  if %i{integer float}.include?(datatype) && format.is_a?(Range)
    raise ArgumentError "only inclusive ranges are supported" if format.exclude_end?
    format = "#{format.begin}:#{format.end}"
  end
  raise ArgumentError, "format must be nil or a string" unless format.nil? || format.is_a?(String)
  raise ArgumentError, "unit must be nil or a string" unless unit.nil? || unit.is_a?(String)
  raise ArgumentError, "format is required for enums" if datatype == :enum && format.nil?
  raise ArgumentError, "format is required for colors" if datatype == :color && format.nil?
  raise ArgumentError, "format must be either rgb or hsv for colors" if datatype == :color && !%w{rgb hsv}.include?(format.to_s)
  raise ArgumentError, "an initial value cannot be provided for a non-retained property" if !value.nil? && !retained

  super(id, name)

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

Instance Attribute Details

#datatypeObject (readonly)

Returns the value of attribute datatype.



6
7
8
# File 'lib/mqtt/homie/property.rb', line 6

def datatype
  @datatype
end

#formatObject

Returns the value of attribute format.



6
7
8
# File 'lib/mqtt/homie/property.rb', line 6

def format
  @format
end

#nodeObject (readonly)

Returns the value of attribute node.



6
7
8
# File 'lib/mqtt/homie/property.rb', line 6

def node
  @node
end

#unitObject

Returns the value of attribute unit.



6
7
8
# File 'lib/mqtt/homie/property.rb', line 6

def unit
  @unit
end

#valueObject

Returns the value of attribute value.



6
7
8
# File 'lib/mqtt/homie/property.rb', line 6

def value
  @value
end

Instance Method Details

#deviceObject



35
36
37
# File 'lib/mqtt/homie/property.rb', line 35

def device
  node.device
end

#mqttObject



129
130
131
# File 'lib/mqtt/homie/property.rb', line 129

def mqtt
  node.mqtt
end

#publishObject



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/mqtt/homie/property.rb', line 133

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

#rangeObject



80
81
82
83
84
85
86
87
# File 'lib/mqtt/homie/property.rb', line 80

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

#retained?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'lib/mqtt/homie/property.rb', line 43

def retained?
  @retained
end

#set(value) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/mqtt/homie/property.rb', line 89

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

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

#settable?Boolean

Returns:

  • (Boolean)


47
48
49
# File 'lib/mqtt/homie/property.rb', line 47

def settable?
  !!@block
end

#subscribeObject



150
151
152
# File 'lib/mqtt/homie/property.rb', line 150

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

#topicObject



39
40
41
# File 'lib/mqtt/homie/property.rb', line 39

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

#unpublishObject



154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/mqtt/homie/property.rb', line 154

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