Skip to content

Commit

Permalink
Add support for Walkingpad A1 (ksmb.walkingpad.v3) (rytilahti#975)
Browse files Browse the repository at this point in the history
* Initial integration walkingpad

* Removed print statement

* Implement PR review suggestions

* Fix step_count bug

* Update miio/walkingpad.py

Co-authored-by: Teemu R. <tpr@iki.fi>

* Fix step_count bug

* Update based on PR feedback & add startup speed / sensitivity functions

* Update docstring with class initialisation

* Implement PR feedback

* Rename time to walking_time and change to return timedelta

* Rename time to walking_time and change to return timedelta

* Correct the description for starting & stopping

* Fix mode and sensitivity return types. Resolve more PR feedback

* Change start function to power-on if treadmill is off when called. Also other minor PR feedback changes.

Co-authored-by: Teemu R. <tpr@iki.fi>
  • Loading branch information
2 people authored and xvlady committed May 9, 2021
1 parent 9425185 commit 0489610
Show file tree
Hide file tree
Showing 6 changed files with 519 additions and 3 deletions.
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 @@ -63,6 +63,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

0 comments on commit 0489610

Please sign in to comment.