Skip to content

Commit

Permalink
Allow stopping in-progress movement
Browse files Browse the repository at this point in the history
  • Loading branch information
abmantis authored and newAM committed Aug 25, 2023
1 parent 78498de commit 5db9c28
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 23 deletions.
81 changes: 58 additions & 23 deletions idasen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ def __init__(
self._logger = _DeskLoggingAdapter(
logger=logging.getLogger(__name__), extra={"mac": self.mac}
)
self._moving = False
self._move_task: Optional[asyncio.Task] = None

async def __aenter__(self):
await self.connect()
Expand Down Expand Up @@ -167,7 +169,7 @@ async def monitor(self, callback: Callable[[float], Awaitable[None]]):

async def output_listener(char: BleakGATTCharacteristic, data: bytearray):
height = _bytes_to_meters(data)
self._logger.info(f"Got data: {height}m")
self._logger.debug(f"Got data: {height}m")

nonlocal previous_height
if abs(height - previous_height) < 0.001:
Expand Down Expand Up @@ -206,6 +208,16 @@ def is_connected(self) -> bool:
"""
return self._client.is_connected

@property
def is_moving(self) -> bool:
"""
Check if the desk is currently being moved by this class.
Returns:
Boolean representing movement status.
"""
return self._moving

@property
def mac(self) -> str:
"""Desk MAC address."""
Expand Down Expand Up @@ -265,31 +277,54 @@ async def move_to_target(self, target: float):
f"{self.MIN_HEIGHT:.3f}"
)

previous_height = await self.get_height()
will_move_up = target > previous_height
while True:
height = await self.get_height()
difference = target - height
self._logger.debug(f"{target=} {height=} {difference=}")
if (height < previous_height and will_move_up) or (
height > previous_height and not will_move_up
):
self._logger.warning(
"stopped moving because desk safety feature kicked in"
)
return
if abs(difference) < 0.005: # tolerance of 0.005 meters
self._logger.info(f"reached target of {target:.3f}")
await self.stop()
return
elif difference > 0:
await self.move_up()
elif difference < 0:
await self.move_down()
previous_height = height
if self._moving:
self._logger.error("Already moving")
return
self._moving = True

async def do_move():
previous_height = await self.get_height()
will_move_up = target > previous_height
while True:
height = await self.get_height()
difference = target - height
self._logger.debug(f"{target=} {height=} {difference=}")
if (height < previous_height and will_move_up) or (
height > previous_height and not will_move_up
):
self._logger.warning(
"stopped moving because desk safety feature kicked in"
)
return
if not self._moving:
return

if abs(difference) < 0.005: # tolerance of 0.005 meters
self._logger.info(f"reached target of {target:.3f}")
await self._stop()
return
elif difference > 0:
await self.move_up()
elif difference < 0:
await self.move_down()
previous_height = height

self._move_task = asyncio.create_task(do_move())
await self._move_task
self._moving = False

async def stop(self):
"""Stop desk movement."""
self._moving = False
if self._move_task:
self._logger.debug("Desk was moving, waiting for it to stop")
await self._move_task

await self._stop()

async def _stop(self):
"""Send stop commands"""
self._logger.debug("Sending stop commands")
await asyncio.gather(
self._client.write_gatt_char(_UUID_COMMAND, _COMMAND_STOP, response=False),
self._client.write_gatt_char(
Expand Down
27 changes: 27 additions & 0 deletions tests/test_idasen.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,33 @@ async def test_move_to_target(desk: IdasenDesk, target: float):
assert abs(await desk.get_height() - target) < 0.005


async def test_move_stop():
desk = IdasenDesk(mac=desk_mac)
client = MockBleakClient()
desk._client = client
client.write_gatt_char = mock.AsyncMock()

async def write_gatt_char_mock(
uuid: str, command: bytearray, response: bool = False
):
if client.write_gatt_char.call_count == 1:
assert desk.is_moving
# Force this method to behave asynchronously, otherwise it will block the
# eventloop
await asyncio.sleep(0.1)

client.write_gatt_char.side_effect = write_gatt_char_mock

async with desk:
move_task = asyncio.create_task(desk.move_to_target(0.7))
# Allow the move_task to start looping
await asyncio.sleep(0.1)

await desk.stop()
assert not desk.is_moving
assert move_task.done()


@pytest.mark.parametrize(
"raw, result",
[
Expand Down

0 comments on commit 5db9c28

Please sign in to comment.