conson-xp 1.51.0__py3-none-any.whl → 1.52.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.
- {conson_xp-1.51.0.dist-info → conson_xp-1.52.0.dist-info}/METADATA +2 -1
- {conson_xp-1.51.0.dist-info → conson_xp-1.52.0.dist-info}/RECORD +19 -13
- xp/__init__.py +1 -1
- xp/cli/commands/term/term_commands.py +23 -0
- xp/models/homekit/homekit_config.py +6 -0
- xp/models/term/__init__.py +2 -0
- xp/models/term/accessory_state.py +50 -0
- xp/services/homekit/homekit_config_validator.py +1 -1
- xp/services/term/homekit_accessory_driver.py +168 -0
- xp/services/term/homekit_service.py +582 -0
- xp/services/term/state_monitor_service.py +1 -1
- xp/term/homekit.py +116 -0
- xp/term/homekit.tcss +86 -0
- xp/term/widgets/room_list.py +232 -0
- xp/term/widgets/status_footer.py +6 -3
- xp/utils/dependencies.py +31 -0
- {conson_xp-1.51.0.dist-info → conson_xp-1.52.0.dist-info}/WHEEL +0 -0
- {conson_xp-1.51.0.dist-info → conson_xp-1.52.0.dist-info}/entry_points.txt +0 -0
- {conson_xp-1.51.0.dist-info → conson_xp-1.52.0.dist-info}/licenses/LICENSE +0 -0
xp/term/homekit.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""HomeKit TUI Application."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Optional
|
|
5
|
+
|
|
6
|
+
from textual.app import App, ComposeResult
|
|
7
|
+
|
|
8
|
+
from xp.services.term.homekit_service import HomekitService
|
|
9
|
+
from xp.term.widgets.room_list import RoomListWidget
|
|
10
|
+
from xp.term.widgets.status_footer import StatusFooterWidget
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class HomekitApp(App[None]):
|
|
14
|
+
"""
|
|
15
|
+
Textual app for HomeKit accessory monitoring.
|
|
16
|
+
|
|
17
|
+
Displays rooms and accessories with real-time state updates
|
|
18
|
+
and toggle control via action keys.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
homekit_service: HomekitService for accessory state operations.
|
|
22
|
+
CSS_PATH: Path to CSS stylesheet file.
|
|
23
|
+
BINDINGS: Keyboard bindings for app actions.
|
|
24
|
+
TITLE: Application title displayed in header.
|
|
25
|
+
ENABLE_COMMAND_PALETTE: Disable Textual's command palette feature.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
CSS_PATH = Path(__file__).parent / "homekit.tcss"
|
|
29
|
+
TITLE = "HomeKit"
|
|
30
|
+
ENABLE_COMMAND_PALETTE = False
|
|
31
|
+
|
|
32
|
+
BINDINGS = [
|
|
33
|
+
("Q", "quit", "Quit"),
|
|
34
|
+
("C", "toggle_connection", "Connect"),
|
|
35
|
+
("r", "refresh_all", "Refresh"),
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
def __init__(self, homekit_service: HomekitService) -> None:
|
|
39
|
+
"""
|
|
40
|
+
Initialize the HomeKit app.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
homekit_service: HomekitService for accessory state operations.
|
|
44
|
+
"""
|
|
45
|
+
super().__init__()
|
|
46
|
+
self.homekit_service: HomekitService = homekit_service
|
|
47
|
+
self.room_list_widget: Optional[RoomListWidget] = None
|
|
48
|
+
self.footer_widget: Optional[StatusFooterWidget] = None
|
|
49
|
+
|
|
50
|
+
def compose(self) -> ComposeResult:
|
|
51
|
+
"""
|
|
52
|
+
Compose the app layout with widgets.
|
|
53
|
+
|
|
54
|
+
Yields:
|
|
55
|
+
RoomListWidget and StatusFooterWidget.
|
|
56
|
+
"""
|
|
57
|
+
self.room_list_widget = RoomListWidget(
|
|
58
|
+
service=self.homekit_service, id="room-list"
|
|
59
|
+
)
|
|
60
|
+
yield self.room_list_widget
|
|
61
|
+
|
|
62
|
+
self.footer_widget = StatusFooterWidget(
|
|
63
|
+
service=self.homekit_service, id="footer-container"
|
|
64
|
+
)
|
|
65
|
+
yield self.footer_widget
|
|
66
|
+
|
|
67
|
+
async def on_mount(self) -> None:
|
|
68
|
+
"""
|
|
69
|
+
Initialize app after UI is mounted.
|
|
70
|
+
|
|
71
|
+
Delays connection by 0.5s to let UI render first. Starts the AccessoryDriver and
|
|
72
|
+
sets up automatic screen refresh every second to update elapsed times.
|
|
73
|
+
"""
|
|
74
|
+
import asyncio
|
|
75
|
+
|
|
76
|
+
# Delay connection to let UI render
|
|
77
|
+
await asyncio.sleep(0.5)
|
|
78
|
+
await self.homekit_service.start()
|
|
79
|
+
|
|
80
|
+
# Set up periodic refresh to update elapsed times
|
|
81
|
+
self.set_interval(1.0, self._refresh_last_update_column)
|
|
82
|
+
|
|
83
|
+
def _refresh_last_update_column(self) -> None:
|
|
84
|
+
"""Refresh only the last_update column to show elapsed time."""
|
|
85
|
+
if self.room_list_widget:
|
|
86
|
+
self.room_list_widget.refresh_last_update_times()
|
|
87
|
+
|
|
88
|
+
def on_key(self, event: Any) -> None:
|
|
89
|
+
"""
|
|
90
|
+
Handle key press events for action keys.
|
|
91
|
+
|
|
92
|
+
Intercepts a-z keys to toggle accessories.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
event: Key press event.
|
|
96
|
+
"""
|
|
97
|
+
key = event.key.lower()
|
|
98
|
+
if len(key) == 1 and "a" <= key <= "z":
|
|
99
|
+
if self.homekit_service.toggle_accessory(key):
|
|
100
|
+
event.prevent_default()
|
|
101
|
+
|
|
102
|
+
def action_toggle_connection(self) -> None:
|
|
103
|
+
"""
|
|
104
|
+
Toggle connection on 'c' key press.
|
|
105
|
+
|
|
106
|
+
Connects if disconnected/failed, disconnects if connected/connecting.
|
|
107
|
+
"""
|
|
108
|
+
self.homekit_service.toggle_connection()
|
|
109
|
+
|
|
110
|
+
def action_refresh_all(self) -> None:
|
|
111
|
+
"""Refresh all module data on 'r' key press."""
|
|
112
|
+
self.homekit_service.refresh_all()
|
|
113
|
+
|
|
114
|
+
async def on_unmount(self) -> None:
|
|
115
|
+
"""Stop AccessoryDriver and clean up service when app unmounts."""
|
|
116
|
+
await self.homekit_service.stop()
|
xp/term/homekit.tcss
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* HomeKit TUI Styling */
|
|
2
|
+
|
|
3
|
+
/* Color overrides */
|
|
4
|
+
$success: #00ff00;
|
|
5
|
+
|
|
6
|
+
/* App-level styling */
|
|
7
|
+
Screen {
|
|
8
|
+
background: $background;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Room List Widget */
|
|
12
|
+
RoomListWidget {
|
|
13
|
+
border: solid $success;
|
|
14
|
+
border-title-align: left;
|
|
15
|
+
width: 1fr;
|
|
16
|
+
height: 1fr;
|
|
17
|
+
background: $background;
|
|
18
|
+
padding: 1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
RoomListWidget:focus {
|
|
22
|
+
background: $background;
|
|
23
|
+
background-tint: transparent;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#rooms-table {
|
|
27
|
+
background: $background !important;
|
|
28
|
+
width: 100%;
|
|
29
|
+
height: 1fr;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
#rooms-table:focus {
|
|
33
|
+
background: $background !important;
|
|
34
|
+
background-tint: transparent;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
DataTable {
|
|
38
|
+
background: $background;
|
|
39
|
+
color: $success;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
DataTable > .datatable--header {
|
|
43
|
+
background: $background;
|
|
44
|
+
color: $success;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
DataTable > .datatable--cursor {
|
|
48
|
+
background: $background;
|
|
49
|
+
color: $success;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
DataTable:focus > .datatable--cursor {
|
|
53
|
+
background: $background;
|
|
54
|
+
color: $success;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Footer styling */
|
|
58
|
+
#footer-container {
|
|
59
|
+
dock: bottom;
|
|
60
|
+
height: 1;
|
|
61
|
+
background: $background;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
Footer {
|
|
65
|
+
width: auto;
|
|
66
|
+
background: $background;
|
|
67
|
+
color: $text;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#status-text {
|
|
71
|
+
dock: right;
|
|
72
|
+
width: auto;
|
|
73
|
+
padding: 0 3;
|
|
74
|
+
background: $background;
|
|
75
|
+
color: $text;
|
|
76
|
+
text-align: right;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
#status-line {
|
|
80
|
+
dock: right;
|
|
81
|
+
width: auto;
|
|
82
|
+
padding: 0 1;
|
|
83
|
+
background: $background;
|
|
84
|
+
color: $text;
|
|
85
|
+
text-align: right;
|
|
86
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"""Room List Widget for displaying HomeKit accessories table."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Any, List, Optional
|
|
5
|
+
|
|
6
|
+
from rich.text import Text
|
|
7
|
+
from textual.app import ComposeResult
|
|
8
|
+
from textual.widgets import DataTable, Static
|
|
9
|
+
|
|
10
|
+
from xp.models.term.accessory_state import AccessoryState
|
|
11
|
+
from xp.services.term.homekit_service import HomekitService
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RoomListWidget(Static):
|
|
15
|
+
"""
|
|
16
|
+
Widget displaying HomeKit accessories in a data table.
|
|
17
|
+
|
|
18
|
+
Shows room/accessory hierarchy with real-time state updates from HomekitService.
|
|
19
|
+
Table displays: room/accessory, action, state, dim, module, serial, type, status, output, updated.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
service: HomekitService for accessory state updates.
|
|
23
|
+
table: DataTable widget displaying accessory information.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
service: Optional[HomekitService] = None,
|
|
29
|
+
*args: Any,
|
|
30
|
+
**kwargs: Any,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""
|
|
33
|
+
Initialize the Room List widget.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
service: Optional HomekitService for signal subscriptions.
|
|
37
|
+
args: Additional positional arguments for Static.
|
|
38
|
+
kwargs: Additional keyword arguments for Static.
|
|
39
|
+
"""
|
|
40
|
+
super().__init__(*args, **kwargs)
|
|
41
|
+
self.service = service
|
|
42
|
+
self.table: Optional[DataTable] = None
|
|
43
|
+
self._row_keys: dict[str, Any] = {} # Map accessory_id to row key
|
|
44
|
+
self._current_room: str = ""
|
|
45
|
+
|
|
46
|
+
def compose(self) -> ComposeResult:
|
|
47
|
+
"""
|
|
48
|
+
Compose the widget layout.
|
|
49
|
+
|
|
50
|
+
Yields:
|
|
51
|
+
DataTable widget.
|
|
52
|
+
"""
|
|
53
|
+
self.table = DataTable(id="rooms-table", cursor_type="row")
|
|
54
|
+
yield self.table
|
|
55
|
+
|
|
56
|
+
def on_mount(self) -> None:
|
|
57
|
+
"""Initialize table and subscribe to service signals when widget mounts."""
|
|
58
|
+
self.border_title = "Rooms"
|
|
59
|
+
|
|
60
|
+
if self.table:
|
|
61
|
+
self.table.add_column("room / accessory", key="name", width=35)
|
|
62
|
+
self.table.add_column("action", key="action", width=8)
|
|
63
|
+
self.table.add_column("state", key="state", width=7)
|
|
64
|
+
self.table.add_column("dim", key="dim", width=6)
|
|
65
|
+
self.table.add_column("module", key="module", width=8)
|
|
66
|
+
self.table.add_column("serial", key="serial", width=12)
|
|
67
|
+
self.table.add_column("type", key="type", width=10)
|
|
68
|
+
self.table.add_column("status", key="status", width=8)
|
|
69
|
+
self.table.add_column("output", key="output", width=7)
|
|
70
|
+
self.table.add_column("updated", key="updated", width=10)
|
|
71
|
+
|
|
72
|
+
if self.service:
|
|
73
|
+
self.service.on_room_list_updated.connect(self.update_accessory_list)
|
|
74
|
+
self.service.on_module_state_changed.connect(self.update_accessory_state)
|
|
75
|
+
|
|
76
|
+
def on_unmount(self) -> None:
|
|
77
|
+
"""Unsubscribe from service signals when widget unmounts."""
|
|
78
|
+
if self.service:
|
|
79
|
+
self.service.on_room_list_updated.disconnect(self.update_accessory_list)
|
|
80
|
+
self.service.on_module_state_changed.disconnect(self.update_accessory_state)
|
|
81
|
+
|
|
82
|
+
def update_accessory_list(self, accessory_states: List[AccessoryState]) -> None:
|
|
83
|
+
"""
|
|
84
|
+
Update entire accessory list from service.
|
|
85
|
+
|
|
86
|
+
Clears existing table and repopulates with all accessories grouped by room.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
accessory_states: List of all accessory states.
|
|
90
|
+
"""
|
|
91
|
+
if not self.table:
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
self.table.clear()
|
|
95
|
+
self._row_keys.clear()
|
|
96
|
+
self._current_room = ""
|
|
97
|
+
|
|
98
|
+
for state in accessory_states:
|
|
99
|
+
# Add room header row if new room
|
|
100
|
+
if state.room_name != self._current_room:
|
|
101
|
+
self._current_room = state.room_name
|
|
102
|
+
self.table.add_row()
|
|
103
|
+
self.table.add_row(Text(state.room_name, style="bold"))
|
|
104
|
+
self.table.add_row()
|
|
105
|
+
|
|
106
|
+
self._add_accessory_row(state)
|
|
107
|
+
|
|
108
|
+
def update_accessory_state(self, state: AccessoryState) -> None:
|
|
109
|
+
"""
|
|
110
|
+
Update individual accessory state in table.
|
|
111
|
+
|
|
112
|
+
Updates existing row if accessory exists, otherwise adds new row.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
state: Updated accessory state.
|
|
116
|
+
"""
|
|
117
|
+
if not self.table:
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
accessory_id = f"{state.module_name}_{state.output}"
|
|
121
|
+
|
|
122
|
+
if accessory_id in self._row_keys:
|
|
123
|
+
row_key = self._row_keys[accessory_id]
|
|
124
|
+
self.table.update_cell(
|
|
125
|
+
row_key, "state", Text(state.output_state, justify="center")
|
|
126
|
+
)
|
|
127
|
+
self.table.update_cell(
|
|
128
|
+
row_key, "dim", Text(self._format_dim(state), justify="center")
|
|
129
|
+
)
|
|
130
|
+
self.table.update_cell(row_key, "status", state.error_status)
|
|
131
|
+
self.table.update_cell(
|
|
132
|
+
row_key,
|
|
133
|
+
"updated",
|
|
134
|
+
Text(self._format_last_update(state.last_update), justify="center"),
|
|
135
|
+
)
|
|
136
|
+
else:
|
|
137
|
+
self._add_accessory_row(state)
|
|
138
|
+
|
|
139
|
+
def _add_accessory_row(self, state: AccessoryState) -> None:
|
|
140
|
+
"""
|
|
141
|
+
Add an accessory row to the table.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
state: Accessory state to add.
|
|
145
|
+
"""
|
|
146
|
+
if not self.table:
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
accessory_id = f"{state.module_name}_{state.output}"
|
|
150
|
+
row_key = self.table.add_row(
|
|
151
|
+
f" - {state.accessory_name}",
|
|
152
|
+
Text(state.action, justify="center"),
|
|
153
|
+
Text(state.output_state, justify="center"),
|
|
154
|
+
Text(self._format_dim(state), justify="center"),
|
|
155
|
+
state.module_name,
|
|
156
|
+
state.serial_number,
|
|
157
|
+
state.module_type,
|
|
158
|
+
state.error_status,
|
|
159
|
+
Text(str(state.output), justify="right"),
|
|
160
|
+
Text(self._format_last_update(state.last_update), justify="center"),
|
|
161
|
+
)
|
|
162
|
+
self._row_keys[accessory_id] = row_key
|
|
163
|
+
|
|
164
|
+
def _format_dim(self, state: AccessoryState) -> str:
|
|
165
|
+
"""
|
|
166
|
+
Format dimming state for display.
|
|
167
|
+
|
|
168
|
+
Shows percentage if dimmable and ON, "-" if dimmable and OFF, empty otherwise.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
state: Accessory state.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
Formatted dimming string.
|
|
175
|
+
"""
|
|
176
|
+
if not state.is_dimmable():
|
|
177
|
+
return ""
|
|
178
|
+
if state.output_state == "OFF":
|
|
179
|
+
return "-"
|
|
180
|
+
return state.dimming_state or ""
|
|
181
|
+
|
|
182
|
+
def _format_last_update(self, last_update: Optional[datetime]) -> str:
|
|
183
|
+
"""
|
|
184
|
+
Format last update timestamp for display.
|
|
185
|
+
|
|
186
|
+
Shows elapsed time in HH:MM:SS format or "--:--:--" if never updated.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
last_update: Last update timestamp or None.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Formatted time string.
|
|
193
|
+
"""
|
|
194
|
+
if last_update is None:
|
|
195
|
+
return "--:--:--"
|
|
196
|
+
|
|
197
|
+
elapsed = datetime.now() - last_update
|
|
198
|
+
total_seconds = int(elapsed.total_seconds())
|
|
199
|
+
|
|
200
|
+
hours = total_seconds // 3600
|
|
201
|
+
minutes = (total_seconds % 3600) // 60
|
|
202
|
+
seconds = total_seconds % 60
|
|
203
|
+
|
|
204
|
+
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
205
|
+
|
|
206
|
+
def refresh_last_update_times(self) -> None:
|
|
207
|
+
"""
|
|
208
|
+
Refresh only the last_update column for all accessories.
|
|
209
|
+
|
|
210
|
+
Updates the elapsed time display without querying the service.
|
|
211
|
+
"""
|
|
212
|
+
if not self.table or not self.service:
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
for accessory_id, row_key in self._row_keys.items():
|
|
216
|
+
state = next(
|
|
217
|
+
(
|
|
218
|
+
s
|
|
219
|
+
for s in self.service.accessory_states
|
|
220
|
+
if f"{s.module_name}_{s.output}" == accessory_id
|
|
221
|
+
),
|
|
222
|
+
None,
|
|
223
|
+
)
|
|
224
|
+
if state:
|
|
225
|
+
self.table.update_cell(
|
|
226
|
+
row_key,
|
|
227
|
+
"updated",
|
|
228
|
+
Text(
|
|
229
|
+
self._format_last_update(state.last_update),
|
|
230
|
+
justify="center",
|
|
231
|
+
),
|
|
232
|
+
)
|
xp/term/widgets/status_footer.py
CHANGED
|
@@ -7,6 +7,7 @@ from textual.containers import Horizontal
|
|
|
7
7
|
from textual.widgets import Footer, Static
|
|
8
8
|
|
|
9
9
|
from xp.models.term.connection_state import ConnectionState
|
|
10
|
+
from xp.services.term.homekit_service import HomekitService
|
|
10
11
|
from xp.services.term.protocol_monitor_service import ProtocolMonitorService
|
|
11
12
|
from xp.services.term.state_monitor_service import StateMonitorService
|
|
12
13
|
|
|
@@ -19,14 +20,16 @@ class StatusFooterWidget(Horizontal):
|
|
|
19
20
|
the current connection state. Subscribes directly to service signals.
|
|
20
21
|
|
|
21
22
|
Attributes:
|
|
22
|
-
service: ProtocolMonitorService or
|
|
23
|
+
service: ProtocolMonitorService, StateMonitorService, or HomekitService for connection state and status updates.
|
|
23
24
|
status_widget: Static widget displaying colored status dot.
|
|
24
25
|
status_text_widget: Static widget displaying status messages.
|
|
25
26
|
"""
|
|
26
27
|
|
|
27
28
|
def __init__(
|
|
28
29
|
self,
|
|
29
|
-
service: Optional[
|
|
30
|
+
service: Optional[
|
|
31
|
+
Union[ProtocolMonitorService, StateMonitorService, HomekitService]
|
|
32
|
+
] = None,
|
|
30
33
|
*args: Any,
|
|
31
34
|
**kwargs: Any,
|
|
32
35
|
) -> None:
|
|
@@ -34,7 +37,7 @@ class StatusFooterWidget(Horizontal):
|
|
|
34
37
|
Initialize the Status Footer widget.
|
|
35
38
|
|
|
36
39
|
Args:
|
|
37
|
-
service: Optional ProtocolMonitorService or
|
|
40
|
+
service: Optional ProtocolMonitorService, StateMonitorService, or HomekitService for signal subscriptions.
|
|
38
41
|
args: Additional positional arguments for Horizontal.
|
|
39
42
|
kwargs: Additional keyword arguments for Horizontal.
|
|
40
43
|
"""
|
xp/utils/dependencies.py
CHANGED
|
@@ -77,8 +77,11 @@ from xp.services.telegram.telegram_discover_service import TelegramDiscoverServi
|
|
|
77
77
|
from xp.services.telegram.telegram_link_number_service import LinkNumberService
|
|
78
78
|
from xp.services.telegram.telegram_output_service import TelegramOutputService
|
|
79
79
|
from xp.services.telegram.telegram_service import TelegramService
|
|
80
|
+
from xp.services.term.homekit_accessory_driver import HomekitAccessoryDriver
|
|
81
|
+
from xp.services.term.homekit_service import HomekitService
|
|
80
82
|
from xp.services.term.protocol_monitor_service import ProtocolMonitorService
|
|
81
83
|
from xp.services.term.state_monitor_service import StateMonitorService
|
|
84
|
+
from xp.term.homekit import HomekitApp
|
|
82
85
|
from xp.term.protocol import ProtocolMonitorApp
|
|
83
86
|
from xp.term.state import StateMonitorApp
|
|
84
87
|
from xp.utils.logging import LoggerService
|
|
@@ -265,6 +268,34 @@ class ServiceContainer:
|
|
|
265
268
|
scope=punq.Scope.singleton,
|
|
266
269
|
)
|
|
267
270
|
|
|
271
|
+
self.container.register(
|
|
272
|
+
HomekitAccessoryDriver,
|
|
273
|
+
factory=lambda: HomekitAccessoryDriver(
|
|
274
|
+
homekit_config=self.container.resolve(HomekitConfig),
|
|
275
|
+
),
|
|
276
|
+
scope=punq.Scope.singleton,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
self.container.register(
|
|
280
|
+
HomekitService,
|
|
281
|
+
factory=lambda: HomekitService(
|
|
282
|
+
conbus_protocol=self.container.resolve(ConbusEventProtocol),
|
|
283
|
+
homekit_config=self.container.resolve(HomekitConfig),
|
|
284
|
+
conson_config=self.container.resolve(ConsonModuleListConfig),
|
|
285
|
+
telegram_service=self.container.resolve(TelegramService),
|
|
286
|
+
accessory_driver=self.container.resolve(HomekitAccessoryDriver),
|
|
287
|
+
),
|
|
288
|
+
scope=punq.Scope.singleton,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
self.container.register(
|
|
292
|
+
HomekitApp,
|
|
293
|
+
factory=lambda: HomekitApp(
|
|
294
|
+
homekit_service=self.container.resolve(HomekitService)
|
|
295
|
+
),
|
|
296
|
+
scope=punq.Scope.singleton,
|
|
297
|
+
)
|
|
298
|
+
|
|
268
299
|
self.container.register(
|
|
269
300
|
ConbusEventRawService,
|
|
270
301
|
factory=lambda: ConbusEventRawService(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|