Skip to content

Commit

Permalink
Add test_properties command to device class (#1014)
Browse files Browse the repository at this point in the history
* Add test_properties command to device class

This allows simple testing of available properties, their values & the number of properties that can be requested at once.
This is done in two steps:
1. Testing all given properties one by one to see which return non-None values
2. Testing all valid, non-None values at once and removing properties to request on failures to obtain the max_properties value

Example output:

```
$ miiocli device --ip <addr> --token <token> test_properties power on off usb_on temperature wifi_led foofoo x
Running command test_properties
Testing properties ('power', 'on', 'off', 'usb_on', 'temperature', 'wifi_led', 'foofoo', 'x') for zimi.powerstrip.v2
Testing power.. on <class 'str'>
Testing on.. None
Testing off.. None
Testing usb_on.. None
Testing temperature.. 46.07 <class 'float'>
Testing wifi_led.. off <class 'str'>
Testing foofoo.. None
Testing x.. None
Found 8 valid properties, testing max_properties..
Testing 8 properties at once.. OK for 8 properties

Please copy the results below to your report
Model: zimi.powerstrip.v2
Total responsives: 8
Total non-empty: 3
All non-empty properties:
{'power': 'on', 'temperature': 46.07, 'wifi_led': 'off'}
Max properties: 8
Done
```

* Consider empty strings as non-existing properties

* Move pformat import to top of the file
  • Loading branch information
rytilahti authored Apr 11, 2021
1 parent fac4d1b commit d7507bb
Showing 1 changed file with 80 additions and 0 deletions.
80 changes: 80 additions & 0 deletions miio/device.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import inspect
import logging
from enum import Enum
from pprint import pformat as pf
from typing import Any, Optional # noqa: F401

import click
Expand Down Expand Up @@ -283,5 +284,84 @@ def get_properties(

return values

@command(
click.argument("properties", type=str, nargs=-1, required=True),
)
def test_properties(self, properties):
"""Helper to test device properties."""

def ok(x):
click.echo(click.style(x, fg="green", bold=True))

def fail(x):
click.echo(click.style(x, fg="red", bold=True))

try:
model = self.info().model
except Exception as ex:
_LOGGER.warning("Unable to obtain device model: %s", ex)
model = "<unavailable>"

click.echo(f"Testing properties {properties} for {model}")
valid_properties = {}
for property in properties:
try:
click.echo(f"Testing {property}.. ", nl=False)
resp = self.get_properties([property])
# Handle responses with one-element lists
if isinstance(resp, list) and len(resp) == 1:
resp = resp.pop()
value = valid_properties[property] = resp
if value is None:
fail("None")
elif not value:
fail("Empty response")
else:
ok(f"{value} {type(value)}")
except Exception as ex:
_LOGGER.warning("Unable to request %s: %s", property, ex)

click.echo(
f"Found {len(valid_properties)} valid properties, testing max_properties.."
)

props_to_test = list(valid_properties.keys())
max_properties = -1
while len(props_to_test) > 1:
try:
click.echo(
f"Testing {len(props_to_test)} properties at once.. ", nl=False
)
resp = self.get_properties(props_to_test)
if len(resp) == len(props_to_test):
max_properties = len(props_to_test)
ok(f"OK for {max_properties} properties")
break
else:
fail("Got different amount of properties than requested")

props_to_test.pop()
except Exception as ex:
_LOGGER.warning("Unable to request properties: %s", ex)
fail(ex)
props_to_test.pop()

non_empty_properties = {
k: v for k, v in valid_properties.items() if v is not None
}

click.echo(
click.style("\nPlease copy the results below to your report", bold=True)
)
click.echo("### Results ###")
click.echo(f"Model: {model}")
_LOGGER.debug(f"All responsive properties:\n{pf(valid_properties)}")
click.echo(f"Total responsives: {len(valid_properties)}")
click.echo(f"Total non-empty: {len(non_empty_properties)}")
click.echo(f"All non-empty properties:\n{pf(non_empty_properties)}")
click.echo(f"Max properties: {max_properties}")

return "Done"

def __repr__(self):
return f"<{self.__class__.__name__ }: {self.ip} (token: {self.token})>"

0 comments on commit d7507bb

Please sign in to comment.