Class: BWA::Client

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/bwa/client.rb

Constant Summary collapse

HEATING_MODES =
%I[ready rest ready_in_rest].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uri) ⇒ Client

Returns a new instance of Client.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/bwa/client.rb', line 37

def initialize(uri)
  uri = URI.parse(uri)
  case uri.scheme
  when "tcp"
    require "socket"
    @io = TCPSocket.new(uri.host, uri.port || 4257)
  when "telnet", "rfc2217"
    require "net/telnet/rfc2217"
    @io = Net::Telnet::RFC2217.new(uri.host,
                                   port: uri.port || 23,
                                   baud: 115_200)
    @queue = []
  else
    require "ccutrer-serialport"
    @io = CCutrer::SerialPort.new(uri.path, baud: 115_200, data_bits: 8, parity: :none, stop_bits: 1)
    @queue = []
  end
  @src = 0x0a
  @buffer = +""
end

Instance Attribute Details

#configurationObject (readonly)

Returns the value of attribute configuration.



13
14
15
# File 'lib/bwa/client.rb', line 13

def configuration
  @configuration
end

#control_configurationObject (readonly)

Returns the value of attribute control_configuration.



13
14
15
# File 'lib/bwa/client.rb', line 13

def control_configuration
  @control_configuration
end

#filter_cyclesObject (readonly)

Returns the value of attribute filter_cycles.



13
14
15
# File 'lib/bwa/client.rb', line 13

def filter_cycles
  @filter_cycles
end

#statusObject (readonly)

Returns the value of attribute status.



13
14
15
# File 'lib/bwa/client.rb', line 13

def status
  @status
end

Instance Method Details

#blower=(desired) ⇒ Object



210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/bwa/client.rb', line 210

def blower=(desired)
  return unless status && configuration

  desired = 0 if desired == false
  desired = configuration.blower if desired == true
  desired = [desired, configuration.blower].min
  times = (desired - status.blower) % (configuration.blower + 1)
  times.times do |i|
    toggle_blower
    sleep(0.1) unless i == times - 1
  end
end

#drain_message_queueObject



108
109
110
# File 'lib/bwa/client.rb', line 108

def drain_message_queue
  poll while messages_pending?
end

#full_configuration?Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/bwa/client.rb', line 58

def full_configuration?
  status && control_configuration && configuration && filter_cycles
end

#heating_mode=(desired) ⇒ Object

Raises:

  • (ArgumentError)


272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/bwa/client.rb', line 272

def heating_mode=(desired)
  raise ArgumentError, "heating_mode must be :ready or :rest" unless %I[ready rest].include?(desired)
  return unless status

  times = if (status.heating_mode == :ready && desired == :rest) ||
             (status.heating_mode == :rest && desired == :ready) ||
             (status.heating_mode == :ready_in_rest && desired == :rest)
            1
          elsif status.heating_mode == :ready_in_rest && desired == :ready
            2
          else
            0
          end
  times.times do |i|
    toggle_heating_mode
    sleep(0.1) unless i == times - 1
  end
end

#hold=(desired) ⇒ Object



223
224
225
226
227
228
# File 'lib/bwa/client.rb', line 223

def hold=(desired)
  return unless status
  return if status.hold == desired

  toggle_hold
end

#messages_pending?Boolean

Returns:

  • (Boolean)


104
105
106
# File 'lib/bwa/client.rb', line 104

def messages_pending?
  !!@io.wait_readable(0)
end

#mister=(desired) ⇒ Object



203
204
205
206
207
208
# File 'lib/bwa/client.rb', line 203

def mister=(desired)
  return unless status
  return if status.mister == desired

  toggle_mister
end

#pollObject



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/bwa/client.rb', line 62

def poll
  message = bytes_read = nil
  loop do
    message, bytes_read = Message.parse(@buffer)
    # discard how much we read
    @buffer = @buffer[bytes_read..-1] if bytes_read
    method = @io.respond_to?(:readpartial) ? :readpartial : :read
    unless message
      # one EOF is just serial ports saying they have no data;
      # several EOFs in a row is the file is dead and gone
      eofs = 0
      begin
        @buffer.concat(@io.__send__(method, 64 * 1024))
      rescue EOFError
        eofs += 1
        raise if eofs == 5

        @io.wait_readable(5)
        retry
      rescue Errno::ECONNRESET
        BWA.logger.debug "ECONNRESET occured; retrying"
        @io.wait_readable(5)
        retry
      end
      next
    end
    break
  end

  if message.is_a?(Messages::Ready) && (msg = @queue&.shift)
    unless BWA.verbosity < 1 && msg[3..4] == Messages::ControlConfigurationRequest::MESSAGE_TYPE
      BWA.logger.debug "wrote: #{BWA.raw2str(msg)}"
    end
    @io.write(msg)
  end
  @status = message.dup if message.is_a?(Messages::Status)
  @filter_cycles = message.dup if message.is_a?(Messages::FilterCycles)
  @control_configuration = message.dup if message.is_a?(Messages::ControlConfiguration)
  @configuration = message.dup if message.is_a?(Messages::ControlConfiguration2)
  message
end

#request_configurationObject



128
129
130
# File 'lib/bwa/client.rb', line 128

def request_configuration
  send_message(Messages::ConfigurationRequest.new)
end

#request_control_infoObject



136
137
138
# File 'lib/bwa/client.rb', line 136

def request_control_info
  send_message(Messages::ControlConfigurationRequest.new(1))
end

#request_control_info2Object



132
133
134
# File 'lib/bwa/client.rb', line 132

def request_control_info2
  send_message(Messages::ControlConfigurationRequest.new(2))
end

#request_filter_configurationObject



140
141
142
# File 'lib/bwa/client.rb', line 140

def request_filter_configuration
  send_message(Messages::ControlConfigurationRequest.new(3))
end

#send_message(message) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/bwa/client.rb', line 112

def send_message(message)
  message.src = @src
  full_message = message.serialize
  unless BWA.verbosity < 1 && message.is_a?(Messages::ControlConfigurationRequest)
    BWA.logger.info "  to spa: #{message.inspect}"
  end
  if @queue
    @queue.push(full_message)
  else
    unless BWA.verbosity < 1 && message.is_a?(Messages::ControlConfigurationRequest)
      BWA.logger.debug "wrote: #{BWA.raw2str(full_message)}"
    end
    @io.write(full_message)
  end
end

#set_pump(index, desired) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/bwa/client.rb', line 172

def set_pump(index, desired)
  return unless status && configuration

  desired = 0 if desired == false
  max_pump_speed = configuration.pumps[index]
  desired = max_pump_speed if desired == true
  desired = [desired, max_pump_speed].min

  # turn all pumps off with a single command
  return toggle_item(:soak) if desired.zero? && configuration.pumps.length == 1 && max_pump_speed != 1

  current_pump_speed = [status.pumps[index], max_pump_speed].min
  times = (desired - current_pump_speed) % (max_pump_speed + 1)
  times.times do |i|
    toggle_pump(index)
    sleep(0.1) unless i == times - 1
  end
end

#set_time(hour, minute, twenty_four_hour_time: false) ⇒ Object



240
241
242
# File 'lib/bwa/client.rb', line 240

def set_time(hour, minute, twenty_four_hour_time: false)
  send_message(Messages::SetTime.new(hour, minute, twenty_four_hour_time))
end

#target_temperature=(desired) ⇒ Object

high range is 80-106 for F, 26-40 for C (by 0.5) low range is 50-99 for F, 10-26 for C (by 0.5)



232
233
234
235
236
237
238
# File 'lib/bwa/client.rb', line 232

def target_temperature=(desired)
  return unless status
  return if status.target_temperature == desired

  desired *= 2 if (status && status.temperature_scale == :celsius) || desired < 50
  send_message(Messages::SetTargetTemperature.new(desired.round))
end

#temperature_range=(desired) ⇒ Object



260
261
262
263
264
265
# File 'lib/bwa/client.rb', line 260

def temperature_range=(desired)
  return unless status
  return if status.temperature_range == desired

  toggle_temperature_range
end

#temperature_scale=(scale) ⇒ Object

Raises:

  • (ArgumentError)


244
245
246
247
248
# File 'lib/bwa/client.rb', line 244

def temperature_scale=(scale)
  raise ArgumentError, "scale must be :fahrenheit or :celsius" unless %I[fahrenheit celsius].include?(scale)

  send_message(Messages::SetTemperatureScale.new(scale))
end

#toggle_aux(index) ⇒ Object



156
157
158
# File 'lib/bwa/client.rb', line 156

def toggle_aux(index)
  toggle_item(index + 0x16)
end

#toggle_blowerObject



164
165
166
# File 'lib/bwa/client.rb', line 164

def toggle_blower
  toggle_item(:blower)
end

#toggle_heating_modeObject



267
268
269
# File 'lib/bwa/client.rb', line 267

def toggle_heating_mode
  toggle_item(:heating_mode)
end

#toggle_holdObject



168
169
170
# File 'lib/bwa/client.rb', line 168

def toggle_hold
  toggle_item(:hold)
end

#toggle_item(item) ⇒ Object



144
145
146
# File 'lib/bwa/client.rb', line 144

def toggle_item(item)
  send_message(Messages::ToggleItem.new(item))
end

#toggle_light(index) ⇒ Object



152
153
154
# File 'lib/bwa/client.rb', line 152

def toggle_light(index)
  toggle_item(index + 0x11)
end

#toggle_misterObject



160
161
162
# File 'lib/bwa/client.rb', line 160

def toggle_mister
  toggle_item(:mister)
end

#toggle_pump(index) ⇒ Object



148
149
150
# File 'lib/bwa/client.rb', line 148

def toggle_pump(index)
  toggle_item(index + 0x04)
end

#toggle_temperature_rangeObject



256
257
258
# File 'lib/bwa/client.rb', line 256

def toggle_temperature_range
  toggle_item(0x50)
end

#update_filter_cycles(new_filter_cycles) ⇒ Object



250
251
252
253
254
# File 'lib/bwa/client.rb', line 250

def update_filter_cycles(new_filter_cycles)
  send_message(new_filter_cycles)
  @filter_cycles = new_filter_cycles.dup
  request_filter_configuration
end