python-selve-new 2.2.15__tar.gz → 2.2.17__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 (46) hide show
  1. {python_selve_new-2.2.15/python_selve_new.egg-info → python_selve_new-2.2.17}/PKG-INFO +1 -1
  2. {python_selve_new-2.2.15 → python_selve_new-2.2.17/python_selve_new.egg-info}/PKG-INFO +1 -1
  3. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/__init__.py +179 -17
  4. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/setup.py +1 -1
  5. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/test.py +11 -6
  6. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/.github/FUNDING.yml +0 -0
  7. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/.github/workflows/python-publish.yml +0 -0
  8. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/.gitignore +0 -0
  9. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/.idea/.gitignore +0 -0
  10. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  11. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/.idea/misc.xml +0 -0
  12. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/.idea/modules.xml +0 -0
  13. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/.idea/python-selve-new.iml +0 -0
  14. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/.idea/vcs.xml +0 -0
  15. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/.vscode/settings.json +0 -0
  16. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/LICENSE +0 -0
  17. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/README.md +0 -0
  18. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/package.sh +0 -0
  19. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/pyproject.toml +0 -0
  20. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/python_selve_new.egg-info/SOURCES.txt +0 -0
  21. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/python_selve_new.egg-info/dependency_links.txt +0 -0
  22. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/python_selve_new.egg-info/entry_points.txt +0 -0
  23. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/python_selve_new.egg-info/requires.txt +0 -0
  24. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/python_selve_new.egg-info/top_level.txt +0 -0
  25. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/commands/__init__.py +0 -0
  26. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/commands/command.py +0 -0
  27. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/commands/device.py +0 -0
  28. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/commands/event.py +0 -0
  29. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/commands/group.py +0 -0
  30. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/commands/iveo.py +0 -0
  31. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/commands/param.py +0 -0
  32. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/commands/senSim.py +0 -0
  33. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/commands/sender.py +0 -0
  34. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/commands/sensor.py +0 -0
  35. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/commands/service.py +0 -0
  36. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/device.py +0 -0
  37. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/gateway.py +0 -0
  38. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/group.py +0 -0
  39. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/iveo.py +0 -0
  40. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/senSim.py +0 -0
  41. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/sender.py +0 -0
  42. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/sensor.py +0 -0
  43. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/util/__init__.py +0 -0
  44. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/util/errors.py +0 -0
  45. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/selve/util/protocol.py +0 -0
  46. {python_selve_new-2.2.15 → python_selve_new-2.2.17}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-selve-new
3
- Version: 2.2.15
3
+ Version: 2.2.17
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.1
2
2
  Name: python-selve-new
3
- Version: 2.2.15
3
+ Version: 2.2.17
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
@@ -9,6 +9,7 @@ from typing import Callable
9
9
 
10
10
  import serial
11
11
  from serial.tools import list_ports
12
+ from serial import SerialException
12
13
  import untangle
13
14
 
14
15
  from selve.commands import param, service
@@ -42,6 +43,7 @@ class Selve:
42
43
  def __init__(self, port=None, discover=True, develop=False, logger=None):
43
44
  # Gateway state
44
45
  self._callbacks = set()
46
+ self._eventCallbacks = set()
45
47
  self.lastLogEvent = None
46
48
  self.state = None
47
49
 
@@ -74,9 +76,14 @@ class Selve:
74
76
  self._writeLock = asyncio.Lock()
75
77
  self._readLock = asyncio.Lock()
76
78
 
79
+ # Trasmit and Recieve Queue init
77
80
  self.txQ = None
78
81
  self.rxQ = None
79
82
 
83
+ #Options
84
+ self.reversedStopPosition = 0
85
+
86
+ #Logger
80
87
  self._LOGGER = logger
81
88
 
82
89
 
@@ -84,8 +91,9 @@ class Selve:
84
91
  # Infinite loop to collect all incoming data
85
92
  self._LOGGER.debug("(Selve Worker): " + "Worker started")
86
93
 
87
- try:
88
- while True:
94
+
95
+ while True:
96
+ try:
89
97
  if not self._pauseWorker.is_set():
90
98
  if not self._serial.is_open:
91
99
  self._serial.open()
@@ -98,6 +106,7 @@ class Selve:
98
106
  async with self._writeLock:
99
107
  async with self._readLock:
100
108
  if self._serial.in_waiting > 0:
109
+ self._LOGGER.debug(f'(Selve Worker): Recieved Serial Data')
101
110
  msg = ""
102
111
  while True:
103
112
  response = self._serial.readline().strip()
@@ -113,13 +122,22 @@ class Selve:
113
122
  if self._stopThread.is_set():
114
123
  self._LOGGER.debug("(Selve Worker): " + 'Exiting worker loop...')
115
124
  break
125
+
116
126
  await asyncio.sleep(0.1)
117
- return True
118
- # serial port exceptions, all of these notify that we are in some
119
- # serious trouble
120
- except serial.SerialException:
121
- # log message
122
- self._LOGGER.error("(Selve Worker): " + 'Serial Port RX error')
127
+
128
+ except serial.SerialException:
129
+ # log message
130
+ self._LOGGER.error("(Selve Worker): " + 'Serial Port RX error')
131
+ self._LOGGER.error("(Selve Worker): " + 'trying to reconnect...')
132
+ await self.recover()
133
+
134
+ #await asyncio.sleep(0.1)
135
+ return True
136
+ # serial port exceptions, all of these notify that we are in some
137
+ # serious trouble
138
+
139
+
140
+
123
141
 
124
142
 
125
143
  async def setup(self, discover=False, fromConfigFlow=False):
@@ -194,6 +212,66 @@ class Selve:
194
212
  self._LOGGER.error("No gateway on comports found!")
195
213
  raise PortError
196
214
 
215
+ async def recover(self):
216
+ self._LOGGER.info("(Selve Worker): " + "Recover serial connection")
217
+ self._LOGGER.debug("(Selve Worker): " + "Waiting 5 seconds before trying...")
218
+ await asyncio.sleep(5)
219
+ self._LOGGER.debug("(Selve Worker): " + "Recovering")
220
+
221
+ if self._port is not None:
222
+ try:
223
+ self._serial = serial.Serial(
224
+ port=self._port,
225
+ baudrate=115200,
226
+ bytesize=serial.EIGHTBITS,
227
+ parity=serial.PARITY_NONE,
228
+ stopbits=serial.STOPBITS_ONE,
229
+ xonxoff=False,
230
+ rtscts=False,
231
+ dsrdtr=False)
232
+
233
+ if await self.pingGatewayFromWorker():
234
+ return
235
+ except serial.SerialException as e:
236
+ self._LOGGER.debug("(Selve Worker): " + "Configured port not valid, maybe it has changed, trying other ports...")
237
+ except Exception as e:
238
+ self._LOGGER.error("(Selve Worker): " + "Unknown exception: " + str(e))
239
+
240
+
241
+ available_ports = list_ports.comports()
242
+ self._LOGGER.debug("(Selve Worker): " + "available comports: " + str(available_ports))
243
+
244
+ if len(available_ports) == 0:
245
+ self._LOGGER.error("(Selve Worker): " + "No available comports!")
246
+ return False
247
+
248
+ for p in available_ports:
249
+ try:
250
+ self._serial = serial.Serial(
251
+ port=p.device,
252
+ baudrate=115200,
253
+ bytesize=serial.EIGHTBITS,
254
+ parity=serial.PARITY_NONE,
255
+ stopbits=serial.STOPBITS_ONE,
256
+ xonxoff=False,
257
+ rtscts=False,
258
+ dsrdtr=False)
259
+ except Exception as e:
260
+ self._LOGGER.error("(Selve Worker): " + "Error at com port: " + str(e))
261
+ try:
262
+ self._serial.close()
263
+ except:
264
+ self._LOGGER.debug("(Selve Worker): " + "Cannot close com port")
265
+ pass
266
+ if await self.pingGatewayFromWorker():
267
+ self._port = p.device
268
+ return
269
+ else:
270
+ self._serial.close()
271
+ self._serial = None
272
+ else:
273
+ self._LOGGER.error("(Selve Worker): " + "No gateway on comports found!")
274
+ raise PortError
197
275
 
198
276
 
199
277
  async def startWorker(self):
@@ -246,6 +324,19 @@ class Selve:
246
324
  """Remove previously registered callback."""
247
325
  self._callbacks.discard(callback)
248
326
 
327
+ def register_event_callback(self, callback: Callable[[], None]) -> None:
328
+ """Register callback, called when other events take place."""
329
+ self._eventCallbacks.add(callback)
330
+
331
+ def remove_event_callback(self, callback: Callable[[], None]) -> None:
332
+ """Remove previously registered callback."""
333
+ self._eventCallbacks.discard(callback)
334
+
335
+
336
+ def updateOptions(self, reversedStopPosition = 0):
337
+ self.reversedStopPosition = reversedStopPosition
338
+
339
+
249
340
  async def _sendCommandToGateway(self, command: Command):
250
341
  commandstr = command.serializeToXML()
251
342
  self._LOGGER.debug('Gateway writing: ' + str(commandstr))
@@ -256,8 +347,25 @@ class Selve:
256
347
  self._serial.flush()
257
348
  #always sleep after writing
258
349
  await asyncio.sleep(0.5)
350
+
351
+ except SerialException as se:
352
+ self._LOGGER.info('Serial error, trying to reconnect once... ' + str(se))
353
+ await self.recover()
354
+
355
+ try:
356
+ self._LOGGER.debug('Trying again...')
357
+ if not self._serial.is_open:
358
+ self._serial.open()
359
+ self._serial.write(commandstr)
360
+ self._serial.flush()
361
+ #always sleep after writing
362
+ await asyncio.sleep(0.5)
363
+
364
+ except Exception as e:
365
+ self._LOGGER.error("error communicating: " + str(e) + " ; Please restart the integration!")
366
+
259
367
  except Exception as e:
260
- self._LOGGER.error("error communicating: " + str(e))
368
+ self._LOGGER.error("error communicating: " + str(e) + " ; Please restart the integration!")
261
369
 
262
370
  async def processResponse(self, xmlstr):
263
371
  """Processes an XML String into a response object. Returns False if something went wrong or the gateway returned an error."""
@@ -627,8 +735,18 @@ class Selve:
627
735
  self.addOrUpdateDevice(device, SelveTypes.DEVICE)
628
736
  config: DeviceGetValuesResponse = await self.executeCommandSyncWithResponse(DeviceGetValues(i))
629
737
  device.state = config.movementState
630
- device.value = config.value
631
- device.targetValue = config.targetValue
738
+
739
+ if self.reversedStopPosition is 0:
740
+ device.value = config.value if config.value else 0
741
+ else:
742
+ device.value = 100 - config.value if config.value else 0
743
+
744
+
745
+ if self.reversedStopPosition is 0:
746
+ device.targetValue = config.targetValue if config.targetValue else 0
747
+ else:
748
+ device.targetValue = 100 - config.targetValue if config.targetValue else 0
749
+
632
750
  device.unreachable = config.unreachable
633
751
  device.overload = config.overload
634
752
  device.obstructed = config.obstructed
@@ -798,8 +916,18 @@ class Selve:
798
916
  self._LOGGER.error("Id not found, creating")
799
917
 
800
918
  device.state = response.actorState
801
- device.value = response.value
802
- device.targetValue = response.targetValue
919
+
920
+ if self.reversedStopPosition is 0:
921
+ device.value = response.value if response.value else 0
922
+ else:
923
+ device.value = 100 - response.value if response.value else 0
924
+
925
+
926
+ if self.reversedStopPosition is 0:
927
+ device.targetValue = response.targetValue if response.targetValue else 0
928
+ else:
929
+ device.targetValue = 100 - response.targetValue if response.targetValue else 0
930
+
803
931
  device.unreachable = response.unreachable
804
932
  device.overload = response.overload
805
933
  device.obstructed = response.obstructed
@@ -845,6 +973,8 @@ class Selve:
845
973
  sender.lastEvent = response.event
846
974
  sender.name = response.senderName
847
975
  self.addOrUpdateDevice(sender, SelveTypes.SENSOR)
976
+ for callback in self._eventCallbacks:
977
+ callback(response)
848
978
 
849
979
  if isinstance(response, LogEventResponse):
850
980
  self.lastLogEvent = response
@@ -861,6 +991,9 @@ class Selve:
861
991
  if isinstance(response, DutyCycleResponse):
862
992
  self.sendingBlocked = response.mode
863
993
  self.utilization = response.traffic
994
+
995
+ for callback in self._eventCallbacks:
996
+ callback(response)
864
997
 
865
998
 
866
999
  def commandResult(self, response: IveoResultResponse | CommandResultResponse):
@@ -897,6 +1030,19 @@ class Selve:
897
1030
  self._LOGGER.debug("No ping")
898
1031
  return False
899
1032
 
1033
+ async def pingGatewayFromWorker(self, fromConfigFlow=False):
1034
+ cmd = ServicePing()
1035
+ methodResponse = await self.executeCommandSyncWithResponsefromWorker(cmd)
1036
+ try:
1037
+ if hasattr(methodResponse, "name"):
1038
+ if methodResponse.name == "selve.GW.service.ping":
1039
+ self._LOGGER.debug("Ping back")
1040
+ return True
1041
+ except:
1042
+ self._LOGGER.debug("Error in ping")
1043
+ self._LOGGER.debug("No ping")
1044
+ return False
1045
+
900
1046
 
901
1047
  async def gatewayState(self):
902
1048
  cmd = ServiceGetState()
@@ -1099,8 +1245,17 @@ class Selve:
1099
1245
  dev = self.getDevice(id, SelveTypes.DEVICE)
1100
1246
  dev.name = response.name if response.name else "None"
1101
1247
  dev.state = response.movementState if response.movementState else MovementState.UNKOWN.value
1102
- dev.value = response.value if response.value else 0
1103
- dev.targetValue = response.targetValue if response.targetValue else 0
1248
+ if self.reversedStopPosition is 0:
1249
+ dev.value = response.value if response.value else 0
1250
+ else:
1251
+ dev.value = 100 - response.value if response.value else 0
1252
+
1253
+
1254
+ if self.reversedStopPosition is 0:
1255
+ dev.targetValue = response.targetValue if response.targetValue else 0
1256
+ else:
1257
+ dev.targetValue = 100 - response.targetValue if response.targetValue else 0
1258
+
1104
1259
  dev.unreachable = response.unreachable if response.unreachable else True
1105
1260
  dev.overload = response.overload if response.overload else False
1106
1261
  dev.obstructed = response.obstructed if response.obstructed else False
@@ -1116,12 +1271,19 @@ class Selve:
1116
1271
 
1117
1272
  def setDeviceValue(self, id: int, value: int, type: SelveTypes):
1118
1273
  dev = self.getDevice(id, type)
1119
- dev.value = value
1274
+ if self.reversedStopPosition is 0:
1275
+ dev.value = value
1276
+ else:
1277
+ dev.value = 100 - value
1278
+
1120
1279
  self.addOrUpdateDevice(dev, type)
1121
1280
 
1122
1281
  def setDeviceTargetValue(self, id: int, value: int, type: SelveTypes):
1123
1282
  dev = self.getDevice(id, type)
1124
- dev.targetValue = value
1283
+ if self.reversedStopPosition is 0:
1284
+ dev.targetValue = value
1285
+ else:
1286
+ dev.targetValue = 100 - value
1125
1287
  self.addOrUpdateDevice(dev, type)
1126
1288
 
1127
1289
  def setDeviceState(self, id: int, state: MovementState, type: SelveTypes):
@@ -15,7 +15,7 @@ with open(path.join(here, 'README.md'), encoding='utf-8') as f:
15
15
  setup(
16
16
 
17
17
  name='python-selve-new', # Required
18
- version='2.2.15', # Required
18
+ version='2.2.17', # Required
19
19
  description='Python library for interfacing with selve devices using the USB-RF controller. Written completely new.', # Required
20
20
  long_description=long_description, # Optional
21
21
  long_description_content_type="text/markdown",
@@ -8,7 +8,7 @@ import selve
8
8
 
9
9
  ## In HA this would be loop = self.hass.loop
10
10
  loop = asyncio.new_event_loop()
11
- portname = 'COM4'
11
+ portname = None
12
12
 
13
13
  logger = logging.getLogger("Logger")
14
14
  logger.setLevel(logging.DEBUG)
@@ -19,23 +19,28 @@ handler.setFormatter(formatter)
19
19
  logger.addHandler(handler)
20
20
 
21
21
 
22
- selve = selve.Selve(portname, loop, develop=False, discover=False, logger=logger)
22
+ selve = selve.Selve(portname, develop=True, discover=False, logger=logger)
23
+ loop.run_until_complete(selve.setup())
23
24
 
25
+ loop.run_until_complete(selve.discover())
24
26
 
27
+ loop.run_until_complete(selve.setEvents(1,1,1,1,1))
28
+ loop.run_until_complete(selve.getEvents())
25
29
 
26
30
  #try:
27
31
  # loop.run_until_complete()
28
32
  #except KeyboardInterrupt:
29
33
  # loop.close()
30
34
 
31
- threading.Thread(target=loop.run_forever, args=())
35
+ #threading.Thread(target=loop.run_forever, args=())
32
36
 
33
- selve.pingGateway()
37
+ #loop.run_until_complete(selve.pingGateway())
34
38
  #selve.resetGateway()
35
- selve.discover()
39
+ #loop.run_until_complete(selve.discover())
36
40
 
41
+ #print("Test")
37
42
 
38
43
  #selve.moveDeviceDown(selve.devices['device'][1])
39
- selve.moveGroupUp(selve.devices['group'][1])
44
+ #selve.moveGroupUp(selve.devices['group'][1])
40
45
 
41
46
  loop.run_forever()