Skip to content

Commit c5b3038

Browse files
committed
feat(server): add bounce effect
1 parent 9dd3d46 commit c5b3038

File tree

13 files changed

+176
-60
lines changed

13 files changed

+176
-60
lines changed

server/src/graphql/schema_input.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub(super) struct StaticLedEffectInput {
2626
pub duration: Option<i32>,
2727
#[graphql(description = "Name of the effect.")]
2828
pub name: Option<String>,
29-
#[graphql(description = "Hue/color (min 0.0, max 360.0)")]
29+
#[graphql(description = "Hue/color (min 0, max 360)")]
3030
pub hue: i32,
3131
#[graphql(description = "Saturation (min 0.0, max 1.0)")]
3232
pub saturation: f64,
@@ -44,7 +44,7 @@ pub(super) struct BreathingLedEffectInput {
4444
pub duration: Option<i32>,
4545
#[graphql(description = "Name of the effect.")]
4646
pub name: Option<String>,
47-
#[graphql(description = "Hue/color (min 0.0, max 360.0)")]
47+
#[graphql(description = "Hue/color (min 0, max 360)")]
4848
pub hue: i32,
4949
#[graphql(description = "Saturation (min 0.0, max 1.0)")]
5050
pub saturation: f64,
@@ -88,7 +88,7 @@ pub(super) struct BlinkLedEffectInput {
8888
pub duration: Option<i32>,
8989
#[graphql(description = "Name of the effect.")]
9090
pub name: Option<String>,
91-
#[graphql(description = "Hue/color (min 0.0, max 360.0)")]
91+
#[graphql(description = "Hue/color (min 0, max 360)")]
9292
pub hue: i32,
9393
#[graphql(description = "Saturation (min 0.0, max 1.0)")]
9494
pub saturation: f64,
@@ -101,14 +101,14 @@ pub(super) struct BlinkLedEffectInput {
101101
#[derive(GraphQLInputObject, Debug)]
102102
pub(super) struct CandleLedEffectInput {
103103
#[graphql(
104-
description = "If specified, must not be empty, and applies the effect only on these controller addresses."
104+
description = "If specified, must not be empty, and applies the effect only on these controller addresses."
105105
)]
106106
pub controllers: Option<Vec<String>>,
107107
#[graphql(description = "Duration of effect, in milliseconds, if specified.")]
108108
pub duration: Option<i32>,
109109
#[graphql(description = "Name of the effect.")]
110110
pub name: Option<String>,
111-
#[graphql(description = "Hue/color (min 0.0, max 360.0)")]
111+
#[graphql(description = "Hue/color (min 0, max 360)")]
112112
pub hue: i32,
113113
#[graphql(description = "Saturation (min 0.0, max 1.0)")]
114114
pub saturation: f64,
@@ -122,6 +122,26 @@ pub(super) struct CandleLedEffectInput {
122122
pub interval: Option<i32>,
123123
}
124124

125+
#[derive(GraphQLInputObject, Debug)]
126+
pub(super) struct BounceLedEffectInput {
127+
#[graphql(
128+
description = "If specified, must not be empty, and applies the effect only on these controller addresses."
129+
)]
130+
pub controllers: Option<Vec<String>>,
131+
#[graphql(description = "Duration of effect, in milliseconds, if specified.")]
132+
pub duration: Option<i32>,
133+
#[graphql(description = "Name of the effect.")]
134+
pub name: Option<String>,
135+
#[graphql(description = "Hue colors to bounce (each min 0, max 360)")]
136+
pub hues: Vec<i32>,
137+
#[graphql(description = "To Saturation (min 0.0, max 1.0)")]
138+
pub saturation: f64,
139+
#[graphql(description = "To value (min 0.0, max 1.0)")]
140+
pub value: f64,
141+
#[graphql(description = "Step ratio, percentage of change from one color to the other. (min 0.0=stay in from, max 1.0=stay in to)")]
142+
pub step: f64
143+
}
144+
125145
#[derive(GraphQLInputObject, Debug)]
126146
pub(super) struct StaticRumbleEffectInput {
127147
#[graphql(

server/src/graphql/schema_mutation.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ impl MutationRoot {
286286
}
287287

288288
#[graphql(
289-
description = "Randomly set brightness between min value and max value, simulating a candle/flame."
289+
description = "Randomly set brightness between min value and max value, simulating a candle/flame."
290290
)]
291291
fn set_led_candle(ctx: &Context, input: CandleLedEffectInput) -> FieldResult<MutationResponse> {
292292
tracing::info!(
@@ -363,6 +363,53 @@ impl MutationRoot {
363363
)
364364
}
365365

366+
#[graphql(
367+
description = "Bounces from one color to the other."
368+
)]
369+
fn set_led_bounce(ctx: &Context, input: BounceLedEffectInput) -> FieldResult<MutationResponse> {
370+
tracing::info!(
371+
"Received led bounce effect ({})",
372+
input
373+
.name
374+
.clone()
375+
.map_or(String::from("unnamed"), |name| format!("'{name}'"))
376+
);
377+
tracing::debug!("Effect input: {input:?}");
378+
379+
if input.name.map_or(false, |name| name.is_empty()) {
380+
return Err(FieldError::new("Name can't be empty!", Value::Null));
381+
}
382+
383+
if !input.hues.iter().all(|hue| (0..=360).contains(hue)) {
384+
return Err(FieldError::new(
385+
"Hue must be between 0 and 360!",
386+
Value::Null,
387+
));
388+
}
389+
390+
if !(0.0..=1.0).contains(&input.saturation) {
391+
return Err(FieldError::new(
392+
"Saturation must be between 0.0 and 1.0!",
393+
Value::Null,
394+
));
395+
}
396+
397+
if !(0.0..=1.0).contains(&input.value) {
398+
return Err(FieldError::new(
399+
"Min value must between 0.0 and 1.0!",
400+
Value::Null,
401+
));
402+
}
403+
404+
let effect = LedEffectKind::new_bounce(input.hues.iter().map(|hue| *hue as f32).collect(), input.saturation as f32, input.value as f32, input.step as f32);
405+
406+
process_led_effect_mutation(
407+
ctx,
408+
LedEffect::from(effect, input.duration),
409+
input.controllers,
410+
)
411+
}
412+
366413
#[graphql(description = "Turn rumble off.")]
367414
fn set_rumble_off(
368415
ctx: &Context,

server/src/graphql/schema_query.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ impl QueryRoot {
3737
api::LedEffectKind::Rainbow { .. } => { graphql::LedEffectType::Rainbow }
3838
api::LedEffectKind::Blink { .. } => { graphql::LedEffectType::Blink }
3939
api::LedEffectKind::Candle { .. } => { graphql::LedEffectType::Candle }
40+
api::LedEffectKind::Bounce { .. } => { graphql::LedEffectType::Bounce }
4041
},
4142
current_rumble_effect: match ctl.rumble_effect.kind {
4243
api::RumbleEffectKind::Off => { graphql::RumbleEffectType::Off }

server/src/graphql/schema_response.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub(super) enum LedEffectType {
3131
Rainbow,
3232
Blink,
3333
Candle,
34+
Bounce,
3435
}
3536

3637
#[derive(GraphQLEnum)]

server/src/monitoring/metrics.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,10 @@ pub async fn metrics_handler() -> Result<impl Reply, Rejection> {
2727
if let Err(e) = encoder.encode(&prometheus::gather(), &mut buffer) {
2828
eprintln!("could not encode prometheus metrics: {}", e);
2929
};
30-
let result = match String::from_utf8(buffer.clone()) {
31-
Ok(v) => v,
32-
Err(e) => {
33-
eprintln!("prometheus metrics could not be from_utf8'd: {}", e);
34-
String::default()
35-
}
36-
};
30+
let result = String::from_utf8(buffer.clone()).unwrap_or_else(|e| {
31+
eprintln!("prometheus metrics could not be from_utf8'd: {}", e);
32+
String::default()
33+
});
3734
buffer.clear();
3835

3936
Ok(result)

server/src/ps_move/api.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ impl PsMoveApi {
167167
}
168168

169169
if connection_type == ConnectionType::Usb {
170-
usb_path = path.clone();
170+
usb_path.clone_from(&path);
171171
bt_address = if cfg!(windows) {
172172
self.get_bt_address_on_windows(&path)
173173
} else {

server/src/ps_move/controller.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ impl PsMoveController {
7979
}
8080

8181
if self.connection_type == ConnectionType::Usb {
82-
self.info.bt_path = other.info.bt_path.clone();
82+
self.info.bt_path.clone_from(&other.info.bt_path);
8383
} else if self.connection_type == ConnectionType::Bluetooth {
84-
self.info.usb_path = other.info.usb_path.clone();
84+
self.info.usb_path.clone_from(&other.info.usb_path);
8585
}
8686
self.connection_type = ConnectionType::UsbAndBluetooth;
8787
}
@@ -94,10 +94,10 @@ impl PsMoveController {
9494
}
9595

9696
pub fn revert_led_effect(&mut self) {
97-
let current_effect = self.led_effect;
97+
let current_effect = self.led_effect.clone();
9898
let current_led = self.setting.led;
9999

100-
self.led_effect = self.last_led_effect;
100+
self.led_effect = self.last_led_effect.clone();
101101
self.setting.led = self.setting.last_led;
102102

103103
self.last_led_effect = current_effect;
@@ -110,22 +110,20 @@ impl PsMoveController {
110110
info!("Last led effect '{}' of '{}' has already expired, setting to off", last_led_effect, self.bt_address);
111111
let off_effect = LedEffect::off();
112112

113-
self.led_effect = off_effect;
114-
self.setting.led = off_effect.kind.get_initial_hsv()
113+
self.setting.led = off_effect.kind.get_initial_hsv();
114+
self.led_effect = off_effect
115115
}
116116
}
117117

118118
info!("Reverted '{:?}' to '{}'", self.bt_address, self.led_effect);
119119
}
120120

121121
pub fn set_led_effect(&mut self, effect: LedEffect) {
122-
self.last_led_effect = self.led_effect;
122+
self.last_led_effect = self.led_effect.clone();
123123
self.setting.last_led = self.setting.led;
124124

125-
let kind = effect.kind;
126-
125+
self.setting.led = effect.kind.get_initial_hsv();
127126
self.led_effect = effect;
128-
self.setting.led = kind.get_initial_hsv();
129127
}
130128

131129
pub fn set_led_effect_with_hsv(&mut self, effect: LedEffect, hsv: Hsv) {

0 commit comments

Comments
 (0)