python-selve-new 2.5.3__tar.gz → 2.5.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/PKG-INFO +1 -1
  2. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/pyproject.toml +4 -1
  3. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/python_selve_new.egg-info/PKG-INFO +1 -1
  4. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/__init__.py +49 -1
  5. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/_version.py +3 -3
  6. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/util/serial_transport.py +1 -0
  7. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/.github/FUNDING.yml +0 -0
  8. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/.github/architect.chatmode.md +0 -0
  9. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/.github/ask.chatmode.md +0 -0
  10. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/.github/code.chatmode.md +0 -0
  11. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/.github/debug.chatmode.md +0 -0
  12. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/.github/workflows/python-publish.yml +0 -0
  13. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/.github/workflows/tests.yml +0 -0
  14. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/.gitignore +0 -0
  15. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/.idea/.gitignore +0 -0
  16. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  17. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/.idea/misc.xml +0 -0
  18. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/.idea/modules.xml +0 -0
  19. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/.idea/python-selve-new.iml +0 -0
  20. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/.idea/vcs.xml +0 -0
  21. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/CHANGELOG.md +0 -0
  22. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/LICENSE +0 -0
  23. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/README.md +0 -0
  24. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/coverage_xdist_example.txt +0 -0
  25. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/debug_response.py +0 -0
  26. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/debug_test.bat +0 -0
  27. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/direct_hardware_test.bat +0 -0
  28. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/direct_hardware_test.py +0 -0
  29. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/generate_coverage.bat +0 -0
  30. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/package.sh +0 -0
  31. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/python_selve_new.egg-info/SOURCES.txt +0 -0
  32. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/python_selve_new.egg-info/dependency_links.txt +0 -0
  33. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/python_selve_new.egg-info/requires.txt +0 -0
  34. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/python_selve_new.egg-info/top_level.txt +0 -0
  35. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/release.py +0 -0
  36. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/run_all_tests.bat +0 -0
  37. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/run_hardware_tests.bat +0 -0
  38. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/run_integration_tests.bat +0 -0
  39. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/run_mock_tests.bat +0 -0
  40. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/run_single_test.bat +0 -0
  41. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/run_tests.bat +0 -0
  42. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/commands/__init__.py +0 -0
  43. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/commands/command.py +0 -0
  44. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/commands/device.py +0 -0
  45. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/commands/event.py +0 -0
  46. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/commands/firmware.py +0 -0
  47. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/commands/group.py +0 -0
  48. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/commands/iveo.py +0 -0
  49. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/commands/param.py +0 -0
  50. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/commands/senSim.py +0 -0
  51. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/commands/sender.py +0 -0
  52. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/commands/sensor.py +0 -0
  53. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/commands/service.py +0 -0
  54. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/device.py +0 -0
  55. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/gateway.py +0 -0
  56. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/group.py +0 -0
  57. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/iveo.py +0 -0
  58. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/senSim.py +0 -0
  59. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/sender.py +0 -0
  60. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/sensor.py +0 -0
  61. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/util/__init__.py +0 -0
  62. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/util/errors.py +0 -0
  63. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/selve/util/protocol.py +0 -0
  64. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/setup.cfg +0 -0
  65. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/setup.py +0 -0
  66. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/setup_and_test.bat +0 -0
  67. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/__init__.py +0 -0
  68. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/conftest.py +0 -0
  69. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/integration/README.md +0 -0
  70. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/integration/__init__.py +0 -0
  71. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/integration/conftest.py +0 -0
  72. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/integration/test_device_integration.py +0 -0
  73. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/integration/test_selve_gateway_integration.py +0 -0
  74. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/integration/test_selve_hardware.py +0 -0
  75. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/integration/test_selve_integration.py +0 -0
  76. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/test_import.py +0 -0
  77. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/test_replacement.py +0 -0
  78. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/__init__.py +0 -0
  79. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/mock_utils.py +0 -0
  80. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_command_coverage.py +0 -0
  81. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_commands.py +0 -0
  82. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_device.py +0 -0
  83. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_device_classes_coverage.py +0 -0
  84. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_device_commands_extended.py +0 -0
  85. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_gateway_configuration_issues.py +0 -0
  86. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_gateway_error_handling_fixed.py +0 -0
  87. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_group.py +0 -0
  88. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_group_commands.py +0 -0
  89. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_missing_components.py +0 -0
  90. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_mock_commands.py +0 -0
  91. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_mock_devices_and_groups.py +0 -0
  92. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_mock_sensors_and_senders.py +0 -0
  93. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_param_commands_extended.py +0 -0
  94. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_port_discovery.py +0 -0
  95. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_selve_advanced_coverage.py +0 -0
  96. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_selve_core_coverage.py +0 -0
  97. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_selve_edge_cases.py +0 -0
  98. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_selve_gateway.py +0 -0
  99. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_selve_init_comprehensive.py +0 -0
  100. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_selve_init_response_coverage.py +0 -0
  101. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_selve_init_simple.py +0 -0
  102. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_selve_main_class_extensive.py +0 -0
  103. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_sender_commands_extended.py +0 -0
  104. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_sensim_commands_extended.py +0 -0
  105. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_sensor_commands_extended.py +0 -0
  106. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_service_command_errors.py +0 -0
  107. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_service_commands.py +0 -0
  108. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_util.py +0 -0
  109. {python_selve_new-2.5.3 → python_selve_new-2.5.5}/tests/unit/test_utility_coverage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-selve-new
3
- Version: 2.5.3
3
+ Version: 2.5.5
4
4
  Summary: Python library for interfacing with selve devices using the USB-RF controller. Written completely new.
5
5
  Home-page: https://github.com/Kannix2005/python-selve-new
6
6
  Author: Stefan Altheimer
@@ -59,4 +59,7 @@ markers = [
59
59
  "unit: marks tests as unit tests",
60
60
  ]
61
61
  asyncio_mode = "auto"
62
- asyncio_default_fixture_loop_scope = "function"
62
+ asyncio_default_fixture_loop_scope = "function"
63
+ filterwarnings = [
64
+ "ignore:coroutine 'Selve._movement_poll_loop' was never awaited:RuntimeWarning",
65
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-selve-new
3
- Version: 2.5.3
3
+ Version: 2.5.5
4
4
  Summary: Python library for interfacing with selve devices using the USB-RF controller. Written completely new.
5
5
  Home-page: https://github.com/Kannix2005/python-selve-new
6
6
  Author: Stefan Altheimer
@@ -51,7 +51,7 @@ from selve.sensor import SelveSensor
51
51
  from selve.util import *
52
52
  from selve.util import Command
53
53
  from selve.util.errors import *
54
- from selve.util.protocol import ParameterType
54
+ from selve.util.protocol import ParameterType, SelveTypes, MovementState
55
55
  from selve.util.serial_transport import SerialTransport
56
56
 
57
57
 
@@ -106,6 +106,9 @@ class Selve:
106
106
  self._pending_futures = deque()
107
107
  self._event_queue = None
108
108
 
109
+ # Active movement polling tasks keyed by device id
110
+ self._movement_tasks: dict = {}
111
+
109
112
  #Options
110
113
  self.reversedStopPosition = 0
111
114
 
@@ -253,6 +256,8 @@ class Selve:
253
256
 
254
257
 
255
258
  async def startWorker(self):
259
+ if self._tx_task is not None and not self._tx_task.done():
260
+ return # already running
256
261
  self._LOGGER.debug("Starting worker")
257
262
  self._pauseWorker.clear()
258
263
  self._stopThread.clear()
@@ -1010,6 +1015,8 @@ class Selve:
1010
1015
  device.device_type = response.deviceType
1011
1016
 
1012
1017
  self.addOrUpdateDevice(device, SelveTypes.DEVICE)
1018
+ if device.state == MovementState.STOPPED_OFF:
1019
+ self._stop_movement_polling(device.id)
1013
1020
 
1014
1021
  if isinstance(response, SensorEventResponse):
1015
1022
  if self.is_id_registered(response.id, SelveTypes.SENSOR):
@@ -1387,6 +1394,8 @@ class Selve:
1387
1394
  dev.freezingAlarm = response.freezingAlarm if response.freezingAlarm else False
1388
1395
  dev.dayMode = response.dayMode if response.dayMode else False
1389
1396
  self.addOrUpdateDevice(dev, SelveTypes.DEVICE)
1397
+ if dev is not None and dev.state == MovementState.STOPPED_OFF:
1398
+ self._stop_movement_polling(id)
1390
1399
 
1391
1400
  def setDeviceValue(self, id: int, value: int, type: SelveTypes):
1392
1401
  dev = self.getDevice(id, type)
@@ -1410,12 +1419,44 @@ class Selve:
1410
1419
  dev.state = state
1411
1420
  self.addOrUpdateDevice(dev, type)
1412
1421
 
1422
+ def _start_movement_polling(self, device_id: int) -> None:
1423
+ """Start a background task that polls device values every 0.5 s during movement."""
1424
+ self._stop_movement_polling(device_id)
1425
+ try:
1426
+ asyncio.get_running_loop()
1427
+ except RuntimeError:
1428
+ return # no running event loop (unit tests, etc.)
1429
+ task = asyncio.create_task(self._movement_poll_loop(device_id))
1430
+ self._movement_tasks[device_id] = task
1431
+
1432
+ def _stop_movement_polling(self, device_id: int) -> None:
1433
+ """Cancel movement polling task for a device if one is active."""
1434
+ task = self._movement_tasks.pop(device_id, None)
1435
+ if task and not task.done():
1436
+ task.cancel()
1437
+
1438
+ async def _movement_poll_loop(self, device_id: int, interval: float = 0.5, timeout: float = 60.0) -> None:
1439
+ """Poll DeviceGetValues every *interval* seconds until movement stops or timeout."""
1440
+ elapsed = 0.0
1441
+ try:
1442
+ while not self._stopThread.is_set() and elapsed < timeout:
1443
+ await asyncio.sleep(interval)
1444
+ elapsed += interval
1445
+ if self.getDevice(device_id, SelveTypes.DEVICE) is None:
1446
+ break
1447
+ await self.updateCommeoDeviceValuesAsync(device_id)
1448
+ except asyncio.CancelledError:
1449
+ pass
1450
+ finally:
1451
+ self._movement_tasks.pop(device_id, None)
1452
+
1413
1453
  async def moveDeviceUp(self, device: SelveDevice | IveoDevice, type=DeviceCommandType.MANUAL):
1414
1454
  if device.communicationType is CommunicationType.COMMEO:
1415
1455
  await self.executeCommand(CommandDriveUp(device.id, type))
1416
1456
  device.state = MovementState.UP_ON
1417
1457
  self.addOrUpdateDevice(device, SelveTypes.DEVICE)
1418
1458
  await self.updateCommeoDeviceValuesAsync(device.id)
1459
+ self._start_movement_polling(device.id)
1419
1460
  else:
1420
1461
  self.setDeviceState(device.id, MovementState.UP_ON, SelveTypes.IVEO)
1421
1462
  await self.executeCommand(IveoManual(device.id, DriveCommandIveo.UP))
@@ -1429,6 +1470,7 @@ class Selve:
1429
1470
  device.state = MovementState.DOWN_ON
1430
1471
  self.addOrUpdateDevice(device, SelveTypes.DEVICE)
1431
1472
  await self.updateCommeoDeviceValuesAsync(device.id)
1473
+ self._start_movement_polling(device.id)
1432
1474
  else:
1433
1475
  self.setDeviceState(device.id, MovementState.DOWN_ON, SelveTypes.IVEO)
1434
1476
  await self.executeCommand(IveoManual(device.id, DriveCommandIveo.DOWN))
@@ -1440,6 +1482,7 @@ class Selve:
1440
1482
  if device.communicationType is CommunicationType.COMMEO:
1441
1483
  await self.executeCommand(CommandDrivePos1(device.id, type))
1442
1484
  await self.updateCommeoDeviceValuesAsync(device.id)
1485
+ self._start_movement_polling(device.id)
1443
1486
  else:
1444
1487
  self.setDeviceState(device.id, MovementState.UP_ON, SelveTypes.IVEO)
1445
1488
  await self.executeCommand(IveoManual(device.id, DriveCommandIveo.POS1))
@@ -1451,6 +1494,7 @@ class Selve:
1451
1494
  if device.communicationType is CommunicationType.COMMEO:
1452
1495
  await self.executeCommand(CommandDrivePos2(device.id, type))
1453
1496
  await self.updateCommeoDeviceValuesAsync(device.id)
1497
+ self._start_movement_polling(device.id)
1454
1498
  else:
1455
1499
  self.setDeviceState(device.id, MovementState.DOWN_ON, SelveTypes.IVEO)
1456
1500
  await self.executeCommand(IveoManual(device.id, DriveCommandIveo.POS2))
@@ -1461,17 +1505,21 @@ class Selve:
1461
1505
  async def moveDevicePos(self, device: SelveDevice, pos: int = 0, type=DeviceCommandType.MANUAL):
1462
1506
  await self.executeCommand(CommandDrivePos(device.id, type, param=Util.percentageToValue(pos)))
1463
1507
  await self.updateCommeoDeviceValuesAsync(device.id)
1508
+ self._start_movement_polling(device.id)
1464
1509
 
1465
1510
  async def moveDeviceStepUp(self, device: SelveDevice, degrees: int = 0, type=DeviceCommandType.MANUAL):
1466
1511
  await self.executeCommand(CommandDriveStepUp(device.id, type, param=Util.degreesToValue(degrees)))
1467
1512
  await self.updateCommeoDeviceValuesAsync(device.id)
1513
+ self._start_movement_polling(device.id)
1468
1514
 
1469
1515
  async def moveDeviceStepDown(self, device: SelveDevice, degrees: int = 0, type=DeviceCommandType.MANUAL):
1470
1516
  await self.executeCommand(CommandDriveStepDown(device.id, type, param=Util.degreesToValue(degrees)))
1471
1517
  await self.updateCommeoDeviceValuesAsync(device.id)
1518
+ self._start_movement_polling(device.id)
1472
1519
 
1473
1520
  async def stopDevice(self, device: SelveDevice | IveoDevice, type=DeviceCommandType.MANUAL):
1474
1521
  if device.communicationType is CommunicationType.COMMEO:
1522
+ self._stop_movement_polling(device.id)
1475
1523
  await self.executeCommand(CommandStop(device.id, type))
1476
1524
  await self.updateCommeoDeviceValuesAsync(device.id)
1477
1525
  else:
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '2.5.3'
22
- __version_tuple__ = version_tuple = (2, 5, 3)
21
+ __version__ = version = '2.5.5'
22
+ __version_tuple__ = version_tuple = (2, 5, 5)
23
23
 
24
- __commit_id__ = commit_id = 'ge708968d7'
24
+ __commit_id__ = commit_id = 'g289213896'
@@ -101,6 +101,7 @@ class SerialTransport:
101
101
  decoded = line_bytes.decode(errors="ignore").strip()
102
102
  if decoded == "":
103
103
  if buffer and self._rx_queue:
104
+ self._logger.debug("Serial RX: %s", buffer)
104
105
  await self._rx_queue.put(buffer)
105
106
  buffer = ""
106
107
  continue