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



206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/bwa/client.rb', line 206

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



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

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)


268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/bwa/client.rb', line 268

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



219
220
221
222
223
224
# File 'lib/bwa/client.rb', line 219

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

  toggle_hold
end

#messages_pending?Boolean

Returns:

  • (Boolean)


100
101
102
# File 'lib/bwa/client.rb', line 100

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

#mister=(desired) ⇒ Object



199
200
201
202
203
204
# File 'lib/bwa/client.rb', line 199

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
# 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
        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



124
125
126
# File 'lib/bwa/client.rb', line 124

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

#request_control_infoObject



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

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

#request_control_info2Object



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

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

#request_filter_configurationObject



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

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

#send_message(message) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/bwa/client.rb', line 108

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



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/bwa/client.rb', line 168

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



236
237
238
# File 'lib/bwa/client.rb', line 236

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)



228
229
230
231
232
233
234
# File 'lib/bwa/client.rb', line 228

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



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

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

  toggle_temperature_range
end

#temperature_scale=(scale) ⇒ Object

Raises:

  • (ArgumentError)


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

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



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

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

#toggle_blowerObject



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

def toggle_blower
  toggle_item(:blower)
end

#toggle_heating_modeObject



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

def toggle_heating_mode
  toggle_item(:heating_mode)
end

#toggle_holdObject



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

def toggle_hold
  toggle_item(:hold)
end

#toggle_item(item) ⇒ Object



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

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

#toggle_light(index) ⇒ Object



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

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

#toggle_misterObject



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

def toggle_mister
  toggle_item(:mister)
end

#toggle_pump(index) ⇒ Object



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

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

#toggle_temperature_rangeObject



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

def toggle_temperature_range
  toggle_item(0x50)
end

#update_filter_cycles(new_filter_cycles) ⇒ Object



246
247
248
249
250
# File 'lib/bwa/client.rb', line 246

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