balboa_worldwide_app 1.0.0 → 1.1.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: 7daff60d63c09a23f5628f69102fa722745c343d5c3641d7d87f09e47be7549a
4
- data.tar.gz: b70859344d26e0f7a54b3a8ffea6389637683c5b5f31de5a0defb8bb52176c0a
3
+ metadata.gz: 474d2a26cc4dca552bcb29ff22aa486903d73b7707c40d9c160e77eb2baed5fb
4
+ data.tar.gz: f085680c104a2d42e1755cf63a44538a376e875820c12468f0fb955c39f69f07
5
5
  SHA512:
6
- metadata.gz: '085e33a524d9410cbd069592804a716448031eece734bf5d2209318ef387ef9b2cda6fe941f4956681015889ca70f6f4429805eabfbc56e0bd23322262c3f850'
7
- data.tar.gz: 749b99bc33e6df19dec5f276206a62556310be025daec5b8c71f061f8fd45510d7a6f58f201655f8b9cfc0bfaf826cf7c7b5b3d5b43755bbedc44ed87bdd70b5
6
+ metadata.gz: d6f16d2ae55926e145489f3b61d9df9be692e2a5a3d21920721ef657301d1e099fe28381e8c7eea9711efd2b314079b818e2f33bbda66234423c3be4c0ccacd5
7
+ data.tar.gz: d7a6d5640c2902c03e22a6f4b11f447b3f4e3b6c9da757070b7db363b8dbb4e1b77ecfbce1553779f68b077e5ac2eac98f06d5ca7c7abafc9e4433459b211e81
@@ -13,6 +13,7 @@ class MQTTBridge
13
13
  @mqtt.connect
14
14
  @bwa = bwa
15
15
  @attributes = {}
16
+ @things = Set.new
16
17
 
17
18
  publish_basic_attributes
18
19
 
@@ -22,11 +23,37 @@ class MQTTBridge
22
23
  message = @bwa.poll
23
24
  next if message.is_a?(BWA::Messages::Ready)
24
25
 
26
+ puts message.inspect unless message.is_a?(BWA::Messages::Status)
25
27
  case message
28
+ when BWA::Messages::ControlConfiguration
29
+ publish("spa/$type", message.model)
30
+ when BWA::Messages::ControlConfiguration2
31
+ message.pumps.each_with_index do |speed, i|
32
+ publish_pump(i + 1, speed) if speed != 0
33
+ end
34
+ message.lights.each_with_index do |exists, i|
35
+ publish_thing("light", i + 1) if exists
36
+ end
37
+ message.aux.each_with_index do |exists, i|
38
+ publish_thing("aux", i + 1) if exists
39
+ end
40
+ publish_mister if message.mister
41
+ publish_blower(message.blower) if message.blower != 0
42
+ publish_circpump if message.circ_pump
43
+ publish("$state", "ready")
26
44
  when BWA::Messages::Status
45
+ @bwa.request_control_info unless @bwa.last_control_configuration
46
+ @bwa.request_control_info2 unless @bwa.last_control_configuration2
47
+
27
48
  # make sure time is in sync
28
49
  now = Time.now
29
- if message.hour != now.hour || message.minute != now.min
50
+ now_minutes = now.hour * 60 + now.min
51
+ spa_minutes = message.hour * 60 + message.minute
52
+ # check the difference in both directions
53
+ diff = [(spa_minutes - now_minutes) % 1440, 1440 - (spa_minutes - now_minutes) % 1440].min
54
+
55
+ # allow a skew of 1 minute, since the seconds will always be off
56
+ if diff > 1
30
57
  @bwa.set_time(now.hour, now.min, message.twenty_four_hour_time)
31
58
  end
32
59
  publish_attribute("spa/priming", message.priming)
@@ -35,10 +62,6 @@ class MQTTBridge
35
62
  publish_attribute("spa/24htime", message.twenty_four_hour_time)
36
63
  publish_attribute("spa/heating", message.heating)
37
64
  publish_attribute("spa/temperaturerange", message.temperature_range)
38
- publish_attribute("spa/circpump", message.circ_pump)
39
- publish_attribute("spa/pump1", message.pump1)
40
- publish_attribute("spa/pump2", message.pump2)
41
- publish_attribute("spa/light1", message.light1)
42
65
  publish_attribute("spa/currenttemperature", message.current_temperature)
43
66
  publish_attribute("spa/currenttemperature/$unit", "º#{message.temperature_scale.to_s[0].upcase}")
44
67
  publish_attribute("spa/settemperature", message.set_temperature)
@@ -50,6 +73,21 @@ class MQTTBridge
50
73
  publish_attribute("spa/currenttemperature/$format", message.temperature_range == :high ? "80:104" : "26:40")
51
74
  publish_attribute("spa/settemperature/$format", message.temperature_range == :high ? "80:104" : "26:40")
52
75
  end
76
+ publish_attribute("spa/filter1", message.filter[0])
77
+ publish_attribute("spa/filter2", message.filter[1])
78
+
79
+ publish_attribute("spa/circpump", message.circ_pump) if @bwa.last_control_configuration2&.circ_pump
80
+ publish_attribute("spa/blower", message.blower) if @bwa.last_control_configuration2&.blower.to_i != 0
81
+ publish_attribute("spa/mister", message.mister) if @bwa.last_control_configuration2&.mister
82
+ (0..5).each do |i|
83
+ publish_attribute("spa/pump#{i + 1}", message.pumps[i]) if @bwa.last_control_configuration2&.pumps&.[](i).to_i != 0
84
+ end
85
+ (0..1).each do |i|
86
+ publish_attribute("spa/light#{i + 1}", message.lights[i]) if @bwa.last_control_configuration2&.lights&.[](i)
87
+ end
88
+ (0..1).each do |i|
89
+ publish_attribute("spa/aux#{i + 1}", message.lights[i]) if @bwa.last_control_configuration2&.aux&.[](i)
90
+ end
53
91
  end
54
92
  end
55
93
  end
@@ -58,24 +96,35 @@ class MQTTBridge
58
96
  @mqtt.get do |topic, value|
59
97
  puts "got #{value.inspect} at #{topic}"
60
98
  case topic[@base_topic.length + 1..-1]
61
- when "heatingmode"
62
- next unless %w{ready rest ready_in_rest}.include?(value)
63
- # @bwa.set_heating_mode(value.to_sym)
64
- when "temperaturescale"
99
+ when "spa/heatingmode/set"
100
+ next @bwa.toggle_heating_mode if value == 'toggle'
101
+ next unless %w{ready rest}.include?(value)
102
+ @bwa.set_heating_mode(value.to_sym)
103
+ when "spa/temperaturescale/set"
65
104
  next unless %w{fahrenheit celsius}.include?(value)
66
- # @bwa.set_temperature_scale(value.to_sym)
105
+ @bwa.set_temperature_scale(value.to_sym)
67
106
  when "spa/24htime/set"
68
107
  next unless %w{true false}.include?(value)
69
108
  now = Time.now
70
109
  @bwa.set_time(now.hour, now.min, value == 'true')
71
- when "temperaturerange"
110
+ when "spa/temperaturerange/set"
111
+ next @bwa.toggle_temperature_range if value == 'toggle'
72
112
  next unless %w{low high}.include?(value)
73
- # @bwa.set_temperature_range(value.to_sym)
74
- when %r{^spa/(pump[12])/set}
75
- @bwa.send(:"set_#{$1}", value.to_i)
76
- when "spa/light1/set"
113
+ @bwa.set_temperature_range(value.to_sym)
114
+ when %r{^spa/pump([1-6])/set$}
115
+ next @bwa.toggle_pump($1.to_i) if value == 'toggle'
116
+ @bwa.set_pump($1.to_i, value.to_i)
117
+ when %r{^spa/(light|aux)([12])/set$}
118
+ next @bwa.send(:"toggle_#{$1}", $2.to_i) if value == 'toggle'
77
119
  next unless %w{true false}.include?(value)
78
- @bwa.set_light1(value == 'true')
120
+ @bwa.send(:"set_#{$1}", $2.to_i, value == 'true')
121
+ when "spa/mister/set"
122
+ next @bwa.toggle_mister if value == 'toggle'
123
+ next unless %w{true false}.include?(value)
124
+ @bwa.set_mister(value == 'true')
125
+ when "spa/blower/set"
126
+ next @bwa.toggle_blower if value == 'toggle'
127
+ @bwa.set_blower(value.to_i)
79
128
  when "spa/settemperature/set"
80
129
  @bwa.set_temperature(value.to_i)
81
130
  end
@@ -105,7 +154,7 @@ class MQTTBridge
105
154
 
106
155
  publish("spa/$name", "BWA Spa")
107
156
  publish("spa/$type", "spa")
108
- publish("spa/$properties", "priming,heatingmode,temperaturescale,24htime,heating,temperaturerange,circpump,pump1,pump2,light1,currenttemperature,settemperature")
157
+ publish_nodes
109
158
 
110
159
  publish("spa/priming/$name", "Is the pump priming")
111
160
  publish("spa/priming/$datatype", "boolean")
@@ -136,26 +185,6 @@ class MQTTBridge
136
185
  publish("spa/temperaturerange/$settable", "true")
137
186
  subscribe("spa/temperaturerange/set")
138
187
 
139
- publish("spa/circpump/$name", "Circ pump is currently running")
140
- publish("spa/circpump/$datatype", "boolean")
141
-
142
- publish("spa/pump1/$name", "Pump 1 speed")
143
- publish("spa/pump1/$datatype", "integer")
144
- publish("spa/pump1/$format", "0:2")
145
- publish("spa/pump1/$settable", "true")
146
- subscribe("spa/pump1/set")
147
-
148
- publish("spa/pump2/$name", "Pump 2 speed")
149
- publish("spa/pump2/$datatype", "integer")
150
- publish("spa/pump2/$format", "0:2")
151
- publish("spa/pump2/$settable", "true")
152
- subscribe("spa/pump2/set")
153
-
154
- publish("spa/light1/$name", "Light 1")
155
- publish("spa/light1/$datatype", "boolean")
156
- publish("spa/light1/$settable", "true")
157
- subscribe("spa/light1/set")
158
-
159
188
  publish("spa/currenttemperature/$name", "Current temperature")
160
189
  publish("spa/currenttemperature/$datatype", "integer")
161
190
 
@@ -164,7 +193,65 @@ class MQTTBridge
164
193
  publish("spa/settemperature/$settable", "true")
165
194
  subscribe("spa/settemperature/set")
166
195
 
167
- publish("$state", "ready")
196
+ publish("spa/filter1/$name", "Filter cycle 1 is currently running")
197
+ publish("spa/filter1/$datatype", "boolean")
198
+
199
+ publish("spa/filter2/$name", "Filter cycle 2 is currently running")
200
+ publish("spa/filter2/$datatype", "boolean")
201
+ end
202
+
203
+ def publish_pump(i, speeds)
204
+ publish("spa/pump#{i}/$name", "Pump #{i} speed")
205
+ publish("spa/pump#{i}/$datatype", "integer")
206
+ publish("spa/pump#{i}/$format", "0:#{speeds}")
207
+ publish("spa/pump#{i}/$settable", "true")
208
+ subscribe("spa/pump#{i}/set")
209
+
210
+ @things << "pump#{i}"
211
+ publish_nodes
212
+ end
213
+
214
+ def publish_thing(type, i)
215
+ publish("spa/#{type}#{i}/$name", "#{type} #{i}")
216
+ publish("spa/#{type}#{i}/$datatype", "boolean")
217
+ publish("spa/#{type}#{i}/$settable", "true")
218
+ subscribe("spa/#{type}#{i}/set")
219
+
220
+ @things << "#{type}#{i}"
221
+ publish_nodes
222
+ end
223
+
224
+ def publish_mister
225
+ publish("spa/mister/$name", type)
226
+ publish("spa/mister/$datatype", "boolean")
227
+ publish("spa/mister/$settable", "true")
228
+ subscribe("spa/mister/set")
229
+
230
+ @things << "mister"
231
+ publish_nodes
232
+ end
233
+
234
+ def publish_blower(speeds)
235
+ publish("spa/blower/$name", "Blower speed")
236
+ publish("spa/blower/$datatype", "integer")
237
+ publish("spa/blower/$format", "0:#{speeds}")
238
+ publish("spa/blower/$settable", "true")
239
+ subscribe("spa/blower/set")
240
+
241
+ @things << "blower"
242
+ publish_nodes
243
+ end
244
+
245
+ def publish_circpump
246
+ publish("spa/circpump/$name", "Circ pump is currently running")
247
+ publish("spa/circpump/$datatype", "boolean")
248
+ @things << "circpump"
249
+
250
+ publish_nodes
251
+ end
252
+
253
+ def publish_nodes
254
+ publish("spa/$properties", (["priming,heatingmode,temperaturescale,24htime,heating,temperaturerange,currenttemperature,settemperature,filter1,filter2"] + @things.to_a).join(','))
168
255
  end
169
256
  end
170
257
 
@@ -176,7 +263,7 @@ if ARGV.empty?
176
263
  $stderr.puts "Could not find spa!"
177
264
  exit 1
178
265
  end
179
- spa_ip = spas.first.first
266
+ spa_ip = "tcp://#{spas.first.first}/"
180
267
  else
181
268
  spa_ip = ARGV[0]
182
269
  end
@@ -2,36 +2,46 @@ require 'bwa/message'
2
2
 
3
3
  module BWA
4
4
  class Client
5
- attr_reader :last_status, :last_filter_configuration
5
+ attr_reader :last_status, :last_control_configuration, :last_control_configuration2, :last_filter_configuration
6
6
 
7
- def initialize(host, port = 4257)
8
- if host =~ %r{^/dev}
9
- require 'serialport'
10
- @io = SerialPort.open(host, "baud" => 115200)
7
+ def initialize(uri)
8
+ uri = URI.parse(uri)
9
+ if uri.scheme == 'tcp'
10
+ require 'socket'
11
+ @io = TCPSocket.new(uri.host, uri.port || 4217)
12
+ elsif uri.scheme == 'telnet' || uri.scheme == 'rfc2217'
13
+ require 'net/telnet/rfc2217'
14
+ @io = Net::Telnet::RFC2217.new("Host" => uri.host, "Port" => uri.port || 23, "baud" => 115200)
11
15
  @queue = []
12
16
  else
13
- require 'socket'
14
- @io = TCPSocket.new(host, port)
17
+ require 'serialport'
18
+ @io = SerialPort.open(uri.path, "baud" => 115200)
19
+ @queue = []
15
20
  end
21
+ @buffer = ""
16
22
  end
17
23
 
18
24
  def poll
19
- message = nil
20
- while message.nil?
21
- begin
22
- message = Message.parse(@io)
23
- if message.is_a?(Messages::Ready) && (msg = @queue.shift)
24
- @io.write(msg)
25
- end
26
- rescue BWA::InvalidMessage => e
27
- unless e.message =~ /Incorrect data length/
28
- puts e.message
29
- puts e.raw_data.unpack("H*").first.scan(/[0-9a-f]{2}/).join(' ')
30
- end
25
+ message = bytes_read = nil
26
+ loop do
27
+ message, bytes_read = Message.parse(@buffer)
28
+ # discard how much we read
29
+ @buffer = @buffer[bytes_read..-1] if bytes_read
30
+ unless message
31
+ @buffer.concat(@io.readpartial(64 * 1024))
32
+ next
31
33
  end
34
+ break
35
+ end
36
+
37
+ if message.is_a?(Messages::Ready) && (msg = @queue&.shift)
38
+ puts "wrote #{msg.unpack('H*').first}"
39
+ @io.write(msg)
32
40
  end
33
41
  @last_status = message.dup if message.is_a?(Messages::Status)
34
42
  @last_filter_configuration = message.dup if message.is_a?(Messages::FilterCycles)
43
+ @last_control_configuration = message.dup if message.is_a?(Messages::ControlConfiguration)
44
+ @last_control_configuration2 = message.dup if message.is_a?(Messages::ControlConfiguration2)
35
45
  message
36
46
  end
37
47
 
@@ -59,6 +69,10 @@ module BWA
59
69
  send_message("\x0a\xbf\x04")
60
70
  end
61
71
 
72
+ def request_control_info2
73
+ send_message("\x0a\xbf\x22\x00\x00\x01")
74
+ end
75
+
62
76
  def request_control_info
63
77
  send_message("\x0a\xbf\x22\x02\x00\x00")
64
78
  end
@@ -67,39 +81,58 @@ module BWA
67
81
  send_message("\x0a\xbf\x22\x01\x00\x00")
68
82
  end
69
83
 
70
- def toggle_item(args)
71
- send_message("\x0a\xbf\x11#{args}")
84
+ def toggle_item(item)
85
+ send_message("\x0a\xbf\x11#{item.chr}\x00")
86
+ end
87
+
88
+ def toggle_pump(i)
89
+ toggle_item(i + 3)
90
+ end
91
+
92
+ def toggle_light(i)
93
+ toggle_item(i + 0x10)
72
94
  end
73
95
 
74
- def toggle_light1
75
- toggle_item("\x11\x00")
96
+ def toggle_mister
97
+ toggle_item(0x0e)
76
98
  end
77
99
 
78
- def toggle_pump1
79
- toggle_item("\x04\x00")
100
+ def toggle_blower
101
+ toggle_item(0x0c)
80
102
  end
81
103
 
82
- def toggle_pump2
83
- toggle_item("\x05\x00")
104
+ def set_pump(i, desired)
105
+ return unless last_status && last_control_configuration2
106
+ times = (desired - last_status.pumps[i - 1]) % (last_control_configuration2.pumps[i - 1] + 1)
107
+ times.times do
108
+ toggle_pump(i)
109
+ sleep(0.1)
110
+ end
84
111
  end
85
112
 
86
- (1..2).each do |i|
113
+ %w{light aux}.each do |type|
87
114
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
88
- def set_pump#{i}(desired)
89
- return unless last_status
90
- times = (desired - last_status.pump#{i}) % 3
91
- times.times do
92
- toggle_pump#{i}
93
- sleep(0.1)
115
+ def set_#{type}(i, desired)
116
+ return unless last_status
117
+ return if last_status.#{type}s[i - 1] == desired
118
+ toggle_#{type}(i)
94
119
  end
95
- end
96
120
  RUBY
97
121
  end
98
122
 
99
- def set_light1(desired)
123
+ def set_mister(desired)
100
124
  return unless last_status
101
- return if last_status.light1 == desired
102
- toggle_light1
125
+ return if last_status.mister == desired
126
+ toggle_mister
127
+ end
128
+
129
+ def set_blower(desired)
130
+ return unless last_status && last_control_configuration2
131
+ times = (desired - last_status.blower) % (last_control_configuration2.blower + 1)
132
+ times.times do
133
+ toggle_blower
134
+ sleep(0.1)
135
+ end
103
136
  end
104
137
 
105
138
  # high range is 80-104 for F, 26-40 for C (by 0.5)
@@ -113,5 +146,41 @@ module BWA
113
146
  hour |= 0x80 if twenty_four_hour_time
114
147
  send_message("\x0a\xbf\x21".force_encoding(Encoding::ASCII_8BIT) + hour.chr + minute.chr)
115
148
  end
149
+
150
+ def set_temperature_scale(scale)
151
+ raise ArgumentError, "scale must be :fahrenheit or :celsius" unless %I{fahrenheit :celsius}.include?(scale)
152
+ arg = scale == :fahrenheit ? 0 : 1
153
+ send_message("\x0a\xbf\x27\x01".force_encoding(Encoding::ASCII_8BIT) + arg.chr)
154
+ end
155
+
156
+ def toggle_temperature_range
157
+ toggle_item(0x50)
158
+ end
159
+
160
+ def set_temperature_range(desired)
161
+ return unless last_status
162
+ return if last_status.temperature_range == desired
163
+ toggle_temperature_range
164
+ end
165
+
166
+ def toggle_heating_mode
167
+ toggle_item(0x51)
168
+ end
169
+
170
+ HEATING_MODES = %I{ready rest ready_in_rest}.freeze
171
+ def set_heating_mode(desired)
172
+ raise ArgumentError, "heating_mode must be :ready or :rest" unless %I{ready rest}.include?(desired)
173
+ return unless last_status
174
+ times = if last_status.heating_mode == :ready && desired == :rest ||
175
+ last_status.heating_mode == :rest && desired == :ready ||
176
+ last_status.heating_mode == :ready_in_rest && desired == :rest
177
+ 1
178
+ elsif last_status.heating_mode == :ready_in_rest && desired == :ready
179
+ 2
180
+ else
181
+ 0
182
+ end
183
+ times.times { toggle_heating_mode }
184
+ end
116
185
  end
117
186
  end
@@ -17,48 +17,36 @@ module BWA
17
17
  @messages << klass
18
18
  end
19
19
 
20
- def parse(io)
21
- io = StringIO.new(io) if io.is_a?(String)
22
- data = ''
23
- # skim through until a start-of-message indicator
24
- until data[0] == '~'
25
- data = io.read(1)
20
+ def parse(data)
21
+ offset = -1
22
+ message_type = length = message_class = nil
23
+ loop do
24
+ offset += 1
25
+ return nil if data.length - offset < 5
26
+
27
+ next unless data[offset] == '~'
28
+ length = data[offset + 1].ord
29
+ # impossible message
30
+ next if length < 5
31
+
32
+ # don't have enough data for what this message wants;
33
+ # it could be garbage on the line so keep scanning
34
+ next if length + 2 > data.length - offset
35
+
36
+ next unless data[offset + length + 1] == '~'
37
+
38
+ next unless CRC.checksum(data.slice(offset + 1, length - 1)) == data[offset + length].ord
39
+ break
26
40
  end
27
41
 
28
- data.concat(io.read(1))
29
- length = data[-1].ord
42
+ puts "discarding invalid data prior to message #{data[0...offset].unpack('H*').first}" unless offset == 0
43
+ #puts "read #{data.slice(offset, length + 2).unpack('H*').first}"
30
44
 
31
- if length < 5
32
- bytes = data.bytes
33
- bytes.shift
34
- bytes.reverse.each { |byte| io.ungetbyte(byte) }
35
- raise InvalidMessage.new("Message has bogus length: #{length}", data)
36
- end
37
-
38
- data.concat(io.read(length))
39
- if data.length != length + 2
40
- data.bytes.reverse.each { |b| io.ungetbyte(b) }
41
- raise InvalidMessage.new("Incorrect data length (received #{data.length - 2}, expected #{length})", data)
42
- end
43
-
44
- message_type = data[2..4]
45
+ message_type = data.slice(offset + 2, 3)
45
46
  klass = @messages.find { |k| k::MESSAGE_TYPE == message_type }
46
47
 
47
- unless data[-1] == '~'
48
- bytes = data.bytes
49
- bytes.shift
50
- bytes.reverse.each { |b| io.ungetbyte(b) }
51
- raise InvalidMessage.new("Missing trailing message indicator", data)
52
- end
53
-
54
- unless CRC.checksum(data[1...-2]) == data[-2].ord
55
- bytes = data.bytes
56
- bytes.shift
57
- bytes.reverse.each { |b| io.ungetbyte(b) }
58
- raise InvalidMessage.new("Invalid checksum", data)
59
- end
60
48
 
61
- return nil if [
49
+ return [nil, offset + length + 2] if [
62
50
  "\xfe\xbf\x00".force_encoding(Encoding::ASCII_8BIT),
63
51
  "\x10\xbf\xe1".force_encoding(Encoding::ASCII_8BIT),
64
52
  "\x10\xbf\x07".force_encoding(Encoding::ASCII_8BIT)].include?(message_type)
@@ -67,9 +55,9 @@ module BWA
67
55
  raise InvalidMessage.new("Unrecognized data length (#{length}) for message #{klass}", data) unless length - 5 == klass::MESSAGE_LENGTH
68
56
 
69
57
  message = klass.new
70
- message.parse(data[5..-2])
71
- message.instance_variable_set(:@raw_data, data)
72
- message
58
+ message.parse(data.slice(offset + 5, length - 5))
59
+ message.instance_variable_set(:@raw_data, data.slice(offset, length + 2))
60
+ [message, offset + length + 2]
73
61
  end
74
62
 
75
63
  def format_time(hour, minute, twenty_four_hour_time = true)
@@ -3,6 +3,22 @@ module BWA
3
3
  class ControlConfiguration < Message
4
4
  MESSAGE_TYPE = "\x0a\xbf\x24".force_encoding(Encoding::ASCII_8BIT)
5
5
  MESSAGE_LENGTH = 21
6
+
7
+ attr_accessor :model, :version
8
+
9
+ def initialize
10
+ @model = ''
11
+ @version = 0
12
+ end
13
+
14
+ def parse(data)
15
+ self.version = "V#{data[2].ord}.#{data[3].ord}"
16
+ self.model = data[4..11].strip
17
+ end
18
+
19
+ def inspect
20
+ "#<BWA::Messages::ControlConfiguration #{model} #{version}>"
21
+ end
6
22
  end
7
23
  end
8
24
  end
@@ -12,6 +28,52 @@ module BWA
12
28
  class ControlConfiguration2 < Message
13
29
  MESSAGE_TYPE = "\x0a\xbf\x2e".force_encoding(Encoding::ASCII_8BIT)
14
30
  MESSAGE_LENGTH = 6
31
+
32
+ attr_accessor :pumps, :lights, :circ_pump, :blower, :mister, :aux
33
+
34
+ def initialize
35
+ self.pumps = Array.new(6, 0)
36
+ self.lights = Array.new(2, false)
37
+ self.circ_pump = false
38
+ self.blower = 0
39
+ self.mister = false
40
+ self.aux = Array.new(2, false)
41
+ end
42
+
43
+ def parse(data)
44
+ flags = data[0].ord
45
+ pumps[0] = flags & 0x03
46
+ pumps[1] = (flags >> 2) & 0x03
47
+ pumps[2] = (flags >> 4) & 0x03
48
+ pumps[3] = (flags >> 6) & 0x03
49
+ flags = data[1].ord
50
+ pumps[4] = flags & 0x03
51
+ pumps[5] = (flags >> 6) & 0x03
52
+ flags = data[2].ord
53
+ lights[0] = (flags & 0x03 != 0)
54
+ lights[1] = ((flags >> 6) & 0x03 != 0)
55
+ flags = data[3].ord
56
+ self.blower = flags & 0x03
57
+ self.circ_pump = ((flags >> 6) & 0x03 != 0)
58
+ flags = data[4].ord
59
+ self.mister = (flags & 0x30 != 0)
60
+ aux[0] = (flags & 0x01 != 0)
61
+ aux[1] = (flags & 0x02 != 0)
62
+ end
63
+
64
+ def inspect
65
+ result = "#<BWA::Messages::ControlConfiguration2 "
66
+ items = []
67
+
68
+ items << "pumps=#{pumps.inspect}"
69
+ items << "lights=#{lights.inspect}"
70
+ items << "circ_pump" if circ_pump
71
+ items << "blower=#{blower}" if blower != 0
72
+ items << "mister" if mister
73
+ items << "aux=#{aux.inspect}"
74
+
75
+ result << items.join(' ') << ">"
76
+ end
15
77
  end
16
78
  end
17
79
  end
@@ -5,12 +5,16 @@ module BWA
5
5
  :heating_mode,
6
6
  :temperature_scale,
7
7
  :twenty_four_hour_time,
8
+ :filter,
8
9
  :heating,
9
10
  :temperature_range,
10
11
  :hour, :minute,
11
12
  :circ_pump,
12
- :pump1, :pump2,
13
- :light1,
13
+ :blower,
14
+ :pumps,
15
+ :lights,
16
+ :mister,
17
+ :aux,
14
18
  :current_temperature, :set_temperature
15
19
 
16
20
  MESSAGE_TYPE = "\xff\xaf\x13".force_encoding(Encoding::ASCII_8BIT)
@@ -21,12 +25,15 @@ module BWA
21
25
  self.heating_mode = :ready
22
26
  @temperature_scale = :fahrenheit
23
27
  self.twenty_four_hour_time = false
28
+ self.filter = Array.new(2, false)
24
29
  self.heating = false
25
30
  self.temperature_range = :high
26
31
  self.hour = self.minute = 0
27
32
  self.circ_pump = false
28
- self.pump1 = self.pump2 = 0
29
- self.light1 = false
33
+ self.pumps = Array.new(6, 0)
34
+ self.lights = Array.new(2, false)
35
+ self.mister = false
36
+ self.aux = Array.new(2, false)
30
37
  self.set_temperature = 100
31
38
  end
32
39
 
@@ -42,16 +49,30 @@ module BWA
42
49
  flags = data[9].ord
43
50
  self.temperature_scale = (flags & 0x01 == 0x01) ? :celsius : :fahrenheit
44
51
  self.twenty_four_hour_time = (flags & 0x02 == 0x02)
52
+ filter[0] = (flags & 0x04 != 0)
53
+ filter[1] = (flags & 0x08 != 0)
45
54
  flags = data[10].ord
46
55
  self.heating = (flags & 0x30 != 0)
47
56
  self.temperature_range = (flags & 0x04 == 0x04) ? :high : :low
48
57
  flags = data[11].ord
49
- self.pump1 = flags & 0x03
50
- self.pump2 = (flags / 4) & 0x03
58
+ pumps[0] = flags & 0x03
59
+ pumps[1] = (flags >> 2) & 0x03
60
+ pumps[2] = (flags >> 4) & 0x03
61
+ pumps[3] = (flags >> 6) & 0x03
62
+ flags = data[12].ord
63
+ pumps[4] = flags & 0x03
64
+ pumps[5] = (flags >> 2) & 0x03
65
+
51
66
  flags = data[13].ord
52
67
  self.circ_pump = (flags & 0x02 == 0x02)
68
+ self.blower = (flags & 0x0C == 0x0C)
53
69
  flags = data[14].ord
54
- self.light1 = (flags & 0x03 == 0x03)
70
+ lights[0] = (flags & 0x03 != 0)
71
+ lights[1] = ((flags >> 2) & 0x03 != 0)
72
+ flags = data[15].ord
73
+ self.mister = (flags & 0x01 == 0x01)
74
+ aux[0] = (flags & 0x08 != 0)
75
+ aux[1] = (flags & 0x10 != 0)
55
76
  self.hour = data[3].ord
56
77
  self.minute = data[4].ord
57
78
  self.current_temperature = data[2].ord
@@ -134,13 +155,16 @@ module BWA
134
155
  items << "priming" if priming
135
156
  items << self.class.format_time(hour, minute, twenty_four_hour_time)
136
157
  items << "#{current_temperature || '--'}/#{set_temperature}º#{temperature_scale.to_s[0].upcase}"
158
+ items << "filter=#{filter.inspect}"
137
159
  items << heating_mode
138
160
  items << "heating" if heating
139
161
  items << temperature_range
140
162
  items << "circ_pump" if circ_pump
141
- items << "pump1=#{pump1}" unless pump1 == 0
142
- items << "pump2=#{pump2}" unless pump2 == 0
143
- items << "light1" if light1
163
+ items << "blower" if blower
164
+ items << "pumps=#{pumps.inspect}"
165
+ items << "lights=#{lights.inspect}"
166
+ items << "aux=#{aux.inspect}"
167
+ items << "mister" if mister
144
168
 
145
169
  result << items.join(' ') << ">"
146
170
  end
@@ -1,3 +1,3 @@
1
1
  module BWA
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: balboa_worldwide_app
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.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: 2020-05-12 00:00:00.000000000 Z
11
+ date: 2020-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: digest-crc
@@ -25,33 +25,47 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.4'
27
27
  - !ruby/object:Gem::Dependency
28
- name: serialport
28
+ name: mqtt
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.3.1
33
+ version: 0.5.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.3.1
40
+ version: 0.5.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: mqtt
42
+ name: net-telnet-rfc2217
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.5.0
47
+ version: 0.0.3
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.5.0
54
+ version: 0.0.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: serialport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.3.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.3.1
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: byebug
57
71
  requirement: !ruby/object:Gem::Requirement