balboa_worldwide_app 1.2.4 → 2.0.1

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.
data/lib/bwa/client.rb CHANGED
@@ -1,24 +1,59 @@
1
- require 'bwa/message'
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+ require "uri"
5
+
6
+ require "bwa/logger"
7
+ require "bwa/message"
2
8
 
3
9
  module BWA
4
10
  class Client
5
- attr_reader :last_status, :last_control_configuration, :last_control_configuration2, :last_filter_configuration
11
+ extend Forwardable
12
+
13
+ attr_reader :status, :control_configuration, :configuration, :filter_cycles
14
+
15
+ delegate model: :control_configuration
16
+ delegate %i[hold
17
+ hold?
18
+ priming
19
+ priming?
20
+ heating_mode
21
+ temperature_scale
22
+ twenty_four_hour_time
23
+ twenty_four_hour_time?
24
+ heating
25
+ heating?
26
+ temperature_range
27
+ current_temperature
28
+ target_temperature
29
+ circulation_pump
30
+ blower
31
+ mister
32
+ pumps
33
+ lights
34
+ aux] => :status
6
35
 
7
36
  def initialize(uri)
8
37
  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)
38
+ case uri.scheme
39
+ when "tcp"
40
+ require "socket"
41
+ @io = TCPSocket.new(uri.host, uri.port || 4257)
42
+ when "telnet", "rfc2217"
43
+ require "net/telnet/rfc2217"
44
+ @io = Net::Telnet::RFC2217.new("Host" => uri.host, "Port" => uri.port || 23, "baud" => 115_200)
15
45
  @queue = []
16
46
  else
17
- require 'ccutrer-serialport'
18
- @io = CCutrer::SerialPort.new(uri.path, baud: 115200)
47
+ require "ccutrer-serialport"
48
+ @io = CCutrer::SerialPort.new(uri.path, baud: 115_200)
19
49
  @queue = []
20
50
  end
21
- @buffer = ""
51
+ @src = 0x0a
52
+ @buffer = +""
53
+ end
54
+
55
+ def full_configuration?
56
+ status && control_configuration && configuration && filter_cycles
22
57
  end
23
58
 
24
59
  def poll
@@ -41,18 +76,20 @@ module BWA
41
76
  end
42
77
 
43
78
  if message.is_a?(Messages::Ready) && (msg = @queue&.shift)
44
- puts "wrote #{msg.unpack('H*').first}"
79
+ unless BWA.verbosity < 1 && msg[3..4] == Messages::ControlConfigurationRequest::MESSAGE_TYPE
80
+ BWA.logger.debug "wrote: #{BWA.raw2str(msg)}"
81
+ end
45
82
  @io.write(msg)
46
83
  end
47
- @last_status = message.dup if message.is_a?(Messages::Status)
48
- @last_filter_configuration = message.dup if message.is_a?(Messages::FilterCycles)
49
- @last_control_configuration = message.dup if message.is_a?(Messages::ControlConfiguration)
50
- @last_control_configuration2 = message.dup if message.is_a?(Messages::ControlConfiguration2)
84
+ @status = message.dup if message.is_a?(Messages::Status)
85
+ @filter_cycles = message.dup if message.is_a?(Messages::FilterCycles)
86
+ @control_configuration = message.dup if message.is_a?(Messages::ControlConfiguration)
87
+ @configuration = message.dup if message.is_a?(Messages::ControlConfiguration2)
51
88
  message
52
89
  end
53
90
 
54
91
  def messages_pending?
55
- !!IO.select([@io], nil, nil, 0)
92
+ !!@io.wait_readable(0)
56
93
  end
57
94
 
58
95
  def drain_message_queue
@@ -60,132 +97,168 @@ module BWA
60
97
  end
61
98
 
62
99
  def send_message(message)
63
- length = message.length + 2
64
- full_message = "#{length.chr}#{message}".force_encoding(Encoding::ASCII_8BIT)
65
- checksum = CRC.checksum(full_message)
66
- full_message = "\x7e#{full_message}#{checksum.chr}\x7e".force_encoding(Encoding::ASCII_8BIT)
100
+ message.src = @src
101
+ full_message = message.serialize
102
+ unless BWA.verbosity < 1 && message.is_a?(Messages::ControlConfigurationRequest)
103
+ BWA.logger.info " to spa: #{message.inspect}"
104
+ end
67
105
  if @queue
68
106
  @queue.push(full_message)
69
107
  else
108
+ unless BWA.verbosity < 1 && message.is_a?(Messages::ControlConfigurationRequest)
109
+ BWA.logger.debug "wrote: #{BWA.raw2str(full_message)}"
110
+ end
70
111
  @io.write(full_message)
71
112
  end
72
113
  end
73
114
 
74
115
  def request_configuration
75
- send_message("\x0a\xbf\x04")
116
+ send_message(Messages::ConfigurationRequest.new)
76
117
  end
77
118
 
78
119
  def request_control_info2
79
- send_message("\x0a\xbf\x22\x00\x00\x01")
120
+ send_message(Messages::ControlConfigurationRequest.new(2))
80
121
  end
81
122
 
82
123
  def request_control_info
83
- send_message("\x0a\xbf\x22\x02\x00\x00")
124
+ send_message(Messages::ControlConfigurationRequest.new(1))
84
125
  end
85
126
 
86
127
  def request_filter_configuration
87
- send_message("\x0a\xbf\x22\x01\x00\x00")
128
+ send_message(Messages::ControlConfigurationRequest.new(3))
88
129
  end
89
130
 
90
131
  def toggle_item(item)
91
- send_message("\x0a\xbf\x11#{item.chr}\x00")
132
+ send_message(Messages::ToggleItem.new(item))
133
+ end
134
+
135
+ def toggle_pump(index)
136
+ toggle_item(index + 0x04)
92
137
  end
93
138
 
94
- def toggle_pump(i)
95
- toggle_item(i + 3)
139
+ def toggle_light(index)
140
+ toggle_item(index + 0x11)
96
141
  end
97
142
 
98
- def toggle_light(i)
99
- toggle_item(i + 0x10)
143
+ def toggle_aux(index)
144
+ toggle_item(index + 0x16)
100
145
  end
101
146
 
102
147
  def toggle_mister
103
- toggle_item(0x0e)
148
+ toggle_item(:mister)
104
149
  end
105
150
 
106
151
  def toggle_blower
107
- toggle_item(0x0c)
152
+ toggle_item(:blower)
108
153
  end
109
154
 
110
- def set_pump(i, desired)
111
- return unless last_status && last_control_configuration2
112
- times = (desired - last_status.pumps[i - 1]) % (last_control_configuration2.pumps[i - 1] + 1)
155
+ def toggle_hold
156
+ toggle_item(:hold)
157
+ end
158
+
159
+ def set_pump(index, desired)
160
+ return unless status && configuration
161
+
162
+ desired = 0 if desired == false
163
+ desired = 1 if desired == true
164
+ times = (desired - status.pumps[index]) % (configuration.pumps[index] + 1)
113
165
  times.times do
114
- toggle_pump(i)
166
+ toggle_pump(index)
115
167
  sleep(0.1)
116
168
  end
117
169
  end
118
170
 
119
- %w{light aux}.each do |type|
171
+ %i[light aux].each do |type|
172
+ suffix = "s" if type == :light
120
173
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
121
- def set_#{type}(i, desired)
122
- return unless last_status
123
- return if last_status.#{type}s[i - 1] == desired
124
- toggle_#{type}(i)
174
+ def set_#{type}(index, desired)
175
+ return unless status
176
+ return if status.#{type}#{suffix}[index] == desired
177
+
178
+ toggle_#{type}(index)
125
179
  end
126
180
  RUBY
127
181
  end
128
182
 
129
- def set_mister(desired)
130
- return unless last_status
131
- return if last_status.mister == desired
183
+ def mister=(desired)
184
+ return unless status
185
+ return if status.mister == desired
186
+
132
187
  toggle_mister
133
188
  end
134
189
 
135
- def set_blower(desired)
136
- return unless last_status && last_control_configuration2
137
- times = (desired - last_status.blower) % (last_control_configuration2.blower + 1)
190
+ def blower=(desired)
191
+ return unless status && configuration
192
+
193
+ times = (desired - status.blower) % (configuration.blower + 1)
138
194
  times.times do
139
195
  toggle_blower
140
196
  sleep(0.1)
141
197
  end
142
198
  end
143
199
 
144
- # high range is 80-104 for F, 26-40 for C (by 0.5)
145
- # low range is 50-80 for F, 10-26 for C (by 0.5)
146
- def set_temperature(desired)
147
- desired *= 2 if last_status && last_status.temperature_scale == :celsius || desired < 50
148
- send_message("\x0a\xbf\x20#{desired.round.chr}")
200
+ def hold=(desired)
201
+ return unless status
202
+ return if status.hold == desired
203
+
204
+ toggle_hold
205
+ end
206
+
207
+ # high range is 80-106 for F, 26-40 for C (by 0.5)
208
+ # low range is 50-99 for F, 10-26 for C (by 0.5)
209
+ def target_temperature=(desired)
210
+ return unless status
211
+ return if status.target_temperature == desired
212
+
213
+ desired *= 2 if (status && status.temperature_scale == :celsius) || desired < 50
214
+ send_message(Messages::SetTargetTemperature.new(desired.round))
215
+ end
216
+
217
+ def set_time(hour, minute, twenty_four_hour_time: false)
218
+ send_message(Messages::SetTime.new(hour, minute, twenty_four_hour_time))
149
219
  end
150
220
 
151
- def set_time(hour, minute, twenty_four_hour_time = false)
152
- hour |= 0x80 if twenty_four_hour_time
153
- send_message("\x0a\xbf\x21".force_encoding(Encoding::ASCII_8BIT) + hour.chr + minute.chr)
221
+ def temperature_scale=(scale)
222
+ raise ArgumentError, "scale must be :fahrenheit or :celsius" unless %I[fahrenheit celsius].include?(scale)
223
+
224
+ send_message(Messages::SetTemperatureScale.new(scale))
154
225
  end
155
226
 
156
- def set_temperature_scale(scale)
157
- raise ArgumentError, "scale must be :fahrenheit or :celsius" unless %I{fahrenheit :celsius}.include?(scale)
158
- arg = scale == :fahrenheit ? 0 : 1
159
- send_message("\x0a\xbf\x27\x01".force_encoding(Encoding::ASCII_8BIT) + arg.chr)
227
+ def update_filter_cycles(new_filter_cycles)
228
+ send_message(new_filter_cycles)
229
+ @filter_cycles = new_filter_cycles.dup
230
+ request_filter_configuration
160
231
  end
161
232
 
162
233
  def toggle_temperature_range
163
234
  toggle_item(0x50)
164
235
  end
165
236
 
166
- def set_temperature_range(desired)
167
- return unless last_status
168
- return if last_status.temperature_range == desired
237
+ def temperature_range=(desired)
238
+ return unless status
239
+ return if status.temperature_range == desired
240
+
169
241
  toggle_temperature_range
170
242
  end
171
243
 
172
244
  def toggle_heating_mode
173
- toggle_item(0x51)
174
- end
175
-
176
- HEATING_MODES = %I{ready rest ready_in_rest}.freeze
177
- def set_heating_mode(desired)
178
- raise ArgumentError, "heating_mode must be :ready or :rest" unless %I{ready rest}.include?(desired)
179
- return unless last_status
180
- times = if last_status.heating_mode == :ready && desired == :rest ||
181
- last_status.heating_mode == :rest && desired == :ready ||
182
- last_status.heating_mode == :ready_in_rest && desired == :rest
183
- 1
184
- elsif last_status.heating_mode == :ready_in_rest && desired == :ready
185
- 2
186
- else
187
- 0
188
- end
245
+ toggle_item(:heating_mode)
246
+ end
247
+
248
+ HEATING_MODES = %I[ready rest ready_in_rest].freeze
249
+ def heating_mode=(desired)
250
+ raise ArgumentError, "heating_mode must be :ready or :rest" unless %I[ready rest].include?(desired)
251
+ return unless status
252
+
253
+ times = if (status.heating_mode == :ready && desired == :rest) ||
254
+ (status.heating_mode == :rest && desired == :ready) ||
255
+ (status.heating_mode == :ready_in_rest && desired == :rest)
256
+ 1
257
+ elsif status.heating_mode == :ready_in_rest && desired == :ready
258
+ 2
259
+ else
260
+ 0
261
+ end
189
262
  times.times { toggle_heating_mode }
190
263
  end
191
264
  end
data/lib/bwa/crc.rb CHANGED
@@ -1,4 +1,6 @@
1
- require 'digest/crc'
1
+ # frozen_string_literal: true
2
+
3
+ require "digest/crc"
2
4
 
3
5
  module BWA
4
6
  class CRC < Digest::CRC8
data/lib/bwa/discovery.rb CHANGED
@@ -1,26 +1,27 @@
1
- require 'socket'
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+ require "bwa/logger"
2
5
 
3
6
  module BWA
4
7
  class Discovery
5
8
  class << self
6
- def discover(timeout = 5, exhaustive = false)
9
+ def discover(timeout = 5, exhaustive: false)
7
10
  socket = UDPSocket.new
8
11
  socket.bind("0.0.0.0", 0)
9
12
  socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
10
- socket.sendmsg("Discovery: Who is out there?", 0, Socket.sockaddr_in(30303, '255.255.255.255'))
13
+ socket.sendmsg("Discovery: Who is out there?", 0, Socket.sockaddr_in(30_303, "255.255.255.255"))
11
14
  spas = {}
12
15
  loop do
13
- if IO.select([socket], nil, nil, timeout)
14
- msg, ip = socket.recvfrom(64)
15
- ip = ip[2]
16
- name, mac = msg.split("\r\n")
17
- name.strip!
18
- if mac.start_with?("00-15-27-")
19
- spas[ip] = name
20
- break unless exhaustive
21
- end
22
- else
23
- break
16
+ break unless socket.wait_readable(timeout)
17
+
18
+ msg, ip = socket.recvfrom(64)
19
+ ip = ip[2]
20
+ name, mac = msg.split("\r\n")
21
+ name.strip!
22
+ if mac.start_with?("00-15-27-")
23
+ spas[ip] = name
24
+ break unless exhaustive
24
25
  end
25
26
  end
26
27
  spas
@@ -28,13 +29,14 @@ module BWA
28
29
 
29
30
  def advertise
30
31
  socket = UDPSocket.new
31
- socket.bind("0.0.0.0", 30303)
32
+ socket.bind("0.0.0.0", 30_303)
32
33
  msg = "BWGSPA\r\n00-15-27-00-00-01\r\n"
33
34
  loop do
34
35
  data, addr = socket.recvfrom(32)
35
- next unless data == 'Discovery: Who is out there?'
36
+ next unless data == "Discovery: Who is out there?"
37
+
36
38
  ip = addr.last
37
- puts "Advertising to #{ip}"
39
+ BWA.logger.info "Advertising to #{ip}"
38
40
  socket.sendmsg(msg, 0, Socket.sockaddr_in(addr[1], ip))
39
41
  end
40
42
  end
data/lib/bwa/logger.rb ADDED
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+
5
+ module BWA
6
+ # This module logs to stdout by default, or you can provide a logger as BWA.logger.
7
+ # If using default logger, set LOG_LEVEL in the environment to control logging.
8
+ #
9
+ # Log levels are:
10
+ #
11
+ # FATAL - fatal errors
12
+ # ERROR - handled errors
13
+ # WARN - problems while parsing known messages
14
+ # INFO - unrecognized messages
15
+ # DEBUG - all messages
16
+ #
17
+ # Certain very frequent messages are suppressed by default even in DEBUG mode.
18
+ # Set LOG_VERBOSITY to one of the following levels to see these:
19
+ #
20
+ # 0 - default
21
+ # 1 - show status messages
22
+ # 2 - show ready and nothing-to-send messages
23
+ #
24
+ class << self
25
+ attr_writer :logger, :verbosity
26
+
27
+ def logger
28
+ @logger ||= Logger.new($stdout).tap do |log|
29
+ $stdout.sync = true
30
+ log.level = ENV.fetch("LOG_LEVEL", "WARN")
31
+ log.formatter = proc do |severity, _datetime, _progname, msg|
32
+ "#{severity[0..0]}, #{msg2logstr(msg)}\n"
33
+ end
34
+ end
35
+ end
36
+
37
+ def verbosity
38
+ @verbosity ||= ENV.fetch("LOG_VERBOSITY", "0").to_i
39
+ @verbosity
40
+ end
41
+
42
+ def msg2logstr(msg)
43
+ case msg
44
+ when ::String
45
+ msg
46
+ when ::Exception
47
+ "#{msg.message} (#{msg.class})\n#{msg.backtrace&.join("\n")}"
48
+ else
49
+ msg.inspect
50
+ end
51
+ end
52
+
53
+ def raw2str(data)
54
+ data.unpack1("H*").gsub!(/(..)/, "\\1 ").chop!
55
+ end
56
+ end
57
+ end
data/lib/bwa/message.rb CHANGED
@@ -1,4 +1,7 @@
1
- require 'bwa/crc'
1
+ # frozen_string_literal: true
2
+
3
+ require "bwa/logger"
4
+ require "bwa/crc"
2
5
 
3
6
  module BWA
4
7
  class InvalidMessage < RuntimeError
@@ -11,30 +14,65 @@ module BWA
11
14
  end
12
15
 
13
16
  class Message
17
+ attr_accessor :src
18
+
14
19
  class Unrecognized < Message
15
20
  end
16
21
 
17
22
  class << self
18
23
  def inherited(klass)
24
+ super
25
+
19
26
  @messages ||= []
20
27
  @messages << klass
21
28
  end
22
29
 
30
+ # Ignore (parse and throw away) messages of these types.
31
+ IGNORED_MESSAGES = [
32
+ (+"\xbf\x00").force_encoding(Encoding::ASCII_8BIT), # request for new clients
33
+ (+"\xbf\xe1").force_encoding(Encoding::ASCII_8BIT),
34
+ (+"\xbf\x07").force_encoding(Encoding::ASCII_8BIT) # nothing to send
35
+ ].freeze
36
+
37
+ # Don't log messages of these types, even in DEBUG mode.
38
+ # They are very frequent and would swamp the logs.
39
+ def common_messages
40
+ @common_messages ||= begin
41
+ msgs = []
42
+ unless BWA.verbosity >= 1
43
+ msgs += [
44
+ Messages::Status::MESSAGE_TYPE,
45
+ (+"\xbf\xe1").force_encoding(Encoding::ASCII_8BIT)
46
+ ]
47
+ end
48
+ unless BWA.verbosity >= 2
49
+ msgs += [
50
+ (+"\xbf\x00").force_encoding(Encoding::ASCII_8BIT),
51
+ (+"\xbf\xe1").force_encoding(Encoding::ASCII_8BIT),
52
+ Messages::Ready::MESSAGE_TYPE,
53
+ (+"\xbf\x07").force_encoding(Encoding::ASCII_8BIT)
54
+ ]
55
+ end
56
+ msgs
57
+ end
58
+ end
59
+
23
60
  def parse(data)
24
61
  offset = -1
25
- message_type = length = message_class = nil
62
+ message_type = length = nil
26
63
  loop do
27
64
  offset += 1
65
+ # Not enough data for a full message; return and hope for more
28
66
  return nil if data.length - offset < 5
29
67
 
30
68
  # Keep scanning until message start char
31
- next unless data[offset] == '~'
69
+ next unless data[offset] == "~"
32
70
 
33
71
  # Read length (safe since we have at least 5 chars)
34
72
  length = data[offset + 1].ord
35
73
 
36
74
  # No message is this short or this long; keep scanning
37
- next if length < 5 or length >= '~'.ord
75
+ next if (length < 5) || (length >= "~".ord)
38
76
 
39
77
  # don't have enough data for what this message wants;
40
78
  # return and hope for more (yes this might cause a
@@ -43,7 +81,7 @@ module BWA
43
81
  return nil if length + 2 > data.length - offset
44
82
 
45
83
  # Not properly terminated; keep scanning
46
- next unless data[offset + length + 1] == '~'
84
+ next unless data[offset + length + 1] == "~"
47
85
 
48
86
  # Not a valid checksum; keep scanning
49
87
  next unless CRC.checksum(data.slice(offset + 1, length - 1)) == data[offset + length].ord
@@ -52,27 +90,33 @@ module BWA
52
90
  break
53
91
  end
54
92
 
55
- puts "discarding invalid data prior to message #{data[0...offset].unpack('H*').first}" unless offset == 0
56
- #puts "read #{data.slice(offset, length + 2).unpack('H*').first}"
93
+ message_type = data.slice(offset + 3, 2)
94
+ BWA.logger.debug "discarding invalid data prior to message #{BWA.raw2str(data[0...offset])}" unless offset.zero?
95
+ unless common_messages.include?(message_type)
96
+ BWA.logger.debug " read: #{BWA.raw2str(data.slice(offset,
97
+ length + 2))}"
98
+ end
57
99
 
58
100
  src = data[offset + 2].ord
59
- message_type = data.slice(offset + 3, 2)
60
101
  klass = @messages.find { |k| k::MESSAGE_TYPE == message_type }
61
102
 
62
-
63
- return [nil, offset + length + 2] if [
64
- "\xbf\x00".force_encoding(Encoding::ASCII_8BIT),
65
- "\xbf\xe1".force_encoding(Encoding::ASCII_8BIT),
66
- "\xbf\x07".force_encoding(Encoding::ASCII_8BIT)].include?(message_type)
103
+ # Ignore these message types
104
+ return [nil, offset + length + 2] if IGNORED_MESSAGES.include?(message_type)
67
105
 
68
106
  if klass
69
107
  valid_length = if klass::MESSAGE_LENGTH.respond_to?(:include?)
70
- klass::MESSAGE_LENGTH.include?(length - 5)
71
- else
72
- length - 5 == klass::MESSAGE_LENGTH
108
+ klass::MESSAGE_LENGTH.include?(length - 5)
109
+ else
110
+ length - 5 == klass::MESSAGE_LENGTH
111
+ end
112
+ unless valid_length
113
+ raise InvalidMessage.new("Unrecognized data length (#{length}) for message #{klass}",
114
+ data)
73
115
  end
74
- raise InvalidMessage.new("Unrecognized data length (#{length}) for message #{klass}", data) unless valid_length
75
116
  else
117
+ BWA.logger.info(
118
+ "Unrecognized message type #{BWA.raw2str(message_type)}: #{BWA.raw2str(data.slice(offset, length + 2))}"
119
+ )
76
120
  klass = Unrecognized
77
121
  end
78
122
 
@@ -80,56 +124,56 @@ module BWA
80
124
  message.parse(data.slice(offset + 5, length - 5))
81
125
  message.instance_variable_set(:@raw_data, data.slice(offset, length + 2))
82
126
  message.instance_variable_set(:@src, src)
127
+ BWA.logger.debug "from spa: #{message.inspect}" unless common_messages.include?(message_type)
83
128
  [message, offset + length + 2]
84
129
  end
85
130
 
86
- def format_time(hour, minute, twenty_four_hour_time = true)
131
+ def format_time(hour, minute, twenty_four_hour_time: true)
87
132
  if twenty_four_hour_time
88
- print_hour = "%02d" % hour
133
+ format("%02d:%02d", hour, minute)
89
134
  else
90
135
  print_hour = hour % 12
91
- print_hour = 12 if print_hour == 0
92
- am_pm = (hour >= 12 ? "PM" : "AM")
136
+ print_hour = 12 if print_hour.zero?
137
+ format("%d:%02d%s", print_hour, minute, hour >= 12 ? "PM" : "AM")
93
138
  end
94
- "#{print_hour}:#{"%02d" % minute}#{am_pm}"
95
139
  end
96
140
 
97
- def format_duration(hours, minutes)
98
- "#{hours}:#{"%02d" % minutes}"
141
+ def format_duration(minutes)
142
+ format("%d:%02d", minutes / 60, minutes % 60)
99
143
  end
100
144
  end
101
145
 
102
- attr_reader :raw_data, :src
146
+ attr_reader :raw_data
103
147
 
104
148
  def initialize
105
149
  # most messages we're sending come from this address
106
150
  @src = 0x0a
107
151
  end
108
152
 
109
- def parse(_data)
110
- end
153
+ def parse(_data); end
111
154
 
112
155
  def serialize(message = "")
113
156
  length = message.length + 5
114
- full_message = "#{length.chr}#{src.chr}#{self.class::MESSAGE_TYPE}#{message}".force_encoding(Encoding::ASCII_8BIT)
157
+ full_message = (+"#{length.chr}#{src.chr}#{self.class::MESSAGE_TYPE}#{message}")
158
+ .force_encoding(Encoding::ASCII_8BIT)
115
159
  checksum = CRC.checksum(full_message)
116
- "\x7e#{full_message}#{checksum.chr}\x7e".force_encoding(Encoding::ASCII_8BIT)
160
+ (+"\x7e#{full_message}#{checksum.chr}\x7e").force_encoding(Encoding::ASCII_8BIT)
117
161
  end
118
162
 
119
163
  def inspect
120
- "#<#{self.class.name} #{raw_data.unpack("H*").first}>"
164
+ "#<#{self.class.name} #{raw_data.unpack1("H*")}>"
121
165
  end
122
166
  end
123
167
  end
124
168
 
125
- require 'bwa/messages/configuration'
126
- require 'bwa/messages/configuration_request'
127
- require 'bwa/messages/control_configuration'
128
- require 'bwa/messages/control_configuration_request'
129
- require 'bwa/messages/filter_cycles'
130
- require 'bwa/messages/ready'
131
- require 'bwa/messages/set_temperature'
132
- require 'bwa/messages/set_temperature_scale'
133
- require 'bwa/messages/set_time'
134
- require 'bwa/messages/status'
135
- require 'bwa/messages/toggle_item'
169
+ require "bwa/messages/configuration"
170
+ require "bwa/messages/configuration_request"
171
+ require "bwa/messages/control_configuration"
172
+ require "bwa/messages/control_configuration_request"
173
+ require "bwa/messages/filter_cycles"
174
+ require "bwa/messages/ready"
175
+ require "bwa/messages/set_target_temperature"
176
+ require "bwa/messages/set_temperature_scale"
177
+ require "bwa/messages/set_time"
178
+ require "bwa/messages/status"
179
+ require "bwa/messages/toggle_item"