import pkgutil
import importlib
import inspect
from typing import List, Dict, Set, Type
from miio.device import Device
import pickle
DeviceTypeName = str
DeviceTypeClass = Type[Device]
CallableMethodSignature = str
def _load_integration_modules() -> None:
package = importlib.import_module("miio.integrations")
for module_info in pkgutil.walk_packages(package.__path__, package.__name__ + "."):
try:
importlib.import_module(module_info.name)
except Exception:
continue
def get_device_types() -> List[DeviceTypeName]:
_load_integration_modules()
device_classes: Set[DeviceTypeClass] = set()
def recurse_subclasses(cls: Type[Device]) -> None:
for subclass in cls.__subclasses__():
device_classes.add(subclass)
recurse_subclasses(subclass)
recurse_subclasses(Device)
return [cls.__name__ for cls in device_classes]
def _get_device_object(ip: str, token: str, device_type: DeviceTypeName) -> Device:
_load_integration_modules()
device_classes: Set[DeviceTypeClass] = set()
def recurse_subclasses(cls: Type[Device]) -> None:
for subclass in cls.__subclasses__():
device_classes.add(subclass)
recurse_subclasses(subclass)
recurse_subclasses(Device)
for cls in device_classes:
if cls.__name__ == device_type:
return cls(ip, token)
raise ValueError(f"Device type '{device_type}' not found")
def _get_device_bytes(device: Device) -> bytes:
return pickle.dumps(device)
def get_device(ip: str, token: str, device_type: DeviceTypeName) -> bytes:
device = _get_device_object(ip, token, device_type)
return _get_device_bytes(device)
def _get_device_methods(device: Device) -> Dict[str, CallableMethodSignature]:
methods_info: Dict[str, CallableMethodSignature] = {}
for name, member in inspect.getmembers(device, predicate=callable):
if not name.startswith("_"):
try:
sig = inspect.signature(member)
methods_info[name] = str(sig)
except Exception:
methods_info[name] = "No signature available"
return methods_info
def get_device_methods(device: bytes) -> Dict[str, CallableMethodSignature]:
device = pickle.loads(device)
return _get_device_methods(device)
import ast
def _call_method(device: Device, method_name: str, args: List[str]) -> str:
try:
converted_args = [ast.literal_eval(arg) for arg in args]
print([type(arg) for arg in converted_args])
method = getattr(device, method_name, None)
if not method or not callable(method):
raise ValueError(f"Method '{method_name}' not found on device {type(device).__name__}")
result = method(*converted_args)
return str(result)
except Exception as e:
return f"Error calling method '{method_name}': {e}"
def call_method(device: bytes, method_name: str, args: List[str]) -> str:
device = pickle.loads(device)
return _call_method(device, method_name, args)
if __name__ == "__main__":
print("Available device types:", get_device_types())
device_type = "Yeelight"
import constants
ip = constants.ip
token = constants.token
device = get_device(ip, token, device_type)
methods_info = get_device_methods(device)
print("Callable methods with parameters:")
for method, params in methods_info.items():
print(f"{method}{params}")
toggle_methods = [method for method in methods_info if "set_rgb" in method]
if toggle_methods:
toggle_method = toggle_methods[0]
result = call_method(device, toggle_method, ["(40, 40, 40)"])
print(f"Result of calling {toggle_method}: {result}")
else:
print("No toggle methods found")