conson-xp 1.35.0__py3-none-any.whl → 1.37.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: conson-xp
3
- Version: 1.35.0
3
+ Version: 1.37.0
4
4
  Summary: XP Protocol Communication Tools
5
5
  Author-Email: ldvchosal <ldvchosal@github.com>
6
6
  License: MIT License
@@ -1,15 +1,15 @@
1
- conson_xp-1.35.0.dist-info/METADATA,sha256=-Lw1Rw_5jRvY22Z8moRh9N31dx9EuzV7qQ8oZeW_7Og,11246
2
- conson_xp-1.35.0.dist-info/WHEEL,sha256=tsUv_t7BDeJeRHaSrczbGeuK-TtDpGsWi_JfpzD255I,90
3
- conson_xp-1.35.0.dist-info/entry_points.txt,sha256=1OcdIcDM1hz3ljCXgybaPUh1IOFEwkaTgLIW9u9zGeg,50
4
- conson_xp-1.35.0.dist-info/licenses/LICENSE,sha256=rxj6woMM-r3YCyGq_UHFtbh7kHTAJgrccH6O-33IDE4,1419
5
- xp/__init__.py,sha256=UiuNf-HRLrftHgahYFnTtw0rkyT8zacWREPahcTjY3Q,181
1
+ conson_xp-1.37.0.dist-info/METADATA,sha256=s1RaPxb6bPVKGRVNN0cCZj8_m5iIzBZqb5F6HKGWDmw,11246
2
+ conson_xp-1.37.0.dist-info/WHEEL,sha256=tsUv_t7BDeJeRHaSrczbGeuK-TtDpGsWi_JfpzD255I,90
3
+ conson_xp-1.37.0.dist-info/entry_points.txt,sha256=1OcdIcDM1hz3ljCXgybaPUh1IOFEwkaTgLIW9u9zGeg,50
4
+ conson_xp-1.37.0.dist-info/licenses/LICENSE,sha256=rxj6woMM-r3YCyGq_UHFtbh7kHTAJgrccH6O-33IDE4,1419
5
+ xp/__init__.py,sha256=SkcUe9q0JFuR_IuWncbF3ypd6CM8KKa86qFUeu7ICiQ,181
6
6
  xp/cli/__init__.py,sha256=QjnKB1KaI2aIyKlzrnvCwfbBuUj8HNgwNMvNJVQofbI,81
7
7
  xp/cli/__main__.py,sha256=l2iKwMdat5rTGd3JWs-uGksnYYDDffp_Npz05QdKEeU,117
8
8
  xp/cli/commands/__init__.py,sha256=noh8fdZAWq-ihJEboP8WugbIgq4LJ3jUWMRA7720xWE,4909
9
9
  xp/cli/commands/conbus/__init__.py,sha256=gE3K5OEoXkkZX8UOc2v3nreQQzwkOQi7n0VZ-Z2juXA,495
10
10
  xp/cli/commands/conbus/conbus.py,sha256=eqdY8ArapvD08Z4p7Xk7eh4z0dESHuMSw7PKtwTJRYU,3021
11
- xp/cli/commands/conbus/conbus_actiontable_commands.py,sha256=tqZzKU5-PQ1JbdqcbarUA5nlybpB0Es656jG_5Bz3Wg,7128
12
- xp/cli/commands/conbus/conbus_autoreport_commands.py,sha256=1ltokrvbzoCwwSAKvki-0EuL2M6i-q2E9RSYHwQX2js,3856
11
+ xp/cli/commands/conbus/conbus_actiontable_commands.py,sha256=wIzO6IcdAqqFG_2_nrmabh8roRF8AuAhskhm02Yta_8,7138
12
+ xp/cli/commands/conbus/conbus_autoreport_commands.py,sha256=TgXFVpoyFNiEatL6r78IghzyF0R2S3cgTrGZaiJPjwA,3934
13
13
  xp/cli/commands/conbus/conbus_blink_commands.py,sha256=HRn4Lr_BO7_WynsaUnO_hKezOi3MVhkPYEOnh0rMMlg,5324
14
14
  xp/cli/commands/conbus/conbus_config_commands.py,sha256=BugIbgNX6_s4MySGvI6tWZkwguciajHUX2Xz8XBux7k,716
15
15
  xp/cli/commands/conbus/conbus_custom_commands.py,sha256=nQttCXzmuUVLy5u7mZaRqYXzy8MIHyHRJQBdkDLunYc,1735
@@ -17,11 +17,11 @@ xp/cli/commands/conbus/conbus_datapoint_commands.py,sha256=UZ_wTO0cdxjed0fGGZYyl
17
17
  xp/cli/commands/conbus/conbus_discover_commands.py,sha256=MnTCzvERO5xerfs0fuuIBoo1O9h_0IfoJ6snLGVl0lA,1899
18
18
  xp/cli/commands/conbus/conbus_event_commands.py,sha256=7URf-2u8Kzcy0chLYShbZfCbKawf--i-8U88AjhxleQ,3177
19
19
  xp/cli/commands/conbus/conbus_export_commands.py,sha256=s3jgg3Wqi1P6rYujpE_9aPA47S4UBQfrZPTr9vzH-UA,2951
20
- xp/cli/commands/conbus/conbus_lightlevel_commands.py,sha256=7KeR9YVW2u6DfuEXlrkrv65Q5cfgFnSwJQjK1TuQrLs,6973
21
- xp/cli/commands/conbus/conbus_linknumber_commands.py,sha256=cd7kvG6Ikf_VqqSxv9enksckd3onUOwziVS9Mr6Yf6k,3465
22
- xp/cli/commands/conbus/conbus_modulenumber_commands.py,sha256=Z9deoalANi0fkqBUosFvkuD-dnSP5vvi9VPdy0osyDs,3537
20
+ xp/cli/commands/conbus/conbus_lightlevel_commands.py,sha256=AQhVneN5_rH6wd7D4KW80XIMh9MGjJv85gN57S206j4,7036
21
+ xp/cli/commands/conbus/conbus_linknumber_commands.py,sha256=hVr1g6nDTa4MezW8joHPjPuZcMp2ttd9PfZaT9sQED4,3528
22
+ xp/cli/commands/conbus/conbus_modulenumber_commands.py,sha256=FjFWnLU_aUoAXQ2tKKLC-ichNiIb90_9OWpSdIUyHvc,3600
23
23
  xp/cli/commands/conbus/conbus_msactiontable_commands.py,sha256=rNLWuC19Nj8CxGJNQRCnwl-mEAipz1iufBcIbCytkGM,2847
24
- xp/cli/commands/conbus/conbus_output_commands.py,sha256=k4DqraUWCbWY6cfSk8WF2WBDz6Dc4ABU1TPq1fCZnRk,5160
24
+ xp/cli/commands/conbus/conbus_output_commands.py,sha256=rJx8pfsl_ZeCNXhEelsY7mfYnaj_DHdz4TC-e8d5QGs,5286
25
25
  xp/cli/commands/conbus/conbus_raw_commands.py,sha256=892-S6wxp5xNPz6K86Le8KtQXNO4a0PQv20Hzx3vhiA,1996
26
26
  xp/cli/commands/conbus/conbus_receive_commands.py,sha256=_PsC-3xidmJBuOWUS60iDzhSHYYn5ZFmORXap-ljVGM,1902
27
27
  xp/cli/commands/conbus/conbus_scan_commands.py,sha256=L7YHNnDUWxT8UHkQoNgyKa6twoUrE126XBkT1G76xK0,1805
@@ -92,7 +92,7 @@ xp/models/protocol/conbus_protocol.py,sha256=3uWYE_t_-mp_2wPEgbDHbZoeQSEv48IdRcQ
92
92
  xp/models/response.py,sha256=h6_B1k_v6IrWhgNWBohEGQGRCp5TcVhgQ3RJS8gTkhY,1230
93
93
  xp/models/telegram/__init__.py,sha256=-_exhjlRLbBNuPxHC4tLA2SAgf8M0yHJMeyEoQIk9PI,53
94
94
  xp/models/telegram/action_type.py,sha256=vkw_chTgmsadksGXvZ9D_qYGpjOwCw-OgbEi8Bml17g,783
95
- xp/models/telegram/datapoint_type.py,sha256=clmgqCsTNKuHmWN6ol2Hwj_71I10f36Oq-S5D5ZA9a8,2942
95
+ xp/models/telegram/datapoint_type.py,sha256=7aV-oUkqDiurT3b9mRF6M6yc5wu71AT3SXlgl6b9CDI,4067
96
96
  xp/models/telegram/event_telegram.py,sha256=FCCfyZXQEUPB6Uo1m7B9nvFCJ0Ipv2CApmImAZo_Xa4,4689
97
97
  xp/models/telegram/event_type.py,sha256=VZhaDpey7KYWnmwN-gstj-r4Vd5hiGdzQuRazUdixB8,333
98
98
  xp/models/telegram/input_action_type.py,sha256=EDYtE4uxByUyGsZTkXxwN9rQxCAejWk08_kv-7COurM,1852
@@ -126,12 +126,12 @@ xp/services/conbus/actiontable/actiontable_download_service.py,sha256=1sk_STBdMf
126
126
  xp/services/conbus/actiontable/actiontable_list_service.py,sha256=PH8B-5j1ON_Q50INylYhrVDE1gG7vNE1nMyy-2DJfqA,2808
127
127
  xp/services/conbus/actiontable/actiontable_show_service.py,sha256=jqNZ4UvZPHH66OYuryjnU1Km-a83OCwYvK0vc56oL8I,3017
128
128
  xp/services/conbus/actiontable/actiontable_upload_service.py,sha256=kXV99ReAYcMZt0icPU04lX9ZWWkiiYcxzsty0pnfzwE,9707
129
- xp/services/conbus/actiontable/msactiontable_service.py,sha256=W44Uy2m2vM_yL57gwyyz59j9zRu5dyAG1g8PJcT7vE8,9777
129
+ xp/services/conbus/actiontable/msactiontable_service.py,sha256=-kZebnqOCunneSny-lI0FOyrPelyLtboQWXJBe2czGE,9743
130
130
  xp/services/conbus/conbus_blink_all_service.py,sha256=6XsqtgHUgPDPWG0Mx2W2gnG_1eiaHrt2DiPXGqGHS50,8472
131
131
  xp/services/conbus/conbus_blink_service.py,sha256=wFCUbHYInbzfE4Ks_qjkav0FhtHXsxM9IY6tD5r0oAk,7898
132
132
  xp/services/conbus/conbus_custom_service.py,sha256=uPsBkoVGr8I5JwpHiCM8Xf2DUYy6e34cQHmGSheY98Y,7766
133
133
  xp/services/conbus/conbus_datapoint_queryall_service.py,sha256=K8a_aO5rXuOx9ZHTrN1rpQ6KIOAkhYx-HsqDf0HIo-E,8399
134
- xp/services/conbus/conbus_datapoint_service.py,sha256=MjUHHkFgFD5Ex5sESamylPzoHfYRmIOZWnTIdI0AXjY,8037
134
+ xp/services/conbus/conbus_datapoint_service.py,sha256=MFyYv2TkP355S5phKLtfj05b5AtfwgQyCvklkR7AKT8,8026
135
135
  xp/services/conbus/conbus_discover_service.py,sha256=ZwjYBlgP6FgpHBJk7pcKr4JHfH7WUHDxe4he4F_HblQ,12740
136
136
  xp/services/conbus/conbus_event_list_service.py,sha256=0xyXXNU44epN5bFkU6oiZMyhxfUguul3evqClvPJDcA,3618
137
137
  xp/services/conbus/conbus_event_raw_service.py,sha256=FZFu-LNLInrTKTpiGLyootozvyIF5Si5FMrxNk2ALD0,7000
@@ -158,7 +158,7 @@ xp/services/homekit/homekit_service.py,sha256=0lW-hg40ETco3gDBEYkR_sX-UIYsLSKCD4
158
158
  xp/services/log_file_service.py,sha256=fvPcZQj8XOKUA-4ep5R8n0PelvwvRlTLlVxvIWM5KR4,10506
159
159
  xp/services/module_type_service.py,sha256=xWhr1EAZMykL5uNWHWdpa5T8yNruGKH43XRTOS8GwZg,7477
160
160
  xp/services/protocol/__init__.py,sha256=qRufBmqRKGzpuzZ5bxBbmwf510TT00Ke8s5HcWGnqRY,818
161
- xp/services/protocol/conbus_event_protocol.py,sha256=7u_Gv7vM53Ikzo56D5Vua4y8_0vsl6nUjfRJmrzgUhk,14936
161
+ xp/services/protocol/conbus_event_protocol.py,sha256=PpSr5sWcibN_xb2iMWAtWc0dTkUkFZelgYuPiejutkE,15155
162
162
  xp/services/protocol/conbus_protocol.py,sha256=JO7yLkD_ohPT0ETjnAIx4CGpZyobf4LdbuozM_43btE,10276
163
163
  xp/services/protocol/protocol_factory.py,sha256=PmjN9AtW9sxNo3voqUiNgQA-pTvX1RW4XXFlHKfFr5Q,2470
164
164
  xp/services/protocol/telegram_protocol.py,sha256=Ki5DrXsKxiaqLcdP9WWUuhUI7cPu2DfwyZkh-Gv9Lb8,9496
@@ -185,7 +185,7 @@ xp/services/telegram/telegram_service.py,sha256=XrP1CPi0ckxoKBaNwLA6lo-TogWxXgmX
185
185
  xp/services/telegram/telegram_version_service.py,sha256=M5HdOTsLdcwo122FP-jW6R740ktLrtKf2TiMDVz23h8,10528
186
186
  xp/services/term/__init__.py,sha256=BIeOK042bMR-0l6MA80wdW5VuHlpWOXtRER9IG5ilQA,245
187
187
  xp/services/term/protocol_monitor_service.py,sha256=PhEzLNzWf1XieQw94ua-hJu9ccwrAzhdxSZGe4kHghs,9945
188
- xp/services/term/state_monitor_service.py,sha256=PgwCH8nce1RODV33aJefiX3on-pSGEgP_4FDAoU5Trc,16218
188
+ xp/services/term/state_monitor_service.py,sha256=PYKiPO4Zijc-DApRECTIChnbdsOFT3Ndc1MsBglekts,16961
189
189
  xp/term/__init__.py,sha256=Xg2DhBeI3xQJLfc7_BPWI1por-rUXemyer5OtOt9Cus,51
190
190
  xp/term/protocol.py,sha256=oLJAExvIaOSpy75A5TaYB_7R9skTTtNtPx8hiJLdy_U,3425
191
191
  xp/term/protocol.tcss,sha256=r_KfxrbpycGHLVXqZc6INBBcUJME0hLrAZkF1oqnab4,2126
@@ -193,7 +193,7 @@ xp/term/state.py,sha256=sR7I6t4gJSkO2YS3TwonAnGPR_f43coCk4xKdWETus0,3233
193
193
  xp/term/state.tcss,sha256=Njp7fc16cCunLq7hi5RvXjPi4jSCGi5aPDnusb9dq1Y,1401
194
194
  xp/term/widgets/__init__.py,sha256=ftWmN_fmjxy2E8Qfm-YSRmzKfgL0KTBCTpgvYWCPbUY,274
195
195
  xp/term/widgets/help_menu.py,sha256=w2NjwiC_s16St0rigZ9ef9S0V9Y4v0J5eCVCHAdRKF4,1789
196
- xp/term/widgets/modules_list.py,sha256=DD0PUnY4gv05hkCVxThWULHg1ZNNY8xr1XFaZrv9kS4,7666
196
+ xp/term/widgets/modules_list.py,sha256=cZlrHhse9onSZGBeJe9tuByWCk-928bsmUBGNeXiF2I,7745
197
197
  xp/term/widgets/protocol_log.py,sha256=CJUpckWj7GC1kVqixDadteyGnI4aHyzd4kkH-pSbzno,2600
198
198
  xp/term/widgets/status_footer.py,sha256=bxrcqKzJ9V0aPSn_WwraVpJz7NxBUh3yIjA3fwv5nVA,3256
199
199
  xp/utils/__init__.py,sha256=_avMF_UOkfR3tNeDIPqQ5odmbq5raKkaq1rZ9Cn1CJs,332
@@ -204,4 +204,4 @@ xp/utils/logging.py,sha256=rZDXwlBrYK8A6MPq5StsMNpgsRowzJXM6fvROPwJdGM,3750
204
204
  xp/utils/serialization.py,sha256=RWHHk86feaB4ZP7rjE4qOWK0900yg2joUBDkP76gfOY,4618
205
205
  xp/utils/state_machine.py,sha256=Oe2sLwCh9z_vr1tF6X0ZRGTeuckRQAGzmef7xc9CNdc,2413
206
206
  xp/utils/time_utils.py,sha256=dEyViDlAG9GWU-J3D_YVa-sGma6yiyyMTgN4h2x3PY4,3781
207
- conson_xp-1.35.0.dist-info/RECORD,,
207
+ conson_xp-1.37.0.dist-info/RECORD,,
xp/__init__.py CHANGED
@@ -3,7 +3,7 @@
3
3
  conson-xp package.
4
4
  """
5
5
 
6
- __version__ = "1.35.0"
6
+ __version__ = "1.37.0"
7
7
  __manufacturer__ = "salchichon"
8
8
  __model__ = "xp.cli"
9
9
  __serial__ = "2025.09.23.000"
@@ -57,7 +57,7 @@ def conbus_download_actiontable(ctx: Context, serial_number: str) -> None:
57
57
  Args:
58
58
  progress: Progress message string.
59
59
  """
60
- click.echo(progress)
60
+ click.echo(progress, nl=False)
61
61
 
62
62
  def on_finish(result: tuple) -> None:
63
63
  """Handle successful completion of action table download.
@@ -57,8 +57,11 @@ def get_autoreport_command(ctx: Context, serial_number: str) -> None:
57
57
  with service:
58
58
  service.on_finish.connect(on_finish)
59
59
  service.query_datapoint(
60
- serial_number=serial_number, datapoint_type=DataPointType.AUTO_REPORT_STATUS
60
+ serial_number=serial_number,
61
+ datapoint_type=DataPointType.AUTO_REPORT_STATUS,
62
+ timeout_seconds=1.0,
61
63
  )
64
+ service.start_reactor()
62
65
 
63
66
 
64
67
  @conbus_autoreport.command("set", short_help="Set auto report status for a module")
@@ -104,6 +107,6 @@ def set_autoreport_command(ctx: Context, serial_number: str, status: str) -> Non
104
107
  serial_number=serial_number,
105
108
  datapoint_type=DataPointType.AUTO_REPORT_STATUS,
106
109
  data_value=data_value,
107
- timeout_seconds=0.5,
110
+ timeout_seconds=1.0,
108
111
  )
109
112
  service.start_reactor()
@@ -200,6 +200,7 @@ def xp_lightlevel_get(
200
200
  result["output_number"] = output_number
201
201
  result["lightlevel_level"] = lightlevel_level
202
202
  click.echo(json.dumps(result, indent=2))
203
+ service.stop_reactor()
203
204
 
204
205
  with service:
205
206
  service.on_finish.connect(on_finish)
@@ -208,3 +209,4 @@ def xp_lightlevel_get(
208
209
  datapoint_type=DataPointType.MODULE_LIGHT_LEVEL,
209
210
  timeout_seconds=0.5,
210
211
  )
212
+ service.start_reactor()
@@ -93,6 +93,7 @@ def get_linknumber_command(ctx: click.Context, serial_number: str) -> None:
93
93
  result = service_response.to_dict()
94
94
  result["linknumber_value"] = linknumber_value
95
95
  click.echo(json.dumps(result, indent=2))
96
+ service.stop_reactor()
96
97
 
97
98
  with service:
98
99
  service.on_finish.connect(on_finish)
@@ -101,3 +102,4 @@ def get_linknumber_command(ctx: click.Context, serial_number: str) -> None:
101
102
  datapoint_type=DataPointType.LINK_NUMBER,
102
103
  timeout_seconds=0.5,
103
104
  )
105
+ service.start_reactor()
@@ -95,6 +95,7 @@ def get_modulenumber_command(ctx: click.Context, serial_number: str) -> None:
95
95
  result = service_response.to_dict()
96
96
  result["modulenumber_value"] = modulenumber_value
97
97
  click.echo(json.dumps(result, indent=2))
98
+ service.stop_reactor()
98
99
 
99
100
  with service:
100
101
  service.on_finish.connect(on_finish)
@@ -103,3 +104,4 @@ def get_modulenumber_command(ctx: click.Context, serial_number: str) -> None:
103
104
  datapoint_type=DataPointType.MODULE_NUMBER,
104
105
  timeout_seconds=0.5,
105
106
  )
107
+ service.start_reactor()
@@ -121,6 +121,7 @@ def xp_output_status(ctx: click.Context, serial_number: str) -> None:
121
121
  response: Datapoint response object.
122
122
  """
123
123
  click.echo(json.dumps(response.to_dict(), indent=2))
124
+ service.stop_reactor()
124
125
 
125
126
  with service:
126
127
  service.on_finish.connect(on_finish)
@@ -128,6 +129,7 @@ def xp_output_status(ctx: click.Context, serial_number: str) -> None:
128
129
  serial_number=serial_number,
129
130
  datapoint_type=DataPointType.MODULE_OUTPUT_STATE,
130
131
  )
132
+ service.start_reactor()
131
133
 
132
134
 
133
135
  @conbus_output.command("state")
@@ -156,6 +158,7 @@ def xp_module_state(ctx: click.Context, serial_number: str) -> None:
156
158
  response: Datapoint response object.
157
159
  """
158
160
  click.echo(json.dumps(response.to_dict(), indent=2))
161
+ service.stop_reactor()
159
162
 
160
163
  with service:
161
164
  service.on_finish.connect(on_finish)
@@ -163,3 +166,4 @@ def xp_module_state(ctx: click.Context, serial_number: str) -> None:
163
166
  serial_number=serial_number,
164
167
  datapoint_type=DataPointType.MODULE_STATE,
165
168
  )
169
+ service.start_reactor()
@@ -30,6 +30,36 @@ class DataPointType(str, Enum):
30
30
  SW_TOP_VERSION: Software top version.
31
31
  VOLTAGE: Voltage data point.
32
32
  AUTO_REPORT_STATUS: Auto report status.
33
+
34
+ Sample telegram datapoint request:
35
+ <S0020044966F02D30FL>
36
+
37
+ Sample telegram datapoint reply:
38
+ <R0020044966F02D00XP24FH>
39
+ <R0020044966F02D01XP24FG>
40
+ <R0020044966F02D02XP24_V0.34.03GI>
41
+ <R0020044966F02D030020044966FB>
42
+ <R0020044966F02D0406FL>
43
+ <R0020044966F02D050007FL>
44
+ <R0020044966F02D0601FO>
45
+ <R0020044966F02D0707FJ>
46
+ <R0020044966F02D08MIFF>
47
+ <R0020044966F02D09OFFBP>
48
+ <R0020044966F02D1000FI>
49
+ <R0020044966F02D11xxxx0000FJ>
50
+ <R0020044966F02D12xxxx0000FK>
51
+ <R0020044966F02D1500:000[%],01:000[%],02:000[%],03:000[%]HB>
52
+ <R0020044966F02D1600:000[H],01:000[H],02:000[H],03:000[H]HC>
53
+ <R0020044966F02D1700:00000[NA],01:00000[NA],02:00000[NA],03:00000[NA]HD>
54
+ <R0020044966F02D18+28,5§CIM>
55
+ <R0020044966F02D19??FB>
56
+ <R0020044966F02D20??FL>
57
+ <R0020044966F02D21PPFK>
58
+ <R0020044966F02D22PLLKMDINEM>
59
+ <R0020044966F02D23AAFI>
60
+ <R0020044966F02D24AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFP>
61
+ <R0020044966F02D25AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFO>
62
+ <R0020044966F02D26??FN>
33
63
  """
34
64
 
35
65
  MODULE_TYPE = "00" # Module type (XP24, XP33, ..)
@@ -182,7 +182,6 @@ class MsActionTableService:
182
182
  """
183
183
  self.logger.debug(f"Failed: {message}")
184
184
  self.on_error.emit(message)
185
- self.on_finish.emit(None)
186
185
 
187
186
  def succeed(
188
187
  self,
@@ -165,7 +165,7 @@ class ConbusDatapointService:
165
165
  self,
166
166
  serial_number: str,
167
167
  datapoint_type: DataPointType,
168
- timeout_seconds: Optional[float] = None,
168
+ timeout_seconds: float = 1.0,
169
169
  ) -> None:
170
170
  """Query a specific datapoint from a module.
171
171
 
@@ -320,9 +320,13 @@ class ConbusEventProtocol(protocol.Protocol, protocol.ClientFactory):
320
320
 
321
321
  def stop_reactor(self) -> None:
322
322
  """Stop the reactor if it's running."""
323
- if self._reactor.running:
324
- self.logger.info("Stopping reactor")
325
- self._reactor.stop()
323
+ try:
324
+ if self._reactor.running:
325
+ self.logger.info("Stopping reactor")
326
+ self._reactor.stop()
327
+ except Exception as e:
328
+ # Reactor might have already stopped or not been started via run()
329
+ self.logger.debug(f"Reactor stop failed (likely already stopped): {e}")
326
330
 
327
331
  def connect(self) -> None:
328
332
  """Connect to TCP server.
@@ -321,7 +321,7 @@ class StateMonitorService:
321
321
 
322
322
  Args:
323
323
  module_state: Module state to update.
324
- output_number: Output number (0-3 for XP24).
324
+ output_number: Output number (0-3 for XP24, 0-2 for XP33 modules).
325
325
  output_state: True for ON, False for OFF.
326
326
  """
327
327
  # Parse existing outputs string "0 1 0 0" → [0, 1, 0, 0]
@@ -340,8 +340,9 @@ class StateMonitorService:
340
340
  def _handle_event_telegram(self, event: TelegramReceivedEvent) -> None:
341
341
  """Handle event telegram for output state changes.
342
342
 
343
- Processes XP24 output event telegrams to update module state in real-time.
344
- Output events use input_number field with values 80-83 to represent outputs 0-3.
343
+ Processes XP24 and XP33 output event telegrams to update module state in real-time.
344
+ - XP24 output events use input_number 80-83 to represent outputs 0-3.
345
+ - XP33 output events use input_number 0-2 to represent channels 0-2.
345
346
 
346
347
  Args:
347
348
  event: Telegram received event containing event telegram.
@@ -352,17 +353,37 @@ class StateMonitorService:
352
353
  self.logger.debug("Failed to parse event telegram")
353
354
  return
354
355
 
355
- # Only process XP24 output events
356
- if event_telegram.module_type != ModuleTypeCode.XP24.value:
357
- self.logger.debug(
358
- f"Ignoring event from module type {event_telegram.module_type}"
359
- )
360
- return
356
+ # Determine output number based on module type
357
+ output_number = None
358
+
359
+ if event_telegram.module_type == ModuleTypeCode.XP24.value:
360
+ # XP24 uses input_number 80-83 for outputs 0-3
361
+ if 80 <= event_telegram.input_number <= 83:
362
+ output_number = event_telegram.input_number - 80
363
+ else:
364
+ self.logger.debug(
365
+ f"Ignoring XP24 input event I{event_telegram.input_number:02d}"
366
+ )
367
+ return
361
368
 
362
- # Validate output number range (80-83 for XP24 outputs 0-3)
363
- if not (80 <= event_telegram.input_number <= 83):
369
+ elif event_telegram.module_type in (
370
+ ModuleTypeCode.XP33.value,
371
+ ModuleTypeCode.XP33LR.value,
372
+ ModuleTypeCode.XP33LED.value,
373
+ ):
374
+ # XP33 modules use input_number 0-2 for channels 0-2
375
+ if 80 <= event_telegram.input_number <= 82:
376
+ output_number = event_telegram.input_number - 80
377
+ else:
378
+ self.logger.debug(
379
+ f"Ignoring XP33 input event I{event_telegram.input_number:02d}"
380
+ )
381
+ return
382
+
383
+ else:
384
+ # Ignore events from other module types
364
385
  self.logger.debug(
365
- f"Ignoring input event I{event_telegram.input_number:02d}"
386
+ f"Ignoring event from module type {event_telegram.module_type}"
366
387
  )
367
388
  return
368
389
 
@@ -374,9 +395,8 @@ class StateMonitorService:
374
395
  )
375
396
  return
376
397
 
377
- # Convert input_number to output_number (80→0, 81→1, 82→2, 83→3)
378
- output_number = event_telegram.input_number - 80
379
- output_state = event_telegram.is_button_press # M=True, B=False
398
+ # Determine output state (M=True/ON, B=False/OFF)
399
+ output_state = event_telegram.is_button_press
380
400
 
381
401
  # Update output state
382
402
  self._update_output_bit(module_state, output_number, output_state)
@@ -111,7 +111,9 @@ class ModulesListWidget(Static):
111
111
  # Update existing row
112
112
  row_key = self._row_keys[serial_number]
113
113
  self.table.update_cell(
114
- row_key, "outputs", self._format_outputs(module_state.outputs)
114
+ row_key,
115
+ "outputs",
116
+ Text(self._format_outputs(module_state.outputs), justify="right"),
115
117
  )
116
118
  self.table.update_cell(
117
119
  row_key,
@@ -144,7 +146,7 @@ class ModulesListWidget(Static):
144
146
  Text(str(module_state.link_number), justify="right"),
145
147
  module_state.serial_number,
146
148
  module_state.module_type,
147
- self._format_outputs(module_state.outputs),
149
+ Text(self._format_outputs(module_state.outputs), justify="right"),
148
150
  Text(self._format_report(module_state.auto_report), justify="center"),
149
151
  module_state.error_status,
150
152
  Text(self._format_last_update(module_state.last_update), justify="center"),