python-selve-new 2.5.4__tar.gz → 2.5.6__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.4 → python_selve_new-2.5.6}/PKG-INFO +1 -1
  2. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/python_selve_new.egg-info/PKG-INFO +1 -1
  3. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/__init__.py +24 -15
  4. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/_version.py +3 -3
  5. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/util/serial_transport.py +1 -0
  6. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_mock_commands.py +3 -3
  7. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/.github/FUNDING.yml +0 -0
  8. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/.github/architect.chatmode.md +0 -0
  9. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/.github/ask.chatmode.md +0 -0
  10. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/.github/code.chatmode.md +0 -0
  11. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/.github/debug.chatmode.md +0 -0
  12. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/.github/workflows/python-publish.yml +0 -0
  13. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/.github/workflows/tests.yml +0 -0
  14. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/.gitignore +0 -0
  15. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/.idea/.gitignore +0 -0
  16. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  17. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/.idea/misc.xml +0 -0
  18. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/.idea/modules.xml +0 -0
  19. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/.idea/python-selve-new.iml +0 -0
  20. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/.idea/vcs.xml +0 -0
  21. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/CHANGELOG.md +0 -0
  22. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/LICENSE +0 -0
  23. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/README.md +0 -0
  24. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/coverage_xdist_example.txt +0 -0
  25. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/debug_response.py +0 -0
  26. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/debug_test.bat +0 -0
  27. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/direct_hardware_test.bat +0 -0
  28. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/direct_hardware_test.py +0 -0
  29. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/generate_coverage.bat +0 -0
  30. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/package.sh +0 -0
  31. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/pyproject.toml +0 -0
  32. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/python_selve_new.egg-info/SOURCES.txt +0 -0
  33. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/python_selve_new.egg-info/dependency_links.txt +0 -0
  34. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/python_selve_new.egg-info/requires.txt +0 -0
  35. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/python_selve_new.egg-info/top_level.txt +0 -0
  36. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/release.py +0 -0
  37. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/run_all_tests.bat +0 -0
  38. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/run_hardware_tests.bat +0 -0
  39. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/run_integration_tests.bat +0 -0
  40. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/run_mock_tests.bat +0 -0
  41. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/run_single_test.bat +0 -0
  42. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/run_tests.bat +0 -0
  43. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/commands/__init__.py +0 -0
  44. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/commands/command.py +0 -0
  45. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/commands/device.py +0 -0
  46. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/commands/event.py +0 -0
  47. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/commands/firmware.py +0 -0
  48. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/commands/group.py +0 -0
  49. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/commands/iveo.py +0 -0
  50. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/commands/param.py +0 -0
  51. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/commands/senSim.py +0 -0
  52. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/commands/sender.py +0 -0
  53. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/commands/sensor.py +0 -0
  54. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/commands/service.py +0 -0
  55. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/device.py +0 -0
  56. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/gateway.py +0 -0
  57. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/group.py +0 -0
  58. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/iveo.py +0 -0
  59. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/senSim.py +0 -0
  60. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/sender.py +0 -0
  61. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/sensor.py +0 -0
  62. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/util/__init__.py +0 -0
  63. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/util/errors.py +0 -0
  64. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/selve/util/protocol.py +0 -0
  65. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/setup.cfg +0 -0
  66. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/setup.py +0 -0
  67. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/setup_and_test.bat +0 -0
  68. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/__init__.py +0 -0
  69. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/conftest.py +0 -0
  70. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/integration/README.md +0 -0
  71. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/integration/__init__.py +0 -0
  72. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/integration/conftest.py +0 -0
  73. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/integration/test_device_integration.py +0 -0
  74. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/integration/test_selve_gateway_integration.py +0 -0
  75. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/integration/test_selve_hardware.py +0 -0
  76. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/integration/test_selve_integration.py +0 -0
  77. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/test_import.py +0 -0
  78. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/test_replacement.py +0 -0
  79. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/__init__.py +0 -0
  80. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/mock_utils.py +0 -0
  81. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_command_coverage.py +0 -0
  82. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_commands.py +0 -0
  83. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_device.py +0 -0
  84. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_device_classes_coverage.py +0 -0
  85. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_device_commands_extended.py +0 -0
  86. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_gateway_configuration_issues.py +0 -0
  87. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_gateway_error_handling_fixed.py +0 -0
  88. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_group.py +0 -0
  89. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_group_commands.py +0 -0
  90. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_missing_components.py +0 -0
  91. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_mock_devices_and_groups.py +0 -0
  92. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_mock_sensors_and_senders.py +0 -0
  93. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_param_commands_extended.py +0 -0
  94. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_port_discovery.py +0 -0
  95. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_selve_advanced_coverage.py +0 -0
  96. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_selve_core_coverage.py +0 -0
  97. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_selve_edge_cases.py +0 -0
  98. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_selve_gateway.py +0 -0
  99. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_selve_init_comprehensive.py +0 -0
  100. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_selve_init_response_coverage.py +0 -0
  101. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_selve_init_simple.py +0 -0
  102. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_selve_main_class_extensive.py +0 -0
  103. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_sender_commands_extended.py +0 -0
  104. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_sensim_commands_extended.py +0 -0
  105. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_sensor_commands_extended.py +0 -0
  106. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_service_command_errors.py +0 -0
  107. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_service_commands.py +0 -0
  108. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/tests/unit/test_util.py +0 -0
  109. {python_selve_new-2.5.4 → python_selve_new-2.5.6}/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.4
3
+ Version: 2.5.6
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-selve-new
3
- Version: 2.5.4
3
+ Version: 2.5.6
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
@@ -256,6 +256,8 @@ class Selve:
256
256
 
257
257
 
258
258
  async def startWorker(self):
259
+ if self._tx_task is not None and not self._tx_task.done():
260
+ return # already running
259
261
  self._LOGGER.debug("Starting worker")
260
262
  self._pauseWorker.clear()
261
263
  self._stopThread.clear()
@@ -773,6 +775,9 @@ class Selve:
773
775
  async def discover(self):
774
776
 
775
777
  await self.stopWorker()
778
+ # Rebuild transport for a clean connection (avoids stale StreamReader state
779
+ # left over from _probe_port, which explains why USB-replug was needed).
780
+ await self._teardown_transport()
776
781
  await self.setEvents(0,0,0,0,0)
777
782
  rdy = await self.gatewayReady()
778
783
  if rdy:
@@ -1094,6 +1099,10 @@ class Selve:
1094
1099
  dev.state = MovementState.UP_ON
1095
1100
  elif response.command is DriveCommandCommeo.STOP:
1096
1101
  dev.state = MovementState.STOPPED_OFF
1102
+ elif response.command is DriveCommandCommeo.DRIVESTEPDOWN:
1103
+ dev.state = MovementState.DOWN_ON
1104
+ elif response.command is DriveCommandCommeo.DRIVESTEPUP:
1105
+ dev.state = MovementState.UP_ON
1097
1106
  self.addOrUpdateDevice(dev, SelveTypes.DEVICE)
1098
1107
  for id in response.failedIds:
1099
1108
  dev = self.getDevice(id, SelveTypes.DEVICE)
@@ -1436,21 +1445,23 @@ class Selve:
1436
1445
  async def _movement_poll_loop(self, device_id: int, interval: float = 0.5, timeout: float = 60.0) -> None:
1437
1446
  """Poll DeviceGetValues every *interval* seconds until movement stops or timeout."""
1438
1447
  elapsed = 0.0
1439
- while not self._stopThread.is_set() and elapsed < timeout:
1440
- await asyncio.sleep(interval)
1441
- elapsed += interval
1442
- dev = self.getDevice(device_id, SelveTypes.DEVICE)
1443
- if dev is None or dev.state == MovementState.STOPPED_OFF:
1444
- break
1445
- await self.updateCommeoDeviceValuesAsync(device_id)
1446
- self._movement_tasks.pop(device_id, None)
1448
+ try:
1449
+ while not self._stopThread.is_set() and elapsed < timeout:
1450
+ await asyncio.sleep(interval)
1451
+ elapsed += interval
1452
+ if self.getDevice(device_id, SelveTypes.DEVICE) is None:
1453
+ break
1454
+ await self.updateCommeoDeviceValuesAsync(device_id)
1455
+ except asyncio.CancelledError:
1456
+ pass
1457
+ finally:
1458
+ self._movement_tasks.pop(device_id, None)
1447
1459
 
1448
1460
  async def moveDeviceUp(self, device: SelveDevice | IveoDevice, type=DeviceCommandType.MANUAL):
1449
1461
  if device.communicationType is CommunicationType.COMMEO:
1450
1462
  await self.executeCommand(CommandDriveUp(device.id, type))
1451
1463
  device.state = MovementState.UP_ON
1452
1464
  self.addOrUpdateDevice(device, SelveTypes.DEVICE)
1453
- await self.updateCommeoDeviceValuesAsync(device.id)
1454
1465
  self._start_movement_polling(device.id)
1455
1466
  else:
1456
1467
  self.setDeviceState(device.id, MovementState.UP_ON, SelveTypes.IVEO)
@@ -1464,7 +1475,6 @@ class Selve:
1464
1475
  await self.executeCommand(CommandDriveDown(device.id, type))
1465
1476
  device.state = MovementState.DOWN_ON
1466
1477
  self.addOrUpdateDevice(device, SelveTypes.DEVICE)
1467
- await self.updateCommeoDeviceValuesAsync(device.id)
1468
1478
  self._start_movement_polling(device.id)
1469
1479
  else:
1470
1480
  self.setDeviceState(device.id, MovementState.DOWN_ON, SelveTypes.IVEO)
@@ -1476,7 +1486,6 @@ class Selve:
1476
1486
  async def moveDevicePos1(self, device: SelveDevice | IveoDevice, type=DeviceCommandType.MANUAL):
1477
1487
  if device.communicationType is CommunicationType.COMMEO:
1478
1488
  await self.executeCommand(CommandDrivePos1(device.id, type))
1479
- await self.updateCommeoDeviceValuesAsync(device.id)
1480
1489
  self._start_movement_polling(device.id)
1481
1490
  else:
1482
1491
  self.setDeviceState(device.id, MovementState.UP_ON, SelveTypes.IVEO)
@@ -1488,7 +1497,6 @@ class Selve:
1488
1497
  async def moveDevicePos2(self, device: SelveDevice | IveoDevice, type=DeviceCommandType.MANUAL):
1489
1498
  if device.communicationType is CommunicationType.COMMEO:
1490
1499
  await self.executeCommand(CommandDrivePos2(device.id, type))
1491
- await self.updateCommeoDeviceValuesAsync(device.id)
1492
1500
  self._start_movement_polling(device.id)
1493
1501
  else:
1494
1502
  self.setDeviceState(device.id, MovementState.DOWN_ON, SelveTypes.IVEO)
@@ -1499,17 +1507,18 @@ class Selve:
1499
1507
 
1500
1508
  async def moveDevicePos(self, device: SelveDevice, pos: int = 0, type=DeviceCommandType.MANUAL):
1501
1509
  await self.executeCommand(CommandDrivePos(device.id, type, param=Util.percentageToValue(pos)))
1502
- await self.updateCommeoDeviceValuesAsync(device.id)
1503
1510
  self._start_movement_polling(device.id)
1504
1511
 
1505
1512
  async def moveDeviceStepUp(self, device: SelveDevice, degrees: int = 0, type=DeviceCommandType.MANUAL):
1506
1513
  await self.executeCommand(CommandDriveStepUp(device.id, type, param=Util.degreesToValue(degrees)))
1507
- await self.updateCommeoDeviceValuesAsync(device.id)
1514
+ device.state = MovementState.UP_ON
1515
+ self.addOrUpdateDevice(device, SelveTypes.DEVICE)
1508
1516
  self._start_movement_polling(device.id)
1509
1517
 
1510
1518
  async def moveDeviceStepDown(self, device: SelveDevice, degrees: int = 0, type=DeviceCommandType.MANUAL):
1511
1519
  await self.executeCommand(CommandDriveStepDown(device.id, type, param=Util.degreesToValue(degrees)))
1512
- await self.updateCommeoDeviceValuesAsync(device.id)
1520
+ device.state = MovementState.DOWN_ON
1521
+ self.addOrUpdateDevice(device, SelveTypes.DEVICE)
1513
1522
  self._start_movement_polling(device.id)
1514
1523
 
1515
1524
  async def stopDevice(self, device: SelveDevice | IveoDevice, type=DeviceCommandType.MANUAL):
@@ -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.4'
22
- __version_tuple__ = version_tuple = (2, 5, 4)
21
+ __version__ = version = '2.5.6'
22
+ __version_tuple__ = version_tuple = (2, 5, 6)
23
23
 
24
- __commit_id__ = commit_id = 'g5d6793713'
24
+ __commit_id__ = commit_id = 'g74b26e3d1'
@@ -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
@@ -72,9 +72,9 @@ class TestMockCommands(unittest.TestCase):
72
72
  # Check that the device state was updated
73
73
  self.assertEqual(self.commeo_device.state, MovementState.UP_ON)
74
74
 
75
- # Check that device values were updated
76
- self.selve.updateCommeoDeviceValuesAsync.assert_called_once_with(1)
77
-
75
+ # updateCommeoDeviceValuesAsync is NOT called immediately; the poll loop handles updates
76
+ self.selve.updateCommeoDeviceValuesAsync.assert_not_called()
77
+
78
78
  self.loop.run_until_complete(test_async())
79
79
 
80
80
  def test_move_device_up_command_exception(self):