|
|
@@ -124,8 +124,10 @@ RSpec.describe 'Transitions' do
|
|
|
|
|
|
@mqtt_client.wait_for_listeners
|
|
|
|
|
|
+ expected_updates = calculate_transition_steps(start_value: 0, end_value: 255, duration: 2000)
|
|
|
+
|
|
|
expect(last_value).to eq(255)
|
|
|
- expect(seen_updates).to eq(8) # duration of 2000ms / 300ms period + 1 for initial packet
|
|
|
+ expect(seen_updates).to eq(expected_updates.length)
|
|
|
end
|
|
|
|
|
|
it 'should transition a field downwards' do
|
|
|
@@ -148,8 +150,10 @@ RSpec.describe 'Transitions' do
|
|
|
|
|
|
@mqtt_client.wait_for_listeners
|
|
|
|
|
|
+ expected_updates = calculate_transition_steps(start_value: 0, end_value: 255, duration: 2000)
|
|
|
+
|
|
|
expect(last_value).to eq(0)
|
|
|
- expect(seen_updates).to eq(8) # duration of 2000ms / 300ms period + 1 for initial packet
|
|
|
+ expect(seen_updates).to eq(expected_updates.length) # duration of 2000ms / 450ms period + 1 for initial packet
|
|
|
end
|
|
|
|
|
|
it 'should transition two fields at once if received in the same command' do
|
|
|
@@ -170,10 +174,12 @@ RSpec.describe 'Transitions' do
|
|
|
|
|
|
@mqtt_client.wait_for_listeners
|
|
|
|
|
|
+ expected_updates = calculate_transition_steps(start_value: 0, end_value: 250, duration: 2000)
|
|
|
+
|
|
|
expect(updates['hue'].last).to eq(250)
|
|
|
expect(updates['brightness'].last).to eq(0)
|
|
|
expect(updates['hue'].length == updates['brightness'].length).to eq(true), "Should have the same number of updates for both fields"
|
|
|
- expect(updates['hue'].length).to eq(8)
|
|
|
+ expect(updates['hue'].length).to eq(expected_updates.length)
|
|
|
end
|
|
|
end
|
|
|
|
|
|
@@ -205,7 +211,14 @@ RSpec.describe 'Transitions' do
|
|
|
|
|
|
@mqtt_client.wait_for_listeners
|
|
|
|
|
|
- expect(seen_updates.map { |x| x['brightness'] }).to eq([0, 64, 128, 191, 255])
|
|
|
+ expected_updates = calculate_transition_steps(start_value: 0, end_value: 255, period: 500, duration: 2000)
|
|
|
+
|
|
|
+ transitions_are_equal(
|
|
|
+ expected: expected_updates,
|
|
|
+ seen: seen_updates.map { |x| x['brightness'] },
|
|
|
+ # Allow some variation for the lossy level -> brightness conversion
|
|
|
+ allowed_variation: 2
|
|
|
+ )
|
|
|
expect((Time.now - start_time)/4).to be >= 0.5 # Don't count the first update
|
|
|
end
|
|
|
|
|
|
@@ -248,7 +261,12 @@ RSpec.describe 'Transitions' do
|
|
|
|
|
|
@mqtt_client.wait_for_listeners
|
|
|
|
|
|
- expect(seen_updates.map { |x| x['brightness'] }).to eq([0, 64, 128, 191, 255])
|
|
|
+ transitions_are_equal(
|
|
|
+ expected: calculate_transition_steps(start_value: 0, end_value: 255, duration: 2000, period: 500),
|
|
|
+ seen: seen_updates.map { |x| x['brightness'] },
|
|
|
+ # Allow some variation for the lossy level -> brightness conversion
|
|
|
+ allowed_variation: 2
|
|
|
+ )
|
|
|
end
|
|
|
end
|
|
|
|
|
|
@@ -270,7 +288,12 @@ RSpec.describe 'Transitions' do
|
|
|
@mqtt_client.wait_for_listeners
|
|
|
|
|
|
expect(seen_updates['state']).to eq(['ON'])
|
|
|
- expect(seen_updates['brightness']).to eq([0, 64, 128, 191, 255])
|
|
|
+ transitions_are_equal(
|
|
|
+ expected: calculate_transition_steps(start_value: 0, end_value: 255, duration: 1000),
|
|
|
+ seen: seen_updates['brightness'],
|
|
|
+ # Allow some variation for the lossy level -> brightness conversion
|
|
|
+ allowed_variation: 3
|
|
|
+ )
|
|
|
end
|
|
|
|
|
|
it 'should transition from on -> off' do
|
|
|
@@ -290,7 +313,12 @@ RSpec.describe 'Transitions' do
|
|
|
@mqtt_client.wait_for_listeners
|
|
|
|
|
|
expect(seen_updates['state']).to eq(['OFF'])
|
|
|
- expect(seen_updates['brightness']).to eq([255, 191, 128, 64, 0])
|
|
|
+ transitions_are_equal(
|
|
|
+ expected: calculate_transition_steps(start_value: 255, end_value: 0, duration: 1000),
|
|
|
+ seen: seen_updates['brightness'],
|
|
|
+ # Allow some variation for the lossy level -> brightness conversion
|
|
|
+ allowed_variation: 3
|
|
|
+ )
|
|
|
end
|
|
|
|
|
|
it 'should transition from off -> on with known last brightness' do
|
|
|
@@ -310,7 +338,12 @@ RSpec.describe 'Transitions' do
|
|
|
|
|
|
@mqtt_client.wait_for_listeners
|
|
|
|
|
|
- expect(seen_updates['brightness']).to eq([99, 140, 181, 222, 255])
|
|
|
+ transitions_are_equal(
|
|
|
+ expected: calculate_transition_steps(start_value: 99, end_value: 255, duration: 1000),
|
|
|
+ seen: seen_updates['brightness'],
|
|
|
+ # Allow some variation for the lossy level -> brightness conversion
|
|
|
+ allowed_variation: 4
|
|
|
+ )
|
|
|
end
|
|
|
|
|
|
it 'should transition from on -> off with known last brightness' do
|
|
|
@@ -329,7 +362,12 @@ RSpec.describe 'Transitions' do
|
|
|
|
|
|
@mqtt_client.wait_for_listeners
|
|
|
|
|
|
- expect(seen_updates['brightness']).to eq([99, 74, 48, 23, 0])
|
|
|
+ transitions_are_equal(
|
|
|
+ expected: calculate_transition_steps(start_value: 99, end_value: 0, duration: 1000),
|
|
|
+ seen: seen_updates['brightness'],
|
|
|
+ # Allow some variation for the lossy level -> brightness conversion
|
|
|
+ allowed_variation: 3
|
|
|
+ )
|
|
|
end
|
|
|
end
|
|
|
|
|
|
@@ -361,8 +399,11 @@ RSpec.describe 'Transitions' do
|
|
|
|
|
|
@mqtt_client.wait_for_listeners
|
|
|
|
|
|
- expect(seen_updates.length).to eq(5)
|
|
|
- expect(seen_updates.last[update_field]).to eq(update_max)
|
|
|
+ transitions_are_equal(
|
|
|
+ expected: calculate_transition_steps(start_value: update_min, end_value: update_max, duration: 1000),
|
|
|
+ seen: seen_updates.map{ |x| x[update_field] },
|
|
|
+ allowed_variation: 3
|
|
|
+ )
|
|
|
end
|
|
|
|
|
|
it "should support field '#{field}' max --> min" do
|
|
|
@@ -379,8 +420,11 @@ RSpec.describe 'Transitions' do
|
|
|
|
|
|
@mqtt_client.wait_for_listeners
|
|
|
|
|
|
- expect(seen_updates.length).to eq(5)
|
|
|
- expect(seen_updates.last[update_field]).to eq(update_min)
|
|
|
+ transitions_are_equal(
|
|
|
+ expected: calculate_transition_steps(start_value: update_max, end_value: update_min, duration: 1000),
|
|
|
+ seen: seen_updates.map{ |x| x[update_field] },
|
|
|
+ allowed_variation: 3
|
|
|
+ )
|
|
|
end
|
|
|
end
|
|
|
end
|
|
|
@@ -400,17 +444,26 @@ RSpec.describe 'Transitions' do
|
|
|
it 'should smoothly transition from one color to another' do
|
|
|
seen_updates = []
|
|
|
|
|
|
- fields = @client.get('/settings')['group_state_fields']
|
|
|
- @client.put(
|
|
|
- '/settings',
|
|
|
- group_state_fields: fields + %w(oh_color),
|
|
|
- mqtt_state_rate_limit: 1000
|
|
|
- )
|
|
|
+ end_color = '0,255,0'
|
|
|
+ end_hs = rgb_to_hs(end_color)
|
|
|
+
|
|
|
+ last_hue = nil
|
|
|
+ last_sat = nil
|
|
|
+
|
|
|
+ @mqtt_client.on_update(@id_params) do |id, message|
|
|
|
+ field, value = message.first
|
|
|
+
|
|
|
+ if field == 'hue'
|
|
|
+ last_hue = value
|
|
|
+ elsif field == 'saturation'
|
|
|
+ last_sat = value
|
|
|
+ end
|
|
|
|
|
|
- @mqtt_client.on_state(@id_params) do |id, message|
|
|
|
- color = message['color']
|
|
|
- seen_updates << color
|
|
|
- color == '0,255,0'
|
|
|
+ if !last_hue.nil? && !last_sat.nil?
|
|
|
+ seen_updates << {hue: last_hue, saturation: last_sat}
|
|
|
+ end
|
|
|
+
|
|
|
+ last_hue == end_hs[:hue] && last_sat == end_hs[:saturation]
|
|
|
end
|
|
|
|
|
|
response = @client.schedule_transition(@id_params, {
|
|
|
@@ -423,9 +476,7 @@ RSpec.describe 'Transitions' do
|
|
|
|
|
|
@mqtt_client.wait_for_listeners
|
|
|
|
|
|
- parts = seen_updates.map { |x| x.split(',').map(&:to_i) }
|
|
|
-
|
|
|
- # This is less even than you'd expect because RGB -> Hue/Sat is lossy.
|
|
|
+ # This ends up being less even than you'd expect because RGB -> Hue/Sat is lossy.
|
|
|
# Raw logs show that the right thing is happening:
|
|
|
#
|
|
|
# >>> stepSizes = (-64,64,0)
|
|
|
@@ -435,42 +486,105 @@ RSpec.describe 'Transitions' do
|
|
|
# >>> current color = (127,128,0)
|
|
|
# >>> current color = (63,192,0)
|
|
|
# >>> current color = (0,255,0)
|
|
|
- expect(parts).to eq([
|
|
|
- [255, 0, 0],
|
|
|
- [255, 84, 0],
|
|
|
- [250, 255, 0],
|
|
|
- [84, 255, 0],
|
|
|
- [0, 255, 0]
|
|
|
- ])
|
|
|
+ expected_updates = calculate_color_transition_steps(start_color: '255,0,0', end_color: '0,255,0', duration: 4000, period: 1000)
|
|
|
+
|
|
|
+ color_transitions_are_equal(
|
|
|
+ expected: expected_updates,
|
|
|
+ seen: seen_updates
|
|
|
+ )
|
|
|
end
|
|
|
|
|
|
it 'should handle color transitions from known state' do
|
|
|
seen_updates = []
|
|
|
|
|
|
- fields = @client.get('/settings')['group_state_fields']
|
|
|
- @client.put(
|
|
|
- '/settings',
|
|
|
- group_state_fields: fields + %w(oh_color),
|
|
|
- mqtt_state_rate_limit: 1000
|
|
|
- )
|
|
|
@client.patch_state({status: 'ON', color: '255,0,0'}, @id_params)
|
|
|
+ end_color = '0,0,255'
|
|
|
+ end_hs = rgb_to_hs(end_color)
|
|
|
+
|
|
|
+ last_hue = nil
|
|
|
+ last_sat = nil
|
|
|
|
|
|
- @mqtt_client.on_state(@id_params) do |id, message|
|
|
|
- color = message['color']
|
|
|
- seen_updates << color if color
|
|
|
- color == '0,0,255'
|
|
|
+ @mqtt_client.on_update(@id_params) do |id, message|
|
|
|
+ field, value = message.first
|
|
|
+
|
|
|
+ if field == 'hue'
|
|
|
+ last_hue = value
|
|
|
+ elsif field == 'saturation'
|
|
|
+ last_sat = value
|
|
|
+ end
|
|
|
+
|
|
|
+ if !last_hue.nil? && !last_sat.nil?
|
|
|
+ seen_updates << {hue: last_hue, saturation: last_sat}
|
|
|
+ end
|
|
|
+
|
|
|
+ last_hue == end_hs[:hue] && last_sat == end_hs[:saturation]
|
|
|
end
|
|
|
|
|
|
@client.patch_state({color: '0,0,255', transition: 2.0}, @id_params)
|
|
|
+
|
|
|
@mqtt_client.wait_for_listeners
|
|
|
|
|
|
- parts = seen_updates.map { |x| x.split(',').map(&:to_i) }
|
|
|
+ expected_updates = calculate_color_transition_steps(start_color: '255,0,0', end_color: '0,0,255', duration: 2000)
|
|
|
+
|
|
|
+ color_transitions_are_equal(
|
|
|
+ expected: expected_updates,
|
|
|
+ seen: seen_updates
|
|
|
+ )
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ context 'computed parameters' do
|
|
|
+ # it 'should accept no length parameters' do
|
|
|
+ # result = @client.schedule_transition(@id_params, field: 'kelvin', end_value: 100)
|
|
|
+
|
|
|
+ # puts @transition_defaults
|
|
|
+
|
|
|
+ # expect(result).to eq({'success' => true})
|
|
|
+ # end
|
|
|
+
|
|
|
+ (@transition_defaults = {
|
|
|
+ duration: {default: 4.5, test: 2},
|
|
|
+ num_periods: {default: 10, test: 5},
|
|
|
+ period: {default: 450, test: 225}
|
|
|
+ }).each do |k, params|
|
|
|
+ it "it should compute other parameters given only #{k}" do
|
|
|
+ seen_values = 0
|
|
|
+ gap = 0
|
|
|
+
|
|
|
+ @mqtt_client.on_update(@id_params) do |id, msg|
|
|
|
+ val = msg['brightness']
|
|
|
|
|
|
- expect(parts).to eq([
|
|
|
- [255,0,0],
|
|
|
- [161,0,255],
|
|
|
- [0,0,255]
|
|
|
- ])
|
|
|
+ if val > 0
|
|
|
+ seen_values += 1
|
|
|
+ last_seen = val
|
|
|
+ end
|
|
|
+
|
|
|
+ if seen_values == 3
|
|
|
+ gap = last_seen/seen_values
|
|
|
+ end
|
|
|
+
|
|
|
+ val == 255
|
|
|
+ end
|
|
|
+
|
|
|
+ t_params = {field: 'level', start_value: 0, end_value: 100}.merge({k => params[:test]})
|
|
|
+
|
|
|
+ start_time = Time.now
|
|
|
+
|
|
|
+ @client.schedule_transition(@id_params, t_params)
|
|
|
+ transitions = @client.transitions
|
|
|
+
|
|
|
+ @mqtt_client.wait_for_listeners
|
|
|
+ duration = Time.now - start_time
|
|
|
+
|
|
|
+ expect(transitions.length).to eq(1), "Should only be one active transition"
|
|
|
+
|
|
|
+ period = transitions.first['period']
|
|
|
+ expected_duration = (k == :duration ? params[:test] : (TransitionHelpers::Defaults::DURATION/1000.0))
|
|
|
+ num_periods = (expected_duration/period.to_f)*1000
|
|
|
+
|
|
|
+ expect(duration).to be_within(1.5).of(expected_duration)
|
|
|
+ expect(gap).to be_within(10).of((255/num_periods).ceil)
|
|
|
+ end
|
|
|
end
|
|
|
end
|
|
|
end
|