Skip to content

Commit

Permalink
feat: implemented set for non-color bulbs
Browse files Browse the repository at this point in the history
  • Loading branch information
WhySoBad committed Apr 14, 2024
1 parent 9463043 commit ea24c71
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 36 deletions.
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,11 @@ Additionally, should the cli not meet your needs the use of protocol buffers all

## Supported devices

Currently, the following light bulbs are supported:
The following tapo smart light bulbs are supported:

* L530
* L630
* L900

For the following devices the support is coming soon™:

* L510
* L520
* L610
Expand Down
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
* [x] Keep devices into which the server cannot login in the devices list -> For completeness reasons
* [ ] Add device groups to control multiple devices at once
* [ ] Add some kind of metrics about devices
* [ ] Implement `set` for non-color bulbs
* [x] Implement `set` for non-color bulbs
* [x] Write docs
* [x] Better handling for expired sessions (SessionTimeout)
4 changes: 2 additions & 2 deletions example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ nmcli con modify tapoctl-hotspot wifi-sec.psk "<password>"
nmcli con up tapoctl-hotspot
```

To disable the internet access for this connection we have to add an iptable rule which drops all packets
To disable the internet access for this connection we have to add an iptables rule which drops all packets
which go from the `wlan0` interface to the `eth0` interface. By adding the following rule to the iptables we achieve the desired behavior:

```bash
Expand Down Expand Up @@ -86,7 +86,7 @@ services:
```
>[!NOTE]
> Should've compiled the tapoctl binary yourself on the Raspberry Pi you can start the server using `tapoctl serve -c path/to/config.toml`
> In case you've compiled the tapoctl binary yourself you can start the server using `tapoctl serve -c path/to/config.toml`

After starting the server you'll see your devices using:
```bash
Expand Down
10 changes: 0 additions & 10 deletions src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,16 +184,6 @@ impl Device {
None => Err(Status::unauthenticated(format!("The device '{}' is currently unauthenticated. Try again later or verify the configuration should the issue persist.", self.name)))
}
}

/// Mutable access to current device handler
///
/// Returns tonic status code should the handler be unavailable
pub fn get_handler_mut(&mut self) -> Result<&mut DeviceHandler, Status> {
match &mut self.handler {
Some(handler) => Ok(handler),
None => Err(Status::unauthenticated(format!("The device '{}' is currently unauthenticated. Try again later or verify the configuration should the issue persist.", self.name)))
}
}
}

pub enum DeviceHandler {
Expand Down
105 changes: 86 additions & 19 deletions src/tapo/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,24 +343,28 @@ impl Tapo for TapoService {
}
}

fn get_transformed_brightness(info: &InfoResponse, change: IntegerValueChange) -> u8 {
let mut current = u8::try_from(info.brightness.unwrap_or_default()).unwrap_or_default();
if change.absolute {
current = u8::try_from(change.value).unwrap_or_default();
} else {
let change_abs = u8::try_from(max(min(change.value.abs(), 100), 1)).unwrap_or_default();
if change.value.is_negative() {
current -= change_abs;
} else {
current += change_abs;
}
}
min(max(current, 1), 100)
}

match device.get_handler()? {
device::DeviceHandler::ColorLight(handler) => {
let mut info = self.get_state().await.get_info_silent(&device).await?;

let mut set = handler.set();
if let Some(change) = brightness {
let mut current = u8::try_from(info.brightness.unwrap_or_default()).unwrap_or_default();
if change.absolute {
current = u8::try_from(change.value).unwrap_or_default();
} else {
let change_abs = u8::try_from(max(min(change.value.abs(), 100), 1)).unwrap_or_default();
if change.value.is_negative() {
current -= change_abs;
} else {
current += change_abs;
}
}
current = min(max(current, 1), 100);
let current = get_transformed_brightness(&info, change);
set = set.brightness(current);
info.brightness = Some(current as u32);
info.device_on = Some(true);
Expand Down Expand Up @@ -450,13 +454,76 @@ impl Tapo for TapoService {
self.get_state().await.update_info_optimistically(device.name.clone(), info.clone());
Ok(Response::new(info))
}
device::DeviceHandler::Light(_handler) => {
Err(Status::unimplemented("Set API is not yet implemented for this device type"))?;
todo!("Send request for every sub-category to simulate set-api like behaviour")
}
_ => {
Err(Status::unimplemented("Set API is not supported by this device type"))
}
// LightDevice only supports brightness and power operations
device::DeviceHandler::Light(handler) => {
let mut info = self.get_state().await.get_info_silent(&device).await?;
// since `map_tapo_err` needs mutable reference on device we can call it only once
// therefore we store the result of
let mut err: Option<tapo::Error> = None;

if let Some(change) = brightness {
let current = get_transformed_brightness(&info.clone(), change);
match handler.set_brightness(current).await {
Ok(_) => {
info.brightness = Some(current as u32);
info.device_on = Some(true);
info.on_time = info.on_time.or(Some(0));
},
Err(error) => {
err = err.or(Some(error));
}
}
};
if let Some(power) = inner.power {
if power {
match handler.on().await {
Ok(_) => {
info.device_on = Some(true);
info.on_time = info.on_time.or(Some(0));
},
Err(error) => {
err = err.or(Some(error));
}
}
} else {
match handler.off().await {
Ok(_) => {
info.device_on = Some(false);
info.on_time = info.on_time.or(Some(0));
},
Err(error) => {
err = err.or(Some(error));
}
}
}
}

self.get_state().await.update_info_optimistically(device.name.clone(), info.clone());

if let Some(err) = err {
Err(err).map_tapo_err(&mut device).await?;
};

Ok(Response::new(info))
},
// GenericDevice only supports power operations
device::DeviceHandler::Generic(handler) => {
let mut info = self.get_state().await.get_info_silent(&device).await?;
if let Some(power) = inner.power {
if power {
handler.on().await.map_tapo_err(&mut device).await?;
info.device_on = Some(true);
info.on_time = info.on_time.or(Some(0));
} else {
handler.off().await.map_tapo_err(&mut device).await?;
info.device_on = Some(false);
info.on_time = None;
}
}

self.get_state().await.update_info_optimistically(device.name.clone(), info.clone());
Ok(Response::new(info))
},
}
}
}

0 comments on commit ea24c71

Please sign in to comment.