Skip to content
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

Add support for Walkingpad A1 (ksmb.walkingpad.v3) #975

Merged
merged 15 commits into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ Supported devices
- Yeelight Dual Control Module (yeelink.switch.sw1)
- Scishare coffee maker (scishare.coffee.s1102)
- Qingping Air Monitor Lite (cgllc.airm.cgdn1)
- Xiaomi Walkingpad A1 (ksmb.walkingpad.v3)


*Feel free to create a pull request to add support for new devices as
Expand Down
28 changes: 26 additions & 2 deletions docs/api/miio.gateway.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
miio.gateway module
===================
miio.gateway package
====================

Subpackages
-----------

.. toctree::
:maxdepth: 4

miio.gateway.devices

Submodules
----------

.. toctree::
:maxdepth: 4

miio.gateway.alarm
miio.gateway.gateway
miio.gateway.gatewaydevice
miio.gateway.light
miio.gateway.radio
miio.gateway.zigbee

Module contents
---------------

.. automodule:: miio.gateway
:members:
Expand Down
22 changes: 21 additions & 1 deletion docs/api/miio.rst
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
miio package
============

Subpackages
-----------

.. toctree::
:maxdepth: 4

miio.gateway

Submodules
----------

.. toctree::
:maxdepth: 4

miio.airconditioner_miot
miio.airconditioningcompanion
miio.airconditioningcompanionMCN
miio.airdehumidifier
Expand All @@ -18,8 +27,10 @@ Submodules
miio.airhumidifier_miot
miio.airhumidifier_mjjsq
miio.airpurifier
miio.airpurifier_airdog
miio.airpurifier_miot
miio.airqualitymonitor
miio.airqualitymonitor_miot
miio.alarmclock
miio.aqaracamera
miio.ceil
Expand All @@ -30,15 +41,19 @@ Submodules
miio.cli
miio.click_common
miio.cooker
miio.curtain_youpin
miio.device
miio.discovery
miio.dreamevacuum_miot
miio.exceptions
miio.extract_tokens
miio.fan
miio.fan_common
miio.fan_leshow
miio.fan_miot
miio.gateway
miio.heater
miio.heater_miot
miio.huizuo
miio.miioprotocol
miio.miot_device
miio.philips_bulb
Expand All @@ -50,17 +65,22 @@ Submodules
miio.powerstrip
miio.protocol
miio.pwzn_relay
miio.scishare_coffeemaker
miio.toiletlid
miio.updater
miio.utils
miio.vacuum
miio.vacuum_cli
miio.vacuum_tui
miio.vacuumcontainers
miio.viomivacuum
miio.walkingpad
miio.waterpurifier
miio.waterpurifier_yunmi
miio.wifirepeater
miio.wifispeaker
miio.yeelight
miio.yeelight_dual_switch

Module contents
---------------
Expand Down
1 change: 1 addition & 0 deletions miio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
VacuumStatus,
)
from miio.viomivacuum import ViomiVacuum
from miio.walkingpad import Walkingpad
from miio.waterpurifier import WaterPurifier
from miio.waterpurifier_yunmi import WaterPurifierYunmi
from miio.wifirepeater import WifiRepeater
Expand Down
193 changes: 193 additions & 0 deletions miio/tests/test_walkingpad.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
from unittest import TestCase

import pytest

from miio import Walkingpad
from miio.walkingpad import (
OperationMode,
OperationSensitivity,
WalkingpadException,
WalkingpadStatus,
)

from .dummies import DummyDevice


class DummyWalkingpad(DummyDevice, Walkingpad):
def _get_state(self, props):
"""Return wanted properties."""

# Overriding here to deal with case of 'all' being requested

if props[0] == "all":
return self.state[props[0]]

return [self.state[x] for x in props if x in self.state]

def _set_state(self, var, value):
"""Set a state of a variable, the value is expected to be an array with length
of 1."""

# Overriding here to deal with case of 'all' being set

if var == "all":
self.state[var] = value
else:
self.state[var] = value.pop(0)

def __init__(self, *args, **kwargs):
self.state = {
"power": "on",
"mode": OperationMode.Manual,
"time": 1387,
"step": 2117,
"sensitivity": OperationSensitivity.Low,
"dist": 1150,
"sp": 3.15,
"cal": 71710,
"start_speed": 3.1,
"all": [
"mode:" + str(OperationMode.Manual.value),
"time:1387",
"sp:3.15",
"dist:1150",
"cal:71710",
"step:2117",
],
}
self.return_values = {
"get_prop": self._get_state,
"set_power": lambda x: self._set_state("power", x),
"set_mode": lambda x: self._set_state("mode", x),
"set_speed": lambda x: (
self._set_state(
"all",
[
"mode:1",
"time:1387",
"sp:" + str(x[0]),
"dist:1150",
"cal:71710",
"step:2117",
],
),
self._set_state("sp", x),
),
"set_step": lambda x: self._set_state("step", x),
"set_sensitivity": lambda x: self._set_state("sensitivity", x),
"set_start_speed": lambda x: self._set_state("start_speed", x),
"set_time": lambda x: self._set_state("time", x),
"set_distance": lambda x: self._set_state("dist", x),
}
super().__init__(args, kwargs)


@pytest.fixture(scope="class")
def walkingpad(request):
request.cls.device = DummyWalkingpad()


@pytest.mark.usefixtures("walkingpad")
class TestWalkingpad(TestCase):
def is_on(self):
return self.device.status().is_on

def state(self):
return self.device.status()

def test_on(self):
self.device.off() # ensure off
assert self.is_on() is False

self.device.on()
assert self.is_on() is True

def test_off(self):
self.device.on() # ensure on
assert self.is_on() is True

self.device.off()
assert self.is_on() is False

def test_status(self):
self.device._reset_state()

assert repr(self.state()) == repr(WalkingpadStatus(self.device.start_state))
assert self.is_on() is True
assert self.state().power == self.device.start_state["power"]
assert self.state().mode == self.device.start_state["mode"]
assert self.state().speed == self.device.start_state["sp"]
assert self.state().step_count == self.device.start_state["step"]
assert self.state().distance == self.device.start_state["dist"]
assert self.state().sensitivity == self.device.start_state["sensitivity"]
assert self.state().walking_time == self.device.start_state["time"]

def test_set_mode(self):
def mode():
return self.device.status().mode

self.device.set_mode(OperationMode.Auto)
assert mode() == OperationMode.Auto

self.device.set_mode(OperationMode.Manual)
assert mode() == OperationMode.Manual

with pytest.raises(WalkingpadException):
self.device.set_mode(-1)

with pytest.raises(WalkingpadException):
self.device.set_mode(3)

with pytest.raises(WalkingpadException):
self.device.set_mode("blah")

def test_set_speed(self):
def speed():
return self.device.status().speed

self.device.set_speed(3.055)
assert speed() == 3.055

with pytest.raises(WalkingpadException):
self.device.set_speed(7.6)

with pytest.raises(WalkingpadException):
self.device.set_speed(-1)

with pytest.raises(WalkingpadException):
self.device.set_speed("blah")

def test_set_start_speed(self):
def speed():
return self.device.status().start_speed

self.device.set_start_speed(3.055)
assert speed() == 3.055

with pytest.raises(WalkingpadException):
self.device.set_start_speed(7.6)

with pytest.raises(WalkingpadException):
self.device.set_start_speed(-1)

with pytest.raises(WalkingpadException):
self.device.set_start_speed("blah")

def test_set_sensitivity(self):
def sensitivity():
return self.device.status().sensitivity

self.device.set_sensitivity(OperationSensitivity.High)
assert sensitivity() == OperationSensitivity.High

self.device.set_sensitivity(OperationSensitivity.Medium)
assert sensitivity() == OperationSensitivity.Medium

with pytest.raises(WalkingpadException):
self.device.set_sensitivity(-1)

with pytest.raises(WalkingpadException):
self.device.set_sensitivity(99)

with pytest.raises(WalkingpadException):
self.device.set_sensitivity("blah")
Loading