This commit is contained in:
Mateusz Gruszczyński committed 2026-09-17 11:21:49 +02:00
1 parent 32f8a32bd2
commit df10ead47e
38 files changed
+655 -210

No files matched your search

+74 -10
View File
@@ -89,6 +89,41 @@ fn default_temperature_step() -> f64 {
1.0
}
pub const VERTICAL_SWING_MAX: u8 = 11;
pub const HORIZONTAL_SWING_MAX: u8 = 6;
#[derive(Debug, Clone, Copy, Deserialize)]
#[serde(untagged)]
enum LouverPositionCompat {
Bool(bool),
Number(u8),
}
fn louver_position_compat(value: LouverPositionCompat) -> u8 {
match value {
LouverPositionCompat::Bool(value) => u8::from(value),
LouverPositionCompat::Number(value) => value,
}
}
fn deserialize_louver_position<'de, D>(deserializer: D) -> Result<u8, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(louver_position_compat(LouverPositionCompat::deserialize(
deserializer,
)?))
}
fn deserialize_optional_louver_position<'de, D>(
deserializer: D,
) -> Result<Option<u8>, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(Option::<LouverPositionCompat>::deserialize(deserializer)?.map(louver_position_compat))
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "snake_case")]
pub enum EnergySourcePreference {
@@ -176,10 +211,12 @@ pub struct Device {
pub target_temperature: f64,
#[serde(default = "default_fan")]
pub fan_speed: u8,
#[serde(default)]
pub swing_vertical: bool,
#[serde(default)]
pub swing_horizontal: bool,
/// Raw GREE `SwUpDn` louver position. 0=off/default, 1=full swing, 2..6=fixed positions, 7..11=partial swing ranges.
#[serde(default, deserialize_with = "deserialize_louver_position")]
pub swing_vertical: u8,
/// Raw GREE `SwingLfRig` louver position. 0=off/default, 1=full swing, 2..6=fixed positions.
#[serde(default, deserialize_with = "deserialize_louver_position")]
pub swing_horizontal: u8,
#[serde(default)]
pub quiet: bool,
/// Raw non-zero value last reported by the unit for the GREE `Quiet` property.
@@ -290,8 +327,8 @@ impl Device {
mode: "cool".into(),
target_temperature: 23.0,
fan_speed: 0,
swing_vertical: false,
swing_horizontal: false,
swing_vertical: 0,
swing_horizontal: 0,
quiet: false,
quiet_wire_value: None,
turbo: false,
@@ -389,8 +426,12 @@ pub struct DeviceCommand {
pub mode: Option<String>,
pub target_temperature: Option<f64>,
pub fan_speed: Option<u8>,
pub swing_vertical: Option<bool>,
pub swing_horizontal: Option<bool>,
/// Raw GREE `SwUpDn` value. Legacy boolean payloads remain accepted (`false`=0, `true`=1).
#[serde(default, deserialize_with = "deserialize_optional_louver_position")]
pub swing_vertical: Option<u8>,
/// Raw GREE `SwingLfRig` value. Legacy boolean payloads remain accepted (`false`=0, `true`=1).
#[serde(default, deserialize_with = "deserialize_optional_louver_position")]
pub swing_horizontal: Option<u8>,
pub quiet: Option<bool>,
pub turbo: Option<bool>,
pub light: Option<bool>,
@@ -476,10 +517,10 @@ impl DeviceCommand {
device.fan_speed = v.min(5);
}
if let Some(v) = self.swing_vertical {
device.swing_vertical = v;
device.swing_vertical = v.min(VERTICAL_SWING_MAX);
}
if let Some(v) = self.swing_horizontal {
device.swing_horizontal = v;
device.swing_horizontal = v.min(HORIZONTAL_SWING_MAX);
}
if let Some(v) = self.quiet {
device.quiet = v;
@@ -544,4 +585,27 @@ mod connection_type_tests {
let migrated: Device = serde_json::from_value(value).expect("deserialize legacy device");
assert_eq!(migrated.connection_type, ConnectionType::Local);
}
#[test]
fn legacy_boolean_swing_values_migrate_to_positions() {
let device = Device::simulated_default();
let mut value = serde_json::to_value(device).expect("serialize device");
let object = value.as_object_mut().expect("device object");
object.insert("swing_vertical".into(), serde_json::Value::Bool(true));
object.insert("swing_horizontal".into(), serde_json::Value::Bool(false));
let migrated: Device = serde_json::from_value(value).expect("deserialize legacy device");
assert_eq!(migrated.swing_vertical, 1);
assert_eq!(migrated.swing_horizontal, 0);
}
#[test]
fn legacy_boolean_swing_command_is_accepted() {
let command: DeviceCommand = serde_json::from_value(serde_json::json!({
"swing_vertical": true,
"swing_horizontal": false
}))
.expect("deserialize legacy command");
assert_eq!(command.swing_vertical, Some(1));
assert_eq!(command.swing_horizontal, Some(0));
}
}