conson-xp 1.33.0__py3-none-any.whl → 1.35.0__py3-none-any.whl

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 (32) hide show
  1. {conson_xp-1.33.0.dist-info → conson_xp-1.35.0.dist-info}/METADATA +1 -1
  2. {conson_xp-1.33.0.dist-info → conson_xp-1.35.0.dist-info}/RECORD +32 -32
  3. xp/__init__.py +1 -1
  4. xp/cli/commands/conbus/conbus_actiontable_commands.py +34 -35
  5. xp/cli/commands/conbus/conbus_autoreport_commands.py +11 -10
  6. xp/cli/commands/conbus/conbus_blink_commands.py +20 -6
  7. xp/cli/commands/conbus/conbus_custom_commands.py +6 -4
  8. xp/cli/commands/conbus/conbus_datapoint_commands.py +8 -6
  9. xp/cli/commands/conbus/conbus_lightlevel_commands.py +19 -16
  10. xp/cli/commands/conbus/conbus_linknumber_commands.py +7 -6
  11. xp/cli/commands/conbus/conbus_modulenumber_commands.py +7 -6
  12. xp/cli/commands/conbus/conbus_msactiontable_commands.py +7 -9
  13. xp/cli/commands/conbus/conbus_output_commands.py +9 -7
  14. xp/cli/commands/conbus/conbus_raw_commands.py +7 -2
  15. xp/cli/commands/conbus/conbus_scan_commands.py +4 -2
  16. xp/services/conbus/actiontable/actiontable_download_service.py +79 -37
  17. xp/services/conbus/actiontable/actiontable_list_service.py +17 -17
  18. xp/services/conbus/actiontable/actiontable_upload_service.py +78 -36
  19. xp/services/conbus/actiontable/msactiontable_service.py +88 -48
  20. xp/services/conbus/conbus_blink_all_service.py +89 -35
  21. xp/services/conbus/conbus_blink_service.py +82 -24
  22. xp/services/conbus/conbus_custom_service.py +81 -26
  23. xp/services/conbus/conbus_datapoint_queryall_service.py +90 -43
  24. xp/services/conbus/conbus_datapoint_service.py +76 -28
  25. xp/services/conbus/conbus_output_service.py +82 -22
  26. xp/services/conbus/conbus_raw_service.py +78 -37
  27. xp/services/conbus/conbus_scan_service.py +86 -42
  28. xp/services/conbus/write_config_service.py +76 -26
  29. xp/utils/dependencies.py +12 -24
  30. {conson_xp-1.33.0.dist-info → conson_xp-1.35.0.dist-info}/WHEEL +0 -0
  31. {conson_xp-1.33.0.dist-info → conson_xp-1.35.0.dist-info}/entry_points.txt +0 -0
  32. {conson_xp-1.33.0.dist-info → conson_xp-1.35.0.dist-info}/licenses/LICENSE +0 -0
@@ -6,18 +6,17 @@ and processing ACK/NAK responses.
6
6
 
7
7
  import logging
8
8
  from datetime import datetime
9
- from typing import Callable, Optional
9
+ from typing import Any, Optional
10
10
 
11
- from twisted.internet.posixbase import PosixReactorBase
11
+ from psygnal import Signal
12
12
 
13
- from xp.models import ConbusClientConfig
14
13
  from xp.models.conbus.conbus_output import ConbusOutputResponse
15
14
  from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
16
15
  from xp.models.telegram.action_type import ActionType
17
16
  from xp.models.telegram.output_telegram import OutputTelegram
18
17
  from xp.models.telegram.system_function import SystemFunction
19
18
  from xp.models.telegram.telegram_type import TelegramType
20
- from xp.services.protocol import ConbusProtocol
19
+ from xp.services.protocol.conbus_event_protocol import ConbusEventProtocol
21
20
  from xp.services.telegram.telegram_output_service import (
22
21
  TelegramOutputService,
23
22
  XPOutputError,
@@ -30,33 +29,47 @@ class ConbusOutputError(Exception):
30
29
  pass
31
30
 
32
31
 
33
- class ConbusOutputService(ConbusProtocol):
32
+ class ConbusOutputService:
34
33
  """
35
34
  Service for sending action telegrams to Conbus module outputs.
36
35
 
37
36
  Manages action telegram transmission (ON/OFF) and processes
38
37
  ACK/NAK responses from modules.
38
+
39
+ Attributes:
40
+ conbus_protocol: Protocol instance for Conbus communication.
41
+ on_finish: Signal emitted when operation finishes (with result).
39
42
  """
40
43
 
44
+ conbus_protocol: ConbusEventProtocol
45
+ on_finish: Signal = Signal(ConbusOutputResponse)
46
+
41
47
  def __init__(
42
48
  self,
49
+ conbus_protocol: ConbusEventProtocol,
43
50
  telegram_output_service: TelegramOutputService,
44
- cli_config: ConbusClientConfig,
45
- reactor: PosixReactorBase,
46
51
  ):
47
52
  """Initialize the Conbus output service.
48
53
 
49
54
  Args:
55
+ conbus_protocol: ConbusEventProtocol for communication.
50
56
  telegram_output_service: TelegramOutputService for telegram generation/parsing.
51
- cli_config: Conbus client configuration.
52
- reactor: Twisted reactor for async operations.
53
57
  """
54
- super().__init__(cli_config, reactor)
58
+ self.conbus_protocol = conbus_protocol
55
59
  self.telegram_output_service = telegram_output_service
60
+
61
+ # Connect protocol signals
62
+ self.conbus_protocol.on_connection_made.connect(self.connection_made)
63
+ self.conbus_protocol.on_telegram_sent.connect(self.telegram_sent)
64
+ self.conbus_protocol.on_telegram_received.connect(self.telegram_received)
65
+ self.conbus_protocol.on_timeout.connect(self.timeout)
66
+ self.conbus_protocol.on_failed.connect(self.failed)
67
+
68
+ # Initialize state
56
69
  self.serial_number: str = ""
57
70
  self.output_number: int = 0
58
71
  self.action_type: ActionType = ActionType.ON_RELEASE
59
- self.finish_callback: Optional[Callable[[ConbusOutputResponse], None]] = None
72
+ self.output_state: str = ""
60
73
  self.service_response: ConbusOutputResponse = ConbusOutputResponse(
61
74
  success=False,
62
75
  serial_number=self.serial_number,
@@ -68,7 +81,7 @@ class ConbusOutputService(ConbusProtocol):
68
81
  # Set up logging
69
82
  self.logger = logging.getLogger(__name__)
70
83
 
71
- def connection_established(self) -> None:
84
+ def connection_made(self) -> None:
72
85
  """Handle connection established event."""
73
86
  self.logger.debug(
74
87
  f"Connection established, sending action {self.action_type} to output {self.output_number}."
@@ -84,7 +97,7 @@ class ConbusOutputService(ConbusProtocol):
84
97
 
85
98
  # Send F27D{output:02d}{action} telegram
86
99
  # F27 = ACTION, D = data with output number and action type
87
- self.send_telegram(
100
+ self.conbus_protocol.send_telegram(
88
101
  telegram_type=TelegramType.SYSTEM,
89
102
  serial_number=self.serial_number,
90
103
  system_function=SystemFunction.ACTION,
@@ -148,8 +161,12 @@ class ConbusOutputService(ConbusProtocol):
148
161
  self.service_response.output_number = self.output_number
149
162
  self.service_response.action_type = self.action_type
150
163
  self.service_response.output_telegram = output_telegram
151
- if self.finish_callback:
152
- self.finish_callback(self.service_response)
164
+ self.on_finish.emit(self.service_response)
165
+
166
+ def timeout(self) -> None:
167
+ """Handle timeout event."""
168
+ self.logger.debug("Timeout occurred")
169
+ self.failed("Timeout")
153
170
 
154
171
  def failed(self, message: str) -> None:
155
172
  """Handle failed connection event.
@@ -164,15 +181,13 @@ class ConbusOutputService(ConbusProtocol):
164
181
  self.service_response.output_number = self.output_number
165
182
  self.service_response.action_type = self.action_type
166
183
  self.service_response.error = message
167
- if self.finish_callback:
168
- self.finish_callback(self.service_response)
184
+ self.on_finish.emit(self.service_response)
169
185
 
170
186
  def send_action(
171
187
  self,
172
188
  serial_number: str,
173
189
  output_number: int,
174
190
  action_type: ActionType,
175
- finish_callback: Callable[[ConbusOutputResponse], None],
176
191
  timeout_seconds: Optional[float] = None,
177
192
  ) -> None:
178
193
  """Send an action telegram to a module output.
@@ -181,14 +196,59 @@ class ConbusOutputService(ConbusProtocol):
181
196
  serial_number: 10-digit module serial number.
182
197
  output_number: Output number (0-99).
183
198
  action_type: Action to perform (ON_RELEASE, OFF_PRESS, etc.).
184
- finish_callback: Callback function to call when operation completes.
185
199
  timeout_seconds: Optional timeout in seconds.
186
200
  """
187
201
  self.logger.info("Starting send_action")
188
202
  if timeout_seconds:
189
- self.timeout_seconds = timeout_seconds
203
+ self.conbus_protocol.timeout_seconds = timeout_seconds
190
204
  self.serial_number = serial_number
191
205
  self.output_number = output_number
192
206
  self.action_type = action_type
193
- self.finish_callback = finish_callback
194
- self.start_reactor()
207
+
208
+ def set_timeout(self, timeout_seconds: float) -> None:
209
+ """Set operation timeout.
210
+
211
+ Args:
212
+ timeout_seconds: Timeout in seconds.
213
+ """
214
+ self.conbus_protocol.timeout_seconds = timeout_seconds
215
+
216
+ def start_reactor(self) -> None:
217
+ """Start the reactor."""
218
+ self.conbus_protocol.start_reactor()
219
+
220
+ def stop_reactor(self) -> None:
221
+ """Stop the reactor."""
222
+ self.conbus_protocol.stop_reactor()
223
+
224
+ def __enter__(self) -> "ConbusOutputService":
225
+ """Enter context manager - reset state for singleton reuse.
226
+
227
+ Returns:
228
+ Self for context manager protocol.
229
+ """
230
+ # Reset state for singleton reuse
231
+ self.service_response = ConbusOutputResponse(
232
+ success=False,
233
+ serial_number="",
234
+ output_number=0,
235
+ action_type=ActionType.ON_RELEASE,
236
+ timestamp=datetime.now(),
237
+ )
238
+ self.output_state = ""
239
+ return self
240
+
241
+ def __exit__(
242
+ self, _exc_type: Optional[type], _exc_val: Optional[Exception], _exc_tb: Any
243
+ ) -> None:
244
+ """Exit context manager and disconnect signals."""
245
+ # Disconnect protocol signals
246
+ self.conbus_protocol.on_connection_made.disconnect(self.connection_made)
247
+ self.conbus_protocol.on_telegram_sent.disconnect(self.telegram_sent)
248
+ self.conbus_protocol.on_telegram_received.disconnect(self.telegram_received)
249
+ self.conbus_protocol.on_timeout.disconnect(self.timeout)
250
+ self.conbus_protocol.on_failed.disconnect(self.failed)
251
+ # Disconnect service signals
252
+ self.on_finish.disconnect()
253
+ # Stop reactor
254
+ self.stop_reactor()
@@ -5,49 +5,61 @@ This service handles sending raw telegram strings without prior validation.
5
5
 
6
6
  import logging
7
7
  from datetime import datetime
8
- from typing import Callable, Optional
8
+ from typing import Any, Optional
9
9
 
10
- from twisted.internet.posixbase import PosixReactorBase
10
+ from psygnal import Signal
11
11
 
12
- from xp.models import ConbusClientConfig
13
12
  from xp.models.conbus.conbus_raw import ConbusRawResponse
14
13
  from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
15
- from xp.services.protocol import ConbusProtocol
14
+ from xp.services.protocol.conbus_event_protocol import ConbusEventProtocol
16
15
 
17
16
 
18
- class ConbusRawService(ConbusProtocol):
17
+ class ConbusRawService:
19
18
  """
20
19
  Service for sending raw telegram sequences to Conbus modules.
21
20
 
22
- Uses ConbusProtocol to provide raw telegram functionality
21
+ Uses ConbusEventProtocol to provide raw telegram functionality
23
22
  for sending arbitrary telegram strings without validation.
23
+
24
+ Attributes:
25
+ conbus_protocol: Protocol instance for Conbus communication.
26
+ on_progress: Signal emitted when telegram is received (with frame).
27
+ on_finish: Signal emitted when operation finishes (with result).
24
28
  """
25
29
 
30
+ conbus_protocol: ConbusEventProtocol
31
+ on_progress: Signal = Signal(str)
32
+ on_finish: Signal = Signal(ConbusRawResponse)
33
+
26
34
  def __init__(
27
35
  self,
28
- cli_config: ConbusClientConfig,
29
- reactor: PosixReactorBase,
36
+ conbus_protocol: ConbusEventProtocol,
30
37
  ) -> None:
31
38
  """Initialize the Conbus raw service.
32
39
 
33
40
  Args:
34
- cli_config: Configuration for Conbus client connection.
35
- reactor: Twisted reactor for event loop.
41
+ conbus_protocol: ConbusEventProtocol instance.
36
42
  """
37
- super().__init__(cli_config, reactor)
43
+ self.conbus_protocol = conbus_protocol
44
+
45
+ # Connect protocol signals
46
+ self.conbus_protocol.on_connection_made.connect(self.connection_made)
47
+ self.conbus_protocol.on_telegram_sent.connect(self.telegram_sent)
48
+ self.conbus_protocol.on_telegram_received.connect(self.telegram_received)
49
+ self.conbus_protocol.on_timeout.connect(self.timeout)
50
+ self.conbus_protocol.on_failed.connect(self.failed)
51
+
38
52
  self.raw_input: str = ""
39
- self.progress_callback: Optional[Callable[[str], None]] = None
40
- self.finish_callback: Optional[Callable[[ConbusRawResponse], None]] = None
41
53
  self.service_response: ConbusRawResponse = ConbusRawResponse(
42
54
  success=False,
43
55
  )
44
56
  # Set up logging
45
57
  self.logger = logging.getLogger(__name__)
46
58
 
47
- def connection_established(self) -> None:
59
+ def connection_made(self) -> None:
48
60
  """Handle connection established event."""
49
61
  self.logger.debug(f"Connection established, sending {self.raw_input}")
50
- self.sendFrame(self.raw_input.encode())
62
+ self.conbus_protocol.send_raw_telegram(self.raw_input)
51
63
 
52
64
  def telegram_sent(self, telegram_sent: str) -> None:
53
65
  """Handle telegram sent event.
@@ -71,19 +83,13 @@ class ConbusRawService(ConbusProtocol):
71
83
  self.service_response.received_telegrams = []
72
84
  self.service_response.received_telegrams.append(telegram_received.frame)
73
85
 
74
- if self.progress_callback:
75
- self.progress_callback(telegram_received.frame)
76
-
77
- def timeout(self) -> bool:
78
- """Handle timeout event.
86
+ self.on_progress.emit(telegram_received.frame)
79
87
 
80
- Returns:
81
- False to indicate connection should be closed.
82
- """
83
- self.logger.debug(f"Timeout: {self.timeout_seconds}s")
84
- if self.finish_callback:
85
- self.finish_callback(self.service_response)
86
- return False
88
+ def timeout(self) -> None:
89
+ """Handle timeout event."""
90
+ timeout_seconds = self.conbus_protocol.timeout_seconds
91
+ self.logger.debug(f"Timeout: {timeout_seconds}s")
92
+ self.on_finish.emit(self.service_response)
87
93
 
88
94
  def failed(self, message: str) -> None:
89
95
  """Handle failed connection event.
@@ -95,28 +101,63 @@ class ConbusRawService(ConbusProtocol):
95
101
  self.service_response.success = False
96
102
  self.service_response.timestamp = datetime.now()
97
103
  self.service_response.error = message
98
- if self.finish_callback:
99
- self.finish_callback(self.service_response)
104
+ self.on_finish.emit(self.service_response)
100
105
 
101
106
  def send_raw_telegram(
102
107
  self,
103
108
  raw_input: str,
104
- progress_callback: Callable[[str], None],
105
- finish_callback: Callable[[ConbusRawResponse], None],
106
109
  timeout_seconds: Optional[float] = None,
107
110
  ) -> None:
108
111
  """Send a raw telegram string to the Conbus server.
109
112
 
110
113
  Args:
111
114
  raw_input: Raw telegram string to send.
112
- progress_callback: Callback to handle progress updates.
113
- finish_callback: Callback function to call when the operation is complete.
114
115
  timeout_seconds: Timeout in seconds.
115
116
  """
116
117
  self.logger.info("Starting send_raw_telegram")
117
118
  if timeout_seconds:
118
- self.timeout_seconds = timeout_seconds
119
- self.progress_callback = progress_callback
120
- self.finish_callback = finish_callback
119
+ self.conbus_protocol.timeout_seconds = timeout_seconds
121
120
  self.raw_input = raw_input
122
- self.start_reactor()
121
+
122
+ def set_timeout(self, timeout_seconds: float) -> None:
123
+ """Set operation timeout.
124
+
125
+ Args:
126
+ timeout_seconds: Timeout in seconds.
127
+ """
128
+ self.conbus_protocol.timeout_seconds = timeout_seconds
129
+
130
+ def start_reactor(self) -> None:
131
+ """Start the reactor."""
132
+ self.conbus_protocol.start_reactor()
133
+
134
+ def stop_reactor(self) -> None:
135
+ """Stop the reactor."""
136
+ self.conbus_protocol.stop_reactor()
137
+
138
+ def __enter__(self) -> "ConbusRawService":
139
+ """Enter context manager.
140
+
141
+ Returns:
142
+ Self for context manager protocol.
143
+ """
144
+ # Reset state for singleton reuse
145
+ self.service_response = ConbusRawResponse(success=False)
146
+ self.raw_input = ""
147
+ return self
148
+
149
+ def __exit__(
150
+ self, _exc_type: Optional[type], _exc_val: Optional[Exception], _exc_tb: Any
151
+ ) -> None:
152
+ """Exit context manager and disconnect signals."""
153
+ # Disconnect protocol signals
154
+ self.conbus_protocol.on_connection_made.disconnect(self.connection_made)
155
+ self.conbus_protocol.on_telegram_sent.disconnect(self.telegram_sent)
156
+ self.conbus_protocol.on_telegram_received.disconnect(self.telegram_received)
157
+ self.conbus_protocol.on_timeout.disconnect(self.timeout)
158
+ self.conbus_protocol.on_failed.disconnect(self.failed)
159
+ # Disconnect service signals
160
+ self.on_progress.disconnect()
161
+ self.on_finish.disconnect()
162
+ # Stop reactor
163
+ self.stop_reactor()
@@ -6,43 +6,50 @@ telegrams to scan modules for all datapoints by function code.
6
6
 
7
7
  import logging
8
8
  from datetime import datetime
9
- from typing import Callable, Optional
9
+ from typing import Any, Optional
10
10
 
11
- from twisted.internet.posixbase import PosixReactorBase
11
+ from psygnal import Signal
12
12
 
13
- from xp.models import (
14
- ConbusClientConfig,
15
- ConbusResponse,
16
- )
13
+ from xp.models import ConbusResponse
17
14
  from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
18
- from xp.services.protocol import ConbusProtocol
15
+ from xp.services.protocol.conbus_event_protocol import ConbusEventProtocol
19
16
 
20
17
 
21
- class ConbusScanService(ConbusProtocol):
18
+ class ConbusScanService:
22
19
  """
23
20
  Service for scanning modules for all datapoints by function code.
24
21
 
25
- Uses ConbusProtocol to provide scan functionality for discovering
22
+ Uses ConbusEventProtocol to provide scan functionality for discovering
26
23
  all available datapoints on a module.
24
+
25
+ Attributes:
26
+ conbus_protocol: Protocol instance for Conbus communication.
27
+ on_progress: Signal emitted when scan progress is made (with telegram frame).
28
+ on_finish: Signal emitted when scan finishes (with result).
27
29
  """
28
30
 
31
+ on_progress: Signal = Signal(str)
32
+ on_finish: Signal = Signal(ConbusResponse)
33
+
29
34
  def __init__(
30
35
  self,
31
- cli_config: ConbusClientConfig,
32
- reactor: PosixReactorBase,
36
+ conbus_protocol: ConbusEventProtocol,
33
37
  ) -> None:
34
38
  """Initialize the Conbus scan service.
35
39
 
36
40
  Args:
37
- cli_config: Conbus client configuration.
38
- reactor: Twisted reactor instance.
41
+ conbus_protocol: ConbusEventProtocol instance.
39
42
  """
40
- super().__init__(cli_config, reactor)
43
+ self.conbus_protocol = conbus_protocol
44
+ self.conbus_protocol.on_connection_made.connect(self.connection_made)
45
+ self.conbus_protocol.on_telegram_sent.connect(self.telegram_sent)
46
+ self.conbus_protocol.on_telegram_received.connect(self.telegram_received)
47
+ self.conbus_protocol.on_timeout.connect(self.timeout)
48
+ self.conbus_protocol.on_failed.connect(self.failed)
49
+
41
50
  self.serial_number: str = ""
42
51
  self.function_code: str = ""
43
52
  self.datapoint_value: int = -1
44
- self.progress_callback: Optional[Callable[[str], None]] = None
45
- self.finish_callback: Optional[Callable[[ConbusResponse], None]] = None
46
53
  self.service_response: ConbusResponse = ConbusResponse(
47
54
  success=False,
48
55
  serial_number=self.serial_number,
@@ -53,8 +60,8 @@ class ConbusScanService(ConbusProtocol):
53
60
  # Set up logging
54
61
  self.logger = logging.getLogger(__name__)
55
62
 
56
- def connection_established(self) -> None:
57
- """Handle connection established event."""
63
+ def connection_made(self) -> None:
64
+ """Handle connection made event."""
58
65
  self.logger.debug("Connection established, starting scan")
59
66
  self.scan_next_datacode()
60
67
 
@@ -66,14 +73,13 @@ class ConbusScanService(ConbusProtocol):
66
73
  """
67
74
  self.datapoint_value += 1
68
75
  if self.datapoint_value >= 100:
69
- if self.finish_callback:
70
- self.finish_callback(self.service_response)
76
+ self.on_finish.emit(self.service_response)
71
77
  return False
72
78
 
73
79
  self.logger.debug(f"Scanning next datacode: {self.datapoint_value:02d}")
74
80
  data = f"{self.datapoint_value:02d}"
75
81
  telegram_body = f"S{self.serial_number}F{self.function_code}D{data}"
76
- self.sendFrame(telegram_body.encode())
82
+ self.conbus_protocol.sendFrame(telegram_body.encode())
77
83
  return True
78
84
 
79
85
  def telegram_sent(self, telegram_sent: str) -> None:
@@ -96,18 +102,13 @@ class ConbusScanService(ConbusProtocol):
96
102
  self.service_response.received_telegrams = []
97
103
  self.service_response.received_telegrams.append(telegram_received.frame)
98
104
 
99
- if self.progress_callback:
100
- self.progress_callback(telegram_received.frame)
101
-
102
- def timeout(self) -> bool:
103
- """Handle timeout event by scanning next data code.
105
+ self.on_progress.emit(telegram_received.frame)
104
106
 
105
- Returns:
106
- True to continue scanning, False to stop.
107
- """
108
- self.logger.debug(f"Timeout: {self.timeout_seconds}s")
109
- continue_scan = self.scan_next_datacode()
110
- return continue_scan
107
+ def timeout(self) -> None:
108
+ """Handle timeout event by scanning next data code."""
109
+ timeout_seconds = self.conbus_protocol.timeout_seconds
110
+ self.logger.debug(f"Timeout: {timeout_seconds}s")
111
+ self.scan_next_datacode()
111
112
 
112
113
  def failed(self, message: str) -> None:
113
114
  """Handle failed connection event.
@@ -119,15 +120,12 @@ class ConbusScanService(ConbusProtocol):
119
120
  self.service_response.success = False
120
121
  self.service_response.timestamp = datetime.now()
121
122
  self.service_response.error = message
122
- if self.finish_callback:
123
- self.finish_callback(self.service_response)
123
+ self.on_finish.emit(self.service_response)
124
124
 
125
125
  def scan_module(
126
126
  self,
127
127
  serial_number: str,
128
128
  function_code: str,
129
- progress_callback: Callable[[str], None],
130
- finish_callback: Callable[[ConbusResponse], None],
131
129
  timeout_seconds: float = 0.25,
132
130
  ) -> None:
133
131
  """Scan a module for all datapoints by function code.
@@ -135,16 +133,62 @@ class ConbusScanService(ConbusProtocol):
135
133
  Args:
136
134
  serial_number: 10-digit module serial number.
137
135
  function_code: The function code to scan.
138
- progress_callback: Callback to handle progress.
139
- finish_callback: Callback function to call when the scan is complete.
140
136
  timeout_seconds: Timeout in seconds.
141
137
  """
142
138
  self.logger.info("Starting scan_module")
143
139
  if timeout_seconds:
144
- self.timeout_seconds = timeout_seconds
140
+ self.conbus_protocol.timeout_seconds = timeout_seconds
145
141
 
146
142
  self.serial_number = serial_number
147
143
  self.function_code = function_code
148
- self.progress_callback = progress_callback
149
- self.finish_callback = finish_callback
150
- self.start_reactor()
144
+
145
+ def set_timeout(self, timeout_seconds: float) -> None:
146
+ """Set operation timeout.
147
+
148
+ Args:
149
+ timeout_seconds: Timeout in seconds.
150
+ """
151
+ self.conbus_protocol.timeout_seconds = timeout_seconds
152
+
153
+ def start_reactor(self) -> None:
154
+ """Start the reactor."""
155
+ self.conbus_protocol.start_reactor()
156
+
157
+ def stop_reactor(self) -> None:
158
+ """Stop the reactor."""
159
+ self.conbus_protocol.stop_reactor()
160
+
161
+ def __enter__(self) -> "ConbusScanService":
162
+ """Enter context manager - reset state for singleton reuse.
163
+
164
+ Returns:
165
+ Self for context manager protocol.
166
+ """
167
+ # Reset state for singleton reuse
168
+ self.serial_number = ""
169
+ self.function_code = ""
170
+ self.datapoint_value = -1
171
+ self.service_response = ConbusResponse(
172
+ success=False,
173
+ serial_number="",
174
+ sent_telegrams=[],
175
+ received_telegrams=[],
176
+ timestamp=datetime.now(),
177
+ )
178
+ return self
179
+
180
+ def __exit__(
181
+ self, _exc_type: Optional[type], _exc_val: Optional[Exception], _exc_tb: Any
182
+ ) -> None:
183
+ """Exit context manager and disconnect signals."""
184
+ # Disconnect protocol signals
185
+ self.conbus_protocol.on_connection_made.disconnect(self.connection_made)
186
+ self.conbus_protocol.on_telegram_sent.disconnect(self.telegram_sent)
187
+ self.conbus_protocol.on_telegram_received.disconnect(self.telegram_received)
188
+ self.conbus_protocol.on_timeout.disconnect(self.timeout)
189
+ self.conbus_protocol.on_failed.disconnect(self.failed)
190
+ # Disconnect service signals
191
+ self.on_progress.disconnect()
192
+ self.on_finish.disconnect()
193
+ # Stop reactor
194
+ self.stop_reactor()