conson-xp 2.0.0__py3-none-any.whl → 2.0.2__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.
- {conson_xp-2.0.0.dist-info → conson_xp-2.0.2.dist-info}/METADATA +1 -1
- {conson_xp-2.0.0.dist-info → conson_xp-2.0.2.dist-info}/RECORD +14 -14
- xp/__init__.py +1 -1
- xp/cli/main.py +23 -4
- xp/models/conbus/conbus_client_config.py +2 -0
- xp/models/homekit/homekit_config.py +8 -0
- xp/services/actiontable/actiontable_serializer.py +4 -4
- xp/services/conbus/actiontable/actiontable_upload_service.py +8 -1
- xp/services/protocol/conbus_event_protocol.py +1 -1
- xp/services/term/homekit_service.py +105 -8
- xp/term/homekit.py +23 -0
- {conson_xp-2.0.0.dist-info → conson_xp-2.0.2.dist-info}/WHEEL +0 -0
- {conson_xp-2.0.0.dist-info → conson_xp-2.0.2.dist-info}/entry_points.txt +0 -0
- {conson_xp-2.0.0.dist-info → conson_xp-2.0.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
conson_xp-2.0.
|
|
2
|
-
conson_xp-2.0.
|
|
3
|
-
conson_xp-2.0.
|
|
4
|
-
conson_xp-2.0.
|
|
5
|
-
xp/__init__.py,sha256=
|
|
1
|
+
conson_xp-2.0.2.dist-info/METADATA,sha256=9UzUpaHd8pegRLDFIPEzaMHr2L3ziBK7QhA2EYqSJ28,11319
|
|
2
|
+
conson_xp-2.0.2.dist-info/WHEEL,sha256=tsUv_t7BDeJeRHaSrczbGeuK-TtDpGsWi_JfpzD255I,90
|
|
3
|
+
conson_xp-2.0.2.dist-info/entry_points.txt,sha256=1OcdIcDM1hz3ljCXgybaPUh1IOFEwkaTgLIW9u9zGeg,50
|
|
4
|
+
conson_xp-2.0.2.dist-info/licenses/LICENSE,sha256=rxj6woMM-r3YCyGq_UHFtbh7kHTAJgrccH6O-33IDE4,1419
|
|
5
|
+
xp/__init__.py,sha256=qQYrRExAxcFFDY2HISDUiyYNIaz1ywu0tBMS6dAtJ9I,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=9TGP3uTxAU-s-kVChvQ-Fn3-HVYj-QpeQ05Is-20HRo,4788
|
|
@@ -41,7 +41,7 @@ xp/cli/commands/telegram/telegram_version_commands.py,sha256=hAMjSAa7zfMNfNFln63
|
|
|
41
41
|
xp/cli/commands/term/__init__.py,sha256=1NNST_8YJfj5LCujQISwQflK6LyEn7mDmZpMpvI9d-o,116
|
|
42
42
|
xp/cli/commands/term/term.py,sha256=gjvsv2OE-F_KNWQrWi04fXQ5cGo0l8P-Ortbb5KTA-A,309
|
|
43
43
|
xp/cli/commands/term/term_commands.py,sha256=pw-hEddLf89_NKft3JPsZALe-7vtvNeq9upubysqLX8,1799
|
|
44
|
-
xp/cli/main.py,sha256=
|
|
44
|
+
xp/cli/main.py,sha256=eyy-Y-3AE9w-LwEDY9oBr9qh25g7EogjBre3dLjr7po,2491
|
|
45
45
|
xp/cli/utils/__init__.py,sha256=gTGIj60Uai0iE7sr9_TtEpl04fD7krtTzbbigXUsUVU,46
|
|
46
46
|
xp/cli/utils/click_tree.py,sha256=sr4l9RWTCnASkdvkJKnRRxWSQPlF1DbFdBNu9gL7Ekc,1693
|
|
47
47
|
xp/cli/utils/datapoint_type_choice.py,sha256=fGgwZAmY_jwBt4fc1LQfyqhvf-TBOUgL3ph9idIDvAI,1680
|
|
@@ -62,7 +62,7 @@ xp/models/conbus/__init__.py,sha256=VIusMWQdBtlwDgj7oSj06wQkklihTp4oWFShvP_JUgA,
|
|
|
62
62
|
xp/models/conbus/conbus.py,sha256=-otqoa_dCDpgEdeAdV66AnSSaXbfPfyHrmV3zpvxGSA,2717
|
|
63
63
|
xp/models/conbus/conbus_autoreport.py,sha256=oTgOstjl_PDASL2n4fXVT1vvcX2nPzve_cBDtwq-wHA,2303
|
|
64
64
|
xp/models/conbus/conbus_blink.py,sha256=DKupiA0-7fh_Dx3DRuKEkS6cEyqM7tq_eMXqKG7Awl4,2925
|
|
65
|
-
xp/models/conbus/conbus_client_config.py,sha256=
|
|
65
|
+
xp/models/conbus/conbus_client_config.py,sha256=o23wJju72kk72gVkgtAGT5XhJbBgoKw4pI5AicKxyR0,1632
|
|
66
66
|
xp/models/conbus/conbus_connection_status.py,sha256=bpIZSGGM47MIDxHxO26bMvDHT1W83rxmcyWgJbmL1lM,1111
|
|
67
67
|
xp/models/conbus/conbus_custom.py,sha256=AoVMT-JYej_zIdZcZC251Ww0JNmAtQobCif2H5kUf00,1979
|
|
68
68
|
xp/models/conbus/conbus_datapoint.py,sha256=4vxQxT8FMKZK7VTyJjXbIe7PlqJcO2iz04gjkn2BwSM,3410
|
|
@@ -81,7 +81,7 @@ xp/models/config/__init__.py,sha256=gEZnX9eE3DjFtLtF32riEjJQLypqQRbyPauBI4Cowbs,
|
|
|
81
81
|
xp/models/config/conson_module_config.py,sha256=t1G0LnNNMnjs3ahhz4-Z_5SlEv2FCrcRq13OmvZ2pvA,3009
|
|
82
82
|
xp/models/homekit/__init__.py,sha256=5HDSOClCu0ArK3IICn3_LDMMLBAzLjBxUUSF73bxSSk,34
|
|
83
83
|
xp/models/homekit/homekit_accessory.py,sha256=ANjDWlFxeNTstl7lKdmf6vMOC0wc005vpiD6awRcptA,1052
|
|
84
|
-
xp/models/homekit/homekit_config.py,sha256=
|
|
84
|
+
xp/models/homekit/homekit_config.py,sha256=YOhODQpURg_1OU0i-4qMglU4E37feNKKtY1uuZRXSoY,3759
|
|
85
85
|
xp/models/log_entry.py,sha256=tAiNwouCP2d4jKiHJY9a-2iAi8LWTpG-TZsOPDIstlA,4423
|
|
86
86
|
xp/models/protocol/__init__.py,sha256=TJ_CJKchA-xgQiv5vCo_ndBBZjrcaTmjT74bR0T-5Cw,38
|
|
87
87
|
xp/models/protocol/conbus_protocol.py,sha256=gFaXK1VY74aVQhMH69Dr-dTDbDuQDQGs8vKmXqK_te8,9318
|
|
@@ -112,7 +112,7 @@ xp/models/term/telegram_display.py,sha256=tXWeEtoIBSnScjha3ZHV9UPICmtBF2bkoLVIjQ
|
|
|
112
112
|
xp/models/write_config_type.py,sha256=IqgguaHgKvz4Qt-WaSVu3J2VaXgtS-br9Yp8q_xkIkY,895
|
|
113
113
|
xp/services/__init__.py,sha256=W9YZyrkh7vm--ZHhAXNQiOYQs5yhhmUHXP5I0Lf1XBg,782
|
|
114
114
|
xp/services/actiontable/__init__.py,sha256=z6js4EuJ6xKHaseTEhuEvKo1tr9K1XyQiruReJtBiPY,26
|
|
115
|
-
xp/services/actiontable/actiontable_serializer.py,sha256=
|
|
115
|
+
xp/services/actiontable/actiontable_serializer.py,sha256=GTpC9_bOdxqQl7cVhpEH9aGusZNRn5WcahwPc2XUWaY,8393
|
|
116
116
|
xp/services/actiontable/download_state_machine.py,sha256=lqNYN9LGGK2KiVUsmvyRfryWRB4-NOfsp7-9GrFubK4,9978
|
|
117
117
|
xp/services/actiontable/msactiontable_serializer.py,sha256=RRL6TZ1gpSQw81kAiw2BV3jTqm4fCJC0pWIcO26Cmos,174
|
|
118
118
|
xp/services/actiontable/msactiontable_xp20_serializer.py,sha256=5K6FxgbV2F4brumNaOH6M8qPyCxIfaqCGOPIYDmFdnk,6998
|
|
@@ -124,7 +124,7 @@ xp/services/conbus/actiontable/__init__.py,sha256=oD6vRk_Ye-eZ9s_hldAgtRJFu4mfAn
|
|
|
124
124
|
xp/services/conbus/actiontable/actiontable_download_service.py,sha256=41Hr1753IBpUeHQqO57uS7qxOB0rJt8qCpznzKlUPOM,15028
|
|
125
125
|
xp/services/conbus/actiontable/actiontable_list_service.py,sha256=oTDSpBkp-MJeaF5bhRnwkSy3na55xqQ4e2ykJzbMCUo,3236
|
|
126
126
|
xp/services/conbus/actiontable/actiontable_show_service.py,sha256=WISY2VsmSlceGa5_9lpFO-gs5TnTjv6YidQksUjCapk,3058
|
|
127
|
-
xp/services/conbus/actiontable/actiontable_upload_service.py,sha256=
|
|
127
|
+
xp/services/conbus/actiontable/actiontable_upload_service.py,sha256=bIAN3Ca3BHTPtZYfPijAgsWP91k-uNFAW7-i-LoTE4I,13553
|
|
128
128
|
xp/services/conbus/conbus_blink_all_service.py,sha256=toDIZDXBGBYnEishcdnJrVzkmfPi7g5nCDXuyA_wFCs,8536
|
|
129
129
|
xp/services/conbus/conbus_blink_service.py,sha256=ggLuzeq_UsgCoxRxg2bsNs9p8Lw_shjsj-niRzb5dKk,7953
|
|
130
130
|
xp/services/conbus/conbus_custom_service.py,sha256=9OIRC2CG_rN96vbv_EZXf7BrX_abhqi5MZx0Se8fEhU,7826
|
|
@@ -143,7 +143,7 @@ xp/services/conbus/write_config_service.py,sha256=BCfmLNPRDpwSwRMRYJvx2FXA8IZsdg
|
|
|
143
143
|
xp/services/log_file_service.py,sha256=y775InPlpG8Z_uitHt7yrgRG-3vlIuCfdJjiZzPc_MA,10578
|
|
144
144
|
xp/services/module_type_service.py,sha256=ga4WO7dmJEaDKrXPmrJ-72n4UaEZonead23lHBkFN1E,7505
|
|
145
145
|
xp/services/protocol/__init__.py,sha256=o5adyCwOrqSf4ADUStor44TFUerYx6QmxGpq4Q0dhSo,631
|
|
146
|
-
xp/services/protocol/conbus_event_protocol.py,sha256=
|
|
146
|
+
xp/services/protocol/conbus_event_protocol.py,sha256=ZSejBycY9igqNdiiejkEAoiUWKsVkOgEWMUYp2uMAsk,19681
|
|
147
147
|
xp/services/reverse_proxy_service.py,sha256=Xuxo2ajjmTXLg5yd6gRGKHpFPX4-6kpOUg10doZB8vw,15161
|
|
148
148
|
xp/services/server/__init__.py,sha256=QEcCj-jK0goAukJCe15TKYFQfSAzWsduPT_wW0HxZU8,48
|
|
149
149
|
xp/services/server/base_server_service.py,sha256=Yu4sTOtp3a1yc3O3J7J-51i9FdFqG-VWBv9aNf0j-9k,12845
|
|
@@ -167,11 +167,11 @@ xp/services/telegram/telegram_service.py,sha256=jPu0Xrh3IpvqPLyuQT5Vf8HHw00vBing
|
|
|
167
167
|
xp/services/telegram/telegram_version_service.py,sha256=oXnZ_K7OQ7xD-GEj3zDYp52KlkqVuHpO4bf7gMlC_w4,10574
|
|
168
168
|
xp/services/term/__init__.py,sha256=BIeOK042bMR-0l6MA80wdW5VuHlpWOXtRER9IG5ilQA,245
|
|
169
169
|
xp/services/term/homekit_accessory_driver.py,sha256=jHhHHOOvzdDjuYys1cUZHcH2uwZy_Y6o5aGAQqXGAVg,5935
|
|
170
|
-
xp/services/term/homekit_service.py,sha256=
|
|
170
|
+
xp/services/term/homekit_service.py,sha256=8aEsmC-EwKTxf0Hxp0aFBdo4doUvhVHeVwfuuc1ttWw,28925
|
|
171
171
|
xp/services/term/protocol_monitor_service.py,sha256=5YBI0Nu7B7gMhaTbUhL6k9LSRfnCIj6CwrCYHiMHavA,10067
|
|
172
172
|
xp/services/term/state_monitor_service.py,sha256=EK9tNBfamAIV0z0EMsXDYWC-rXv6l6k_bHsC8xyEFSo,17116
|
|
173
173
|
xp/term/__init__.py,sha256=Xg2DhBeI3xQJLfc7_BPWI1por-rUXemyer5OtOt9Cus,51
|
|
174
|
-
xp/term/homekit.py,sha256=
|
|
174
|
+
xp/term/homekit.py,sha256=HJH3dZQsdp5rqcuV4EWJbytk7glCyDmj27614nRbIyI,9909
|
|
175
175
|
xp/term/homekit.tcss,sha256=A1f5-V3mvxAMZK_ERq8lLjNcOWH0U5tblIBbeL3OYYM,1382
|
|
176
176
|
xp/term/protocol.py,sha256=6MX3mduLei-AgLGaIe8lfOSu4Hi0y3KGePFFM2ssstc,3475
|
|
177
177
|
xp/term/protocol.tcss,sha256=r_KfxrbpycGHLVXqZc6INBBcUJME0hLrAZkF1oqnab4,2126
|
|
@@ -191,4 +191,4 @@ xp/utils/logging.py,sha256=wJ1d-yg97NiZUrt2F8iDMcmnHVwC-PErcI-7dpyiRDc,3777
|
|
|
191
191
|
xp/utils/serialization.py,sha256=TS1OwpTOemSvXsCGw3js4JkYYFEqkzrPe8V9QYQefdw,4684
|
|
192
192
|
xp/utils/state_machine.py,sha256=W9AY4ntRZnFeHAa5d43hm37j53uJPlqkRvWTPiBhJ_0,2464
|
|
193
193
|
xp/utils/time_utils.py,sha256=K17godWpL18VEypbTlvNOEDG6R3huYnf29yjkcnwRpU,3796
|
|
194
|
-
conson_xp-2.0.
|
|
194
|
+
conson_xp-2.0.2.dist-info/RECORD,,
|
xp/__init__.py
CHANGED
xp/cli/main.py
CHANGED
|
@@ -24,17 +24,36 @@ from xp.utils.logging import LoggerService
|
|
|
24
24
|
cls=HelpColorsGroup, help_headers_color="yellow", help_options_color="green"
|
|
25
25
|
)
|
|
26
26
|
@click.version_option()
|
|
27
|
+
@click.option(
|
|
28
|
+
"--cli-config",
|
|
29
|
+
"-c",
|
|
30
|
+
default="cli.yml",
|
|
31
|
+
help="Path to the CLI configuration file (default: cli.yml)",
|
|
32
|
+
type=click.Path(exists=False),
|
|
33
|
+
)
|
|
34
|
+
@click.option(
|
|
35
|
+
"--log-config",
|
|
36
|
+
"-l",
|
|
37
|
+
default="logger.yml",
|
|
38
|
+
help="Path to the logger configuration file (default: logger.yml)",
|
|
39
|
+
type=click.Path(exists=False),
|
|
40
|
+
)
|
|
27
41
|
@click.pass_context
|
|
28
|
-
def cli(ctx: click.Context) -> None:
|
|
42
|
+
def cli(ctx: click.Context, cli_config: str, log_config: str) -> None:
|
|
29
43
|
"""
|
|
30
44
|
XP CLI tool for remote console bus operations.
|
|
31
45
|
|
|
32
46
|
Args:
|
|
33
47
|
ctx: Click context object for passing state between commands.
|
|
48
|
+
cli_config: Path to the CLI configuration file.
|
|
49
|
+
log_config: Path to the logger configuration file.
|
|
34
50
|
"""
|
|
35
|
-
container = ServiceContainer(
|
|
36
|
-
|
|
37
|
-
|
|
51
|
+
container = ServiceContainer(
|
|
52
|
+
client_config_path=cli_config,
|
|
53
|
+
logger_config_path=log_config,
|
|
54
|
+
)
|
|
55
|
+
logger_service = container.get_container().resolve(LoggerService)
|
|
56
|
+
logger_service.setup()
|
|
38
57
|
|
|
39
58
|
# Initialize the service container and store it in the context
|
|
40
59
|
ctx.ensure_object(dict)
|
|
@@ -15,11 +15,13 @@ class ClientConfig(BaseModel):
|
|
|
15
15
|
ip: IP address of the Conbus server.
|
|
16
16
|
port: Port number for the connection.
|
|
17
17
|
timeout: Connection timeout in seconds.
|
|
18
|
+
queue_delay_max: Max delay between queued telegrams in centiseconds (default 40 = 0.40s).
|
|
18
19
|
"""
|
|
19
20
|
|
|
20
21
|
ip: str = "192.168.1.100"
|
|
21
22
|
port: int = 10001
|
|
22
23
|
timeout: float = 0.1
|
|
24
|
+
queue_delay_max: int = 40
|
|
23
25
|
|
|
24
26
|
|
|
25
27
|
class ConbusClientConfig(BaseModel):
|
|
@@ -66,6 +66,10 @@ class HomekitAccessoryConfig(BaseModel):
|
|
|
66
66
|
on_action: on code for the accessory.
|
|
67
67
|
off_action: off code for the accessory.
|
|
68
68
|
toggle_action: Optional toggle action code for the accessory.
|
|
69
|
+
dimup_action: Optional dim up action code for the dimmable accessory.
|
|
70
|
+
dimdown_action: Optional dim down action code for the dimmable accessory.
|
|
71
|
+
levelup_action: Optional level up action code for the dimmable accessory.
|
|
72
|
+
leveldown_action: Optional level down action code for the dimmable accessory.
|
|
69
73
|
hap_accessory: Optional HAP accessory identifier.
|
|
70
74
|
"""
|
|
71
75
|
|
|
@@ -78,6 +82,10 @@ class HomekitAccessoryConfig(BaseModel):
|
|
|
78
82
|
on_action: str
|
|
79
83
|
off_action: str
|
|
80
84
|
toggle_action: Optional[str] = None
|
|
85
|
+
dimup_action: Optional[str] = None
|
|
86
|
+
dimdown_action: Optional[str] = None
|
|
87
|
+
levelup_action: Optional[str] = None
|
|
88
|
+
leveldown_action: Optional[str] = None
|
|
81
89
|
hap_accessory: Optional[int] = None
|
|
82
90
|
|
|
83
91
|
|
|
@@ -65,8 +65,8 @@ class ActionTableSerializer(ActionTableSerializerProtocol):
|
|
|
65
65
|
link_number = de_bcd(data[i + 1])
|
|
66
66
|
module_input = de_bcd(data[i + 2])
|
|
67
67
|
|
|
68
|
-
# Extract output
|
|
69
|
-
module_output = lower3(data[i + 3])
|
|
68
|
+
# Extract output (0-indexed in wire format, convert to 1-indexed) and command
|
|
69
|
+
module_output = lower3(data[i + 3]) + 1
|
|
70
70
|
command_raw = upper5(data[i + 3])
|
|
71
71
|
|
|
72
72
|
parameter_raw = byte_to_unsigned(data[i + 4])
|
|
@@ -125,8 +125,8 @@ class ActionTableSerializer(ActionTableSerializerProtocol):
|
|
|
125
125
|
link_byte = to_bcd(entry.link_number)
|
|
126
126
|
input_byte = to_bcd(entry.module_input)
|
|
127
127
|
|
|
128
|
-
# Combine output (lower 3 bits) and command (upper 5 bits)
|
|
129
|
-
output_command_byte = (entry.module_output & 0x07) | (
|
|
128
|
+
# Combine output (lower 3 bits, 0-indexed) and command (upper 5 bits)
|
|
129
|
+
output_command_byte = ((entry.module_output - 1) & 0x07) | (
|
|
130
130
|
(entry.command.value & 0x1F) << 3
|
|
131
131
|
)
|
|
132
132
|
|
|
@@ -81,6 +81,7 @@ class ActionTableUploadService:
|
|
|
81
81
|
# Upload state
|
|
82
82
|
self.upload_data_chunks: list[str] = []
|
|
83
83
|
self.current_chunk_index: int = 0
|
|
84
|
+
self._eof_sent: bool = False
|
|
84
85
|
|
|
85
86
|
# Set up logging
|
|
86
87
|
self.logger = logging.getLogger(__name__)
|
|
@@ -173,7 +174,7 @@ class ActionTableUploadService:
|
|
|
173
174
|
)
|
|
174
175
|
self.current_chunk_index += 1
|
|
175
176
|
self.on_progress.emit(".")
|
|
176
|
-
|
|
177
|
+
elif not self._eof_sent:
|
|
177
178
|
# All chunks sent, send EOF
|
|
178
179
|
self.logger.debug("All chunks sent, sending EOF")
|
|
179
180
|
self.conbus_protocol.send_telegram(
|
|
@@ -182,7 +183,13 @@ class ActionTableUploadService:
|
|
|
182
183
|
system_function=SystemFunction.EOF,
|
|
183
184
|
data_value="00",
|
|
184
185
|
)
|
|
186
|
+
self.on_progress.emit("END")
|
|
187
|
+
self.logger.debug("EOF sent, waiting for last ACK")
|
|
188
|
+
self._eof_sent = True
|
|
189
|
+
else:
|
|
190
|
+
self.logger.debug("Last ACK received, closing connection")
|
|
185
191
|
self.on_finish.emit(True)
|
|
192
|
+
|
|
186
193
|
elif reply_telegram.system_function == SystemFunction.NAK:
|
|
187
194
|
self.logger.debug("Received NAK during upload")
|
|
188
195
|
self.failed("Upload failed: NAK received")
|
|
@@ -513,7 +513,7 @@ class ConbusEventProtocol(protocol.Protocol, protocol.ClientFactory):
|
|
|
513
513
|
self.logger.debug("Queue manager: event loop")
|
|
514
514
|
telegram = self.telegram_queue.get_nowait()
|
|
515
515
|
self.sendFrame(telegram)
|
|
516
|
-
later = randint(
|
|
516
|
+
later = randint(5, self.cli_config.queue_delay_max) / 100
|
|
517
517
|
self.call_later(later, self.process_telegram_queue)
|
|
518
518
|
|
|
519
519
|
def set_event_loop(self, event_loop: asyncio.AbstractEventLoop) -> None:
|
|
@@ -81,6 +81,9 @@ class HomekitService:
|
|
|
81
81
|
# Set up HomeKit callback
|
|
82
82
|
self._accessory_driver.set_callback(self._on_homekit_set)
|
|
83
83
|
|
|
84
|
+
# Track active level action: (accessory_id, action_type) or None
|
|
85
|
+
self._active_level_action: Optional[tuple[str, str]] = None
|
|
86
|
+
|
|
84
87
|
# Connect to protocol signals
|
|
85
88
|
self._connect_signals()
|
|
86
89
|
|
|
@@ -421,12 +424,15 @@ class HomekitService:
|
|
|
421
424
|
Returns:
|
|
422
425
|
True if command was sent, False otherwise.
|
|
423
426
|
"""
|
|
427
|
+
config = self._find_accessory_config_by_id(accessory_id)
|
|
424
428
|
state = self._accessory_states.get(accessory_id)
|
|
425
|
-
if not state:
|
|
429
|
+
if not config or not state or not config.dimup_action:
|
|
430
|
+
self.logger.warning(f"No config for accessory {accessory_id}")
|
|
426
431
|
return False
|
|
427
|
-
|
|
428
|
-
self.
|
|
429
|
-
|
|
432
|
+
|
|
433
|
+
self.send_action(config.dimup_action)
|
|
434
|
+
self.on_status_message.emit(f"Dim+ {state.accessory_name}")
|
|
435
|
+
return True
|
|
430
436
|
|
|
431
437
|
def decrease_dimmer(self, accessory_id: str) -> bool:
|
|
432
438
|
"""
|
|
@@ -438,12 +444,103 @@ class HomekitService:
|
|
|
438
444
|
Returns:
|
|
439
445
|
True if command was sent, False otherwise.
|
|
440
446
|
"""
|
|
447
|
+
config = self._find_accessory_config_by_id(accessory_id)
|
|
441
448
|
state = self._accessory_states.get(accessory_id)
|
|
442
|
-
if not state:
|
|
449
|
+
if not config or not state or not config.dimdown_action:
|
|
450
|
+
self.logger.warning(f"No config for accessory {accessory_id}")
|
|
443
451
|
return False
|
|
444
|
-
|
|
445
|
-
self.
|
|
446
|
-
|
|
452
|
+
|
|
453
|
+
self.send_action(config.dimdown_action)
|
|
454
|
+
self.on_status_message.emit(f"Dim- {state.accessory_name}")
|
|
455
|
+
return True
|
|
456
|
+
|
|
457
|
+
def levelup_selected(self, accessory_id: str) -> bool:
|
|
458
|
+
"""
|
|
459
|
+
Increase level for accessory (toggle Make/Break).
|
|
460
|
+
|
|
461
|
+
First press sends Make (M), second press sends Break (B).
|
|
462
|
+
|
|
463
|
+
Args:
|
|
464
|
+
accessory_id: Accessory ID (e.g., "A12_1").
|
|
465
|
+
|
|
466
|
+
Returns:
|
|
467
|
+
True if command was sent, False otherwise.
|
|
468
|
+
"""
|
|
469
|
+
config = self._find_accessory_config_by_id(accessory_id)
|
|
470
|
+
state = self._accessory_states.get(accessory_id)
|
|
471
|
+
if not config or not state or not config.levelup_action:
|
|
472
|
+
self.logger.warning(f"No config for accessory {accessory_id}")
|
|
473
|
+
return False
|
|
474
|
+
|
|
475
|
+
return self._send_level_action(
|
|
476
|
+
accessory_id, "levelup", config.levelup_action, state.accessory_name
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
def leveldown_selected(self, accessory_id: str) -> bool:
|
|
480
|
+
"""
|
|
481
|
+
Decrease level for accessory (toggle Make/Break).
|
|
482
|
+
|
|
483
|
+
First press sends Make (M), second press sends Break (B).
|
|
484
|
+
|
|
485
|
+
Args:
|
|
486
|
+
accessory_id: Accessory ID (e.g., "A12_1").
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
True if command was sent, False otherwise.
|
|
490
|
+
"""
|
|
491
|
+
config = self._find_accessory_config_by_id(accessory_id)
|
|
492
|
+
state = self._accessory_states.get(accessory_id)
|
|
493
|
+
if not config or not state or not config.leveldown_action:
|
|
494
|
+
self.logger.warning(f"No config for accessory {accessory_id}")
|
|
495
|
+
return False
|
|
496
|
+
|
|
497
|
+
return self._send_level_action(
|
|
498
|
+
accessory_id, "leveldown", config.leveldown_action, state.accessory_name
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
def _send_level_action(
|
|
502
|
+
self, accessory_id: str, action_type: str, action: str, name: str
|
|
503
|
+
) -> bool:
|
|
504
|
+
"""
|
|
505
|
+
Send level action with Make/Break toggle.
|
|
506
|
+
|
|
507
|
+
Args:
|
|
508
|
+
accessory_id: Accessory ID.
|
|
509
|
+
action_type: "levelup" or "leveldown".
|
|
510
|
+
action: Action code (e.g., "E02L13I15").
|
|
511
|
+
name: Accessory name for status message.
|
|
512
|
+
|
|
513
|
+
Returns:
|
|
514
|
+
True if command was sent.
|
|
515
|
+
"""
|
|
516
|
+
current = self._active_level_action
|
|
517
|
+
|
|
518
|
+
# If same action is active, send Break and clear
|
|
519
|
+
if current and current[0] == accessory_id and current[1] == action_type:
|
|
520
|
+
self._conbus_protocol.send_raw_telegram(f"{action}B")
|
|
521
|
+
self._active_level_action = None
|
|
522
|
+
direction = "+" if action_type == "levelup" else "-"
|
|
523
|
+
self.on_status_message.emit(f"Level{direction} {name} [B]")
|
|
524
|
+
return True
|
|
525
|
+
|
|
526
|
+
# If different action is active, send Break for it first
|
|
527
|
+
if current:
|
|
528
|
+
old_config = self._find_accessory_config_by_id(current[0])
|
|
529
|
+
if old_config:
|
|
530
|
+
old_action = (
|
|
531
|
+
old_config.levelup_action
|
|
532
|
+
if current[1] == "levelup"
|
|
533
|
+
else old_config.leveldown_action
|
|
534
|
+
)
|
|
535
|
+
if old_action:
|
|
536
|
+
self._conbus_protocol.send_raw_telegram(f"{old_action}B")
|
|
537
|
+
|
|
538
|
+
# Send Make for new action
|
|
539
|
+
self._conbus_protocol.send_raw_telegram(f"{action}M")
|
|
540
|
+
self._active_level_action = (accessory_id, action_type)
|
|
541
|
+
direction = "+" if action_type == "levelup" else "-"
|
|
542
|
+
self.on_status_message.emit(f"Level{direction} {name} [M]")
|
|
543
|
+
return True
|
|
447
544
|
|
|
448
545
|
def refresh_all(self) -> None:
|
|
449
546
|
"""
|
xp/term/homekit.py
CHANGED
|
@@ -41,6 +41,8 @@ class HomekitApp(App[None]):
|
|
|
41
41
|
("minus", "turn_off_selected", "Off"),
|
|
42
42
|
("plus", "dim_up", "Dim+"),
|
|
43
43
|
("quotation_mark", "dim_down", "Dim-"),
|
|
44
|
+
("asterisk", "level_up", "Level+"),
|
|
45
|
+
("ç", "level_down", "Level-"),
|
|
44
46
|
]
|
|
45
47
|
|
|
46
48
|
def __init__(self, homekit_service: HomekitService) -> None:
|
|
@@ -106,12 +108,17 @@ class HomekitApp(App[None]):
|
|
|
106
108
|
- - : Turn OFF
|
|
107
109
|
- + : Dim up
|
|
108
110
|
- " : Dim down
|
|
111
|
+
- * : Level up
|
|
112
|
+
- ç : Level down
|
|
109
113
|
|
|
110
114
|
Args:
|
|
111
115
|
event: Key press event.
|
|
112
116
|
"""
|
|
113
117
|
key = event.key
|
|
114
118
|
|
|
119
|
+
# Debug: show received key
|
|
120
|
+
self.homekit_service.on_status_message.emit(f"Key: {key}")
|
|
121
|
+
|
|
115
122
|
# Selection keys (a-z0-9)
|
|
116
123
|
if len(key) == 1 and (("a" <= key <= "z") or ("0" <= key <= "9")):
|
|
117
124
|
accessory_id = self.homekit_service.select_accessory(key)
|
|
@@ -140,6 +147,12 @@ class HomekitApp(App[None]):
|
|
|
140
147
|
elif key in ("quotation_mark", '"'):
|
|
141
148
|
self.homekit_service.decrease_dimmer(self.selected_accessory_id)
|
|
142
149
|
event.prevent_default()
|
|
150
|
+
elif key in ("asterisk", "star", "*"):
|
|
151
|
+
self.homekit_service.levelup_selected(self.selected_accessory_id)
|
|
152
|
+
event.prevent_default()
|
|
153
|
+
elif key in ("cedille", "ç"):
|
|
154
|
+
self.homekit_service.leveldown_selected(self.selected_accessory_id)
|
|
155
|
+
event.prevent_default()
|
|
143
156
|
|
|
144
157
|
def _select_row(self, action_key: str) -> None:
|
|
145
158
|
"""
|
|
@@ -243,6 +256,16 @@ class HomekitApp(App[None]):
|
|
|
243
256
|
if self.selected_accessory_id:
|
|
244
257
|
self.homekit_service.decrease_dimmer(self.selected_accessory_id)
|
|
245
258
|
|
|
259
|
+
def action_level_up(self) -> None:
|
|
260
|
+
"""Increase level on selected accessory."""
|
|
261
|
+
if self.selected_accessory_id:
|
|
262
|
+
self.homekit_service.levelup_selected(self.selected_accessory_id)
|
|
263
|
+
|
|
264
|
+
def action_level_down(self) -> None:
|
|
265
|
+
"""Decrease level on selected accessory."""
|
|
266
|
+
if self.selected_accessory_id:
|
|
267
|
+
self.homekit_service.leveldown_selected(self.selected_accessory_id)
|
|
268
|
+
|
|
246
269
|
async def on_unmount(self) -> None:
|
|
247
270
|
"""Stop AccessoryDriver and clean up service when app unmounts."""
|
|
248
271
|
await self.homekit_service.stop()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|