-
-
Notifications
You must be signed in to change notification settings - Fork 44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial KE100 support. #121
Initial KE100 support. #121
Conversation
I did some more digging and the EmptyResult error is thrown in control_child method in pub(crate) async fn control_child<R>(
&self,
device_id: String,
child_request: TapoRequest,
) -> Result<R, Error>
where
R: fmt::Debug + DeserializeOwned + TapoResponseExt,
{
debug!("Control child...");
let params = MultipleRequestParams::new(vec![child_request]);
let request = TapoRequest::MultipleRequest(Box::new(TapoParams::new(params)));
let params = ControlChildParams::new(device_id, request);
let request = TapoRequest::ControlChild(Box::new(TapoParams::new(params)));
let responses = self
.protocol
.execute_request::<ControlChildResult<TapoMultipleResponse<R>>>(request, true)
.await?
.ok_or_else(|| Error::Tapo(TapoResponseError::EmptyResult))?
.response_data
.result
.responses;
let response = responses
.into_iter()
.next()
.ok_or_else(|| Error::Tapo(TapoResponseError::EmptyResult))?;
validate_response(&response)?;
response
.result
.ok_or_else(|| Error::Tapo(TapoResponseError::EmptyResult))
} Since the control_child method is used for read and write operations, I guess we need to check the result to be empty at some point, but would it make sense to do the check, when we interpret the result? |
Looking at the tapo app source code, I see a few fields that might be useful:
You're correct. |
Can you please run the example with For example,
|
This what I get when executing
What do you think about splitting |
This should get the job done. api_client pub(crate) async fn control_child<R>(
&self,
device_id: String,
child_request: TapoRequest,
) -> Result<Option<R>, Error>
where
R: fmt::Debug + DeserializeOwned + TapoResponseExt,
{
debug!("Control child...");
let params = MultipleRequestParams::new(vec![child_request]);
let request = TapoRequest::MultipleRequest(Box::new(TapoParams::new(params)));
let params = ControlChildParams::new(device_id, request);
let request = TapoRequest::ControlChild(Box::new(TapoParams::new(params)));
let responses = self
.protocol
.execute_request::<ControlChildResult<TapoMultipleResponse<R>>>(request, true)
.await?
.ok_or_else(|| Error::Tapo(TapoResponseError::EmptyResult))?
.response_data
.result
.responses;
let response = responses
.into_iter()
.next()
.ok_or_else(|| Error::Tapo(TapoResponseError::EmptyResult))?;
validate_response(&response)?;
Ok(response.result)
} hub_handler pub(crate) async fn control_child<R>(
&self,
device_id: String,
request_data: TapoRequest,
) -> Result<Option<R>, Error>
where
R: fmt::Debug + DeserializeOwned + TapoResponseExt,
{
self.client.control_child(device_id, request_data).await
} s200b_handler pub async fn get_device_info(&self) -> Result<S200BResult, Error> {
let request = TapoRequest::GetDeviceInfo(TapoParams::new(EmptyParams));
self.hub_handler
.control_child(self.device_id.clone(), request)
.await?
.ok_or_else(|| Error::Tapo(TapoResponseError::EmptyResult))
} |
Thanks for the code snippets. I managed to resolve the issue with the |
Meanwhile I added support for min/max_control temp, child_protection and temp_offset. Let me know if you have any other thoughts on what could be added. |
You're correct. However, It would be useful to include Regarding your question, there's a separate endpoint called |
I've tried the changes you proposed but seem to be getting a compilation error in
I suggest we try to get this one fixed and then close this PR. I'll keep working on the other stuff and will create another PR once ready. Thanks for the help with getting the Rust code right. As you may have recognized I'm learning by doing here. |
Never mind, I fixed it. If I understood the Rust documentation right, I needed to bind control_child: pub async fn set_temperature(&self, target_temperature: u8) -> Result<(), Error> {
let control_range = self.get_control_range().await?;
if target_temperature < control_range[0] || target_temperature > control_range[1] {
return Err(Error::Validation {
field: "target_temperature".to_string(),
message: format!("Target temperature must be between {} (min_control_temp) and {} (max_control_temp)", control_range[0], control_range[1]),
});
}
let json = serde_json::to_value(TrvSetDeviceInfoParams::new().target_temp(target_temperature)?)?;
let request = TapoRequest::SetDeviceInfo(Box::new(TapoParams::new(json)));
self.hub_handler
.control_child::<KE100Result>(self.device_id.clone(), request)
.await?;
Ok(())
} I assume I can use the same pattern for all the functions which don't return a result? |
Exactly, yeah. However, it would be better to use Please give me a shout when you're done with everything you want to do, and I'll do a review. |
Just pushed the last updates. I think the PR is ready to go for review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very well done. You have addressed all the important aspects, which is impressive considering you are new to Rust.
I am aware that I left a few comments, but they are all minor and mostly related to naming and unnecessary elements that can be removed.
4498527
to
79a4a91
Compare
…ost protection returns empty result, which creates and EmptyResult error.
and handling None result in set_frost_protection_on response
Fixing documenation for target_temperature to be only u8. Fixing type defintions for min/max_control_temp.
target_temperature is validated against current min/max_control_temp Added function to set min_temp but currently does not affect device
As suggested by mihai-dinculescu Co-authored-by: Mihai Dinculescu <[email protected]>
- Rename temp to temperature in variable names and add renaming in serde - Add module for TemperatureUnit enum and remove declaration from results - Remove set_min_temperature() function - Replace get_control_range() with min/max from get_device_info() - Replace `unwrap()` with `?`
hub_handler returns Result<Option<R>, Error> for control_child().
0dead6c
to
10c919a
Compare
I found one more thing I may need your help with. It's about decoding the base64 encoded nickname. The following fragment in let hub = ApiClient::new(tapo_username, tapo_password)?
.h100(ip_address)
.await?;
// Get a handler for the child device
let device = hub.ke100(device_id);
// Get the device info of the child device
let device_info = device.get_device_info().await?;
info!("Device info: {device_info:?}"); But when I use the detour via // Adapted code from examples/tapo_h100.rs
let hub = ApiClient::new(tapo_username, tapo_password)?
.h100(ip_address)
.await?;
let child_device_list = hub.get_child_device_list().await?;
for child in child_device_list {
match child {
ChildDeviceResult::KE100(device) => {
info!(
"Found KE100 child device with nickname: {}, id: {}, current temperature: {} {:?} and target temperature: {} {:?}.",
device.nickname,
device.device_id,
device.current_temperature,
device.temperature_unit,
device.target_temperature,
device.temperature_unit,
);
} Do you see a similar behavior, when you test other child devices such as s200b or t300? Is this intended behavior or should we do something about this? |
Ah, that is a defect. Nice find! |
- Enforce single value TemperatureUnit enum for KE100 with Celsius - Revert previous change to global TemperatureUnit enum
- Fix for nickname decoding
Merged your fixes for the nickname decoding into this PR. The decoding seems to work also with the feature I added to supported empty results for the |
65dcc08
to
07cf962
Compare
I've committed some minor tweaks for language and lints. |
a086c43
to
81dde27
Compare
Released in v0.7.6. |
Reading data works. Setting temperature and frost protection returns empty result from the device, which creates an EmptyResult runtime error.