From db7e4fb82c68c82d467129c9c99d193e7dfc3027 Mon Sep 17 00:00:00 2001 From: Teemu R Date: Mon, 2 Oct 2017 10:16:04 +0200 Subject: [PATCH] Initial support for wifi speakers (#86) * Initial support for wifi speakers First steps towards fixing #69, we require feedback from someone who owns the device to complete it. * Fix import order to make tests pass * add the required parameter to volume up and volume down * Revise based on code review, add an example & some docstrings, plus fix invalid send() in status() --- mirobo/__init__.py | 1 + mirobo/discovery.py | 4 +- mirobo/wifispeaker.py | 99 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 mirobo/wifispeaker.py diff --git a/mirobo/__init__.py b/mirobo/__init__.py index 830c56d95..f7bfb2ccd 100644 --- a/mirobo/__init__.py +++ b/mirobo/__init__.py @@ -13,5 +13,6 @@ from mirobo.philips_eyecare import PhilipsEyecare from mirobo.chuangmi_ir import ChuangmiIr from mirobo.fan import Fan +from mirobo.wifispeaker import WifiSpeaker from mirobo.device import Device, DeviceException from mirobo.discovery import Discovery diff --git a/mirobo/discovery.py b/mirobo/discovery.py index e447e25b5..1bf1772a5 100644 --- a/mirobo/discovery.py +++ b/mirobo/discovery.py @@ -4,7 +4,8 @@ import inspect import codecs from . import (Device, Vacuum, Plug, PlugV1, Strip, AirPurifier, Ceil, - PhilipsEyecare, ChuangmiIr, AirHumidifier, WaterPurifier) + PhilipsEyecare, ChuangmiIr, AirHumidifier, WaterPurifier, + WifiSpeaker) from typing import Union, Callable, Dict, Optional # noqa: F401 @@ -31,6 +32,7 @@ "philips-light-bulb": Ceil, "philips-light-ceil": Ceil, "philips-light-sread1": PhilipsEyecare, + "xiaomi-wifispeaker-v1": WifiSpeaker, # name needs to be checked "yeelink-light-": lambda x: other_package_info( x, "python-yeelight package"), "lumi-gateway-": lambda x: other_package_info( diff --git a/mirobo/wifispeaker.py b/mirobo/wifispeaker.py new file mode 100644 index 000000000..2b64268e5 --- /dev/null +++ b/mirobo/wifispeaker.py @@ -0,0 +1,99 @@ +import warnings +import logging +from .device import Device + +_LOGGER = logging.getLogger(__name__) + + +class WifiSpeakerStatus: + def __init__(self, data): + # {"DeviceName": "Mi Internet Speaker", "channel_title\": "XXX", + # "current_state": "PLAYING", "hardware_version": "S602", + # "play_mode": "REPEAT_ALL", "track_artist": "XXX", + # "track_duration": "00:04:58", "track_title": "XXX", + # "transport_channel": "PLAYLIST"} + self.data = data + + @property + def device_name(self) -> str: + """Name of the device.""" + return self.data["DeviceName"] + + @property + def channel(self) -> str: + """Name of the channel.""" + return self.data["channel_title"] + + @property + def state(self) -> str: + """State of the device, e.g. PLAYING.""" + # note: this can be enumized when all values are known + return self.data["current_state"] + + @property + def hardware_version(self) -> str: + return self.data["hardware_version"] + + @property + def play_mode(self): + """Play mode such as REPEAT_ALL.""" + # note: this can be enumized when all values are known + return self.data["play_mode"] + + @property + def track_artist(self) -> str: + """Artist of the current track.""" + return self.data["track_artist"] + + @property + def track_title(self) -> str: + """Title of the current track.""" + return self.data["track_title"] + + @property + def track_duration(self) -> str: + """Total duration of the current track.""" + return self.data["track_duration"] + + @property + def transport_channel(self) -> str: + """Transport channel, e.g. PLAYLIST""" + # note: this can be enumized when all values are known + return self.data["transport_channel"] + + +class WifiSpeaker(Device): + def __init__(self, *args, **kwargs): + warnings.warn("Please help to complete this by providing more " + "information about possible values for `state`, " + "`play_mode` and `transport_channel`.", stacklevel=2) + super().__init__(*args, **kwargs) + + def status(self): + """Return device status.""" + return WifiSpeakerStatus(self.send("get_prop", ["umi"])) + + def power(self): + """Toggle power on and off.""" + # is this a toggle? + return self.send("power") + + def volume_up(self, amount: int = 5): + """Set volume up.""" + return self.send("vol_up", [amount]) + + def volume_down(self, amount: int = 5): + """Set volume down.""" + return self.send("vol_down", [amount]) + + def track_previous(self): + """Move to previous track.""" + return self.send("previous_track") + + def track_next(self): + """Move to next track.""" + return self.send("next_track") + + def track_position(self): + """Return current track position.""" + return self.send("get_prop", ["rel_time"])