lghorizon 0.9.0__py3-none-any.whl → 0.9.0.dev1__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.
- lghorizon/__init__.py +23 -58
- lghorizon/{lghorizon_device_state_processor.py → device_state_processor.py} +34 -98
- lghorizon/{lghorizon_api.py → lghorizonapi.py} +27 -57
- lghorizon/{lghorizon_message_factory.py → message_factory.py} +2 -1
- lghorizon/{lghorizon_recording_factory.py → recording_factory.py} +3 -17
- lghorizon-0.9.0.dev1.dist-info/METADATA +41 -0
- lghorizon-0.9.0.dev1.dist-info/RECORD +13 -0
- lghorizon/exceptions.py +0 -17
- lghorizon/lghorizon_device.py +0 -409
- lghorizon/lghorizon_models.py +0 -1512
- lghorizon/lghorizon_mqtt_client.py +0 -335
- lghorizon-0.9.0.dist-info/METADATA +0 -191
- lghorizon-0.9.0.dist-info/RECORD +0 -17
- {lghorizon-0.9.0.dist-info → lghorizon-0.9.0.dev1.dist-info}/WHEEL +0 -0
- {lghorizon-0.9.0.dist-info → lghorizon-0.9.0.dev1.dist-info}/licenses/LICENSE +0 -0
- {lghorizon-0.9.0.dist-info → lghorizon-0.9.0.dev1.dist-info}/top_level.txt +0 -0
lghorizon/__init__.py
CHANGED
|
@@ -1,71 +1,36 @@
|
|
|
1
1
|
"""Python client for LG Horizon."""
|
|
2
2
|
|
|
3
|
-
from .
|
|
4
|
-
from .
|
|
5
|
-
from .lghorizon_models import (
|
|
3
|
+
from .lghorizonapi import LGHorizonApi
|
|
4
|
+
from .models.lghorizon_auth import (
|
|
6
5
|
LGHorizonAuth,
|
|
7
|
-
LGHorizonChannel,
|
|
8
|
-
LGHorizonCustomer,
|
|
9
|
-
LGHorizonDeviceState,
|
|
10
|
-
LGHorizonProfile,
|
|
11
|
-
LGHorizonRecording,
|
|
12
|
-
LGHorizonRecordingList,
|
|
13
|
-
LGHorizonShowRecordingList,
|
|
14
|
-
LGHorizonRecordingSeason,
|
|
15
|
-
LGHorizonRecordingSingle,
|
|
16
|
-
LGHorizonRecordingShow,
|
|
17
|
-
LGHorizonRecordingQuota,
|
|
18
|
-
LGHorizonRecordingType,
|
|
19
|
-
LGHorizonUIStateType,
|
|
20
|
-
LGHorizonMessageType,
|
|
21
|
-
LGHorizonRunningState,
|
|
22
|
-
LGHorizonRecordingSource,
|
|
23
|
-
LGHorizonRecordingState,
|
|
24
|
-
LGHorizonSourceType,
|
|
25
|
-
LGHorizonPlayerState,
|
|
26
|
-
LGHorizonAppsState,
|
|
27
|
-
LGHorizonUIState,
|
|
28
|
-
LGHorizonProfileOptions,
|
|
29
|
-
LGHorizonServicesConfig,
|
|
30
6
|
)
|
|
31
|
-
from .exceptions import (
|
|
32
|
-
LGHorizonApiError,
|
|
33
|
-
LGHorizonApiConnectionError,
|
|
7
|
+
from .models.exceptions import (
|
|
34
8
|
LGHorizonApiUnauthorizedError,
|
|
9
|
+
LGHorizonApiConnectionError,
|
|
35
10
|
LGHorizonApiLockedError,
|
|
36
11
|
)
|
|
12
|
+
from .const import (
|
|
13
|
+
ONLINE_RUNNING,
|
|
14
|
+
ONLINE_STANDBY,
|
|
15
|
+
RECORDING_TYPE_SHOW,
|
|
16
|
+
RECORDING_TYPE_SEASON,
|
|
17
|
+
RECORDING_TYPE_SINGLE,
|
|
18
|
+
)
|
|
37
19
|
|
|
38
20
|
__all__ = [
|
|
39
21
|
"LGHorizonApi",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
22
|
+
"LGHorizonBox",
|
|
23
|
+
"LGHorizonRecordingListSeasonShow",
|
|
24
|
+
"LGHorizonRecordingSingle",
|
|
25
|
+
"LGHorizonRecordingShow",
|
|
26
|
+
"LGHorizonRecordingEpisode",
|
|
43
27
|
"LGHorizonCustomer",
|
|
44
|
-
"LGHorizonDeviceState",
|
|
45
|
-
"LGHorizonProfile",
|
|
46
|
-
"LGHorizonApiError",
|
|
47
|
-
"LGHorizonApiConnectionError",
|
|
48
28
|
"LGHorizonApiUnauthorizedError",
|
|
29
|
+
"LGHorizonApiConnectionError",
|
|
49
30
|
"LGHorizonApiLockedError",
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
"LGHorizonUIStateType",
|
|
57
|
-
"LGHorizonMessageType",
|
|
58
|
-
"LGHorizonRunningState",
|
|
59
|
-
"LGHorizonRecordingSource",
|
|
60
|
-
"LGHorizonRecordingState",
|
|
61
|
-
"LGHorizonSourceType",
|
|
62
|
-
"LGHorizonPlayerState",
|
|
63
|
-
"LGHorizonAppsState",
|
|
64
|
-
"LGHorizonUIState",
|
|
65
|
-
"LGHorizonProfileOptions",
|
|
66
|
-
"LGHorizonProfile",
|
|
67
|
-
"LGHorizonAuth",
|
|
68
|
-
"LGHorizonServicesConfig",
|
|
69
|
-
"LGHorizonRecording",
|
|
70
|
-
"LGHorizonShowRecordingList",
|
|
71
|
-
]
|
|
31
|
+
"ONLINE_RUNNING",
|
|
32
|
+
"ONLINE_STANDBY",
|
|
33
|
+
"RECORDING_TYPE_SHOW",
|
|
34
|
+
"RECORDING_TYPE_SEASON",
|
|
35
|
+
"RECORDING_TYPE_SINGLE",
|
|
36
|
+
] # noqa
|
|
@@ -3,34 +3,33 @@
|
|
|
3
3
|
import random
|
|
4
4
|
import json
|
|
5
5
|
import urllib.parse
|
|
6
|
-
from datetime import datetime as dt, timezone
|
|
7
|
-
|
|
8
|
-
import time
|
|
9
6
|
|
|
10
7
|
from typing import cast, Dict, Optional
|
|
11
8
|
|
|
12
|
-
from .
|
|
13
|
-
from .
|
|
14
|
-
from .
|
|
9
|
+
from .models.lghorizon_device_state import LGHorizonDeviceState, LGHorizonRunningState
|
|
10
|
+
from .models.lghorizon_message import LGHorizonStatusMessage, LGHorizonUIStatusMessage
|
|
11
|
+
from .models.lghorizon_sources import (
|
|
15
12
|
LGHorizonSourceType,
|
|
16
13
|
LGHorizonLinearSource,
|
|
17
14
|
LGHorizonVODSource,
|
|
18
15
|
LGHorizonReplaySource,
|
|
19
16
|
LGHorizonNDVRSource,
|
|
20
17
|
LGHorizonReviewBufferSource,
|
|
21
|
-
LGHorizonRecordingSource,
|
|
22
18
|
)
|
|
23
|
-
from .
|
|
24
|
-
from .
|
|
19
|
+
from .models.lghorizon_auth import LGHorizonAuth
|
|
20
|
+
from .models.lghorizon_events import (
|
|
21
|
+
LGHorizonReplayEvent,
|
|
22
|
+
LGHorizonVOD,
|
|
23
|
+
)
|
|
25
24
|
|
|
26
|
-
from .
|
|
27
|
-
from .
|
|
28
|
-
from .
|
|
25
|
+
from .models.lghorizon_recordings import LGHorizonRecordingSingle
|
|
26
|
+
from .models.lghorizon_channel import LGHorizonChannel
|
|
27
|
+
from .models.lghorizon_ui_status import (
|
|
29
28
|
LGHorizonUIStateType,
|
|
30
29
|
LGHorizonAppsState,
|
|
31
30
|
LGHorizonPlayerState,
|
|
32
31
|
)
|
|
33
|
-
from .
|
|
32
|
+
from .models.lghorizon_customer import LGHorizonCustomer
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
class LGHorizonDeviceStateProcessor:
|
|
@@ -100,9 +99,6 @@ class LGHorizonDeviceStateProcessor:
|
|
|
100
99
|
return
|
|
101
100
|
await device_state.reset()
|
|
102
101
|
device_state.source_type = player_state.source_type
|
|
103
|
-
device_state.ui_state_type = LGHorizonUIStateType.MAINUI
|
|
104
|
-
device_state.speed = player_state.speed
|
|
105
|
-
|
|
106
102
|
match player_state.source_type:
|
|
107
103
|
case LGHorizonSourceType.LINEAR:
|
|
108
104
|
await self._process_linear_state(device_state, player_state)
|
|
@@ -120,10 +116,9 @@ class LGHorizonDeviceStateProcessor:
|
|
|
120
116
|
device_state: LGHorizonDeviceState,
|
|
121
117
|
apps_state: LGHorizonAppsState,
|
|
122
118
|
) -> None:
|
|
123
|
-
device_state.
|
|
124
|
-
device_state.
|
|
119
|
+
device_state.channel_id = apps_state.id
|
|
120
|
+
device_state.title = apps_state.app_name
|
|
125
121
|
device_state.image = apps_state.logo_path
|
|
126
|
-
device_state.ui_state_type = LGHorizonUIStateType.APPS
|
|
127
122
|
|
|
128
123
|
async def _process_linear_state(
|
|
129
124
|
self,
|
|
@@ -145,22 +140,12 @@ class LGHorizonDeviceStateProcessor:
|
|
|
145
140
|
service_path,
|
|
146
141
|
)
|
|
147
142
|
replay_event = LGHorizonReplayEvent(event_json)
|
|
148
|
-
device_state.id = replay_event.event_id
|
|
149
143
|
channel = self._channels[replay_event.channel_id]
|
|
150
144
|
device_state.source_type = source.source_type
|
|
151
|
-
device_state.channel_id = channel.
|
|
145
|
+
device_state.channel_id = channel.channel_number
|
|
152
146
|
device_state.channel_name = channel.title
|
|
153
|
-
device_state.
|
|
154
|
-
device_state.
|
|
155
|
-
device_state.episode_number = replay_event.episode_number
|
|
156
|
-
device_state.show_title = replay_event.title
|
|
157
|
-
now_in_ms = int(time.time() * 1000)
|
|
158
|
-
|
|
159
|
-
device_state.last_position_update = int(time.time() * 1000)
|
|
160
|
-
device_state.start_time = replay_event.start_time
|
|
161
|
-
device_state.end_time = replay_event.end_time
|
|
162
|
-
device_state.duration = replay_event.end_time - replay_event.start_time
|
|
163
|
-
device_state.position = now_in_ms - int(replay_event.start_time * 1000)
|
|
147
|
+
device_state.title = replay_event.title
|
|
148
|
+
device_state.sub_title = replay_event.full_episode_title
|
|
164
149
|
|
|
165
150
|
# Add random number to url to force refresh
|
|
166
151
|
join_param = "?"
|
|
@@ -170,6 +155,7 @@ class LGHorizonDeviceStateProcessor:
|
|
|
170
155
|
f"{channel.stream_image}{join_param}{str(random.randrange(1000000))}"
|
|
171
156
|
)
|
|
172
157
|
device_state.image = image_url
|
|
158
|
+
await device_state.reset_progress()
|
|
173
159
|
|
|
174
160
|
async def _process_reviewbuffer_state(
|
|
175
161
|
self,
|
|
@@ -191,20 +177,13 @@ class LGHorizonDeviceStateProcessor:
|
|
|
191
177
|
service_path,
|
|
192
178
|
)
|
|
193
179
|
replay_event = LGHorizonReplayEvent(event_json)
|
|
194
|
-
device_state.id = replay_event.event_id
|
|
195
180
|
channel = self._channels[replay_event.channel_id]
|
|
196
181
|
device_state.source_type = source.source_type
|
|
197
|
-
device_state.channel_id = channel.
|
|
182
|
+
device_state.channel_id = channel.channel_number
|
|
198
183
|
device_state.channel_name = channel.title
|
|
199
|
-
device_state.
|
|
200
|
-
device_state.
|
|
201
|
-
|
|
202
|
-
device_state.show_title = replay_event.title
|
|
203
|
-
device_state.last_position_update = player_state.last_speed_change_time
|
|
204
|
-
device_state.position = player_state.relative_position
|
|
205
|
-
device_state.start_time = replay_event.start_time
|
|
206
|
-
device_state.end_time = replay_event.end_time
|
|
207
|
-
device_state.duration = replay_event.end_time - replay_event.start_time
|
|
184
|
+
device_state.title = replay_event.title
|
|
185
|
+
device_state.sub_title = replay_event.full_episode_title
|
|
186
|
+
|
|
208
187
|
# Add random number to url to force refresh
|
|
209
188
|
join_param = "?"
|
|
210
189
|
if join_param in channel.stream_image:
|
|
@@ -213,6 +192,7 @@ class LGHorizonDeviceStateProcessor:
|
|
|
213
192
|
f"{channel.stream_image}{join_param}{str(random.randrange(1000000))}"
|
|
214
193
|
)
|
|
215
194
|
device_state.image = image_url
|
|
195
|
+
await device_state.reset_progress()
|
|
216
196
|
|
|
217
197
|
async def _process_replay_state(
|
|
218
198
|
self,
|
|
@@ -234,27 +214,15 @@ class LGHorizonDeviceStateProcessor:
|
|
|
234
214
|
service_path,
|
|
235
215
|
)
|
|
236
216
|
replay_event = LGHorizonReplayEvent(event_json)
|
|
237
|
-
device_state.id = replay_event.event_id
|
|
238
|
-
# Iets met buffer doen
|
|
239
|
-
channel = self._channels[replay_event.channel_id]
|
|
240
|
-
padding = channel.replay_pre_padding + channel.replay_post_padding
|
|
241
217
|
device_state.source_type = source.source_type
|
|
242
|
-
device_state.channel_id =
|
|
243
|
-
device_state.
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
device_state.last_position_update = int(time.time() * 1000)
|
|
248
|
-
device_state.start_time = replay_event.start_time
|
|
249
|
-
device_state.end_time = replay_event.end_time
|
|
250
|
-
device_state.duration = (
|
|
251
|
-
replay_event.end_time - replay_event.start_time + padding
|
|
252
|
-
)
|
|
253
|
-
device_state.position = (
|
|
254
|
-
player_state.relative_position + channel.replay_pre_padding
|
|
255
|
-
)
|
|
218
|
+
device_state.channel_id = None
|
|
219
|
+
device_state.title = replay_event.title
|
|
220
|
+
if replay_event.full_episode_title:
|
|
221
|
+
device_state.sub_title = replay_event.full_episode_title
|
|
222
|
+
|
|
256
223
|
# Add random number to url to force refresh
|
|
257
224
|
device_state.image = await self._get_intent_image_url(replay_event.event_id)
|
|
225
|
+
await device_state.reset_progress()
|
|
258
226
|
|
|
259
227
|
async def _process_vod_state(
|
|
260
228
|
self,
|
|
@@ -276,20 +244,11 @@ class LGHorizonDeviceStateProcessor:
|
|
|
276
244
|
service_path,
|
|
277
245
|
)
|
|
278
246
|
vod = LGHorizonVOD(vod_json)
|
|
279
|
-
device_state.
|
|
280
|
-
|
|
281
|
-
device_state.show_title = vod.series_title
|
|
282
|
-
device_state.episode_title = vod.title
|
|
283
|
-
device_state.season_number = vod.season
|
|
284
|
-
device_state.episode_number = vod.episode
|
|
285
|
-
else:
|
|
286
|
-
device_state.show_title = vod.title
|
|
287
|
-
|
|
247
|
+
device_state.title = vod.title
|
|
248
|
+
device_state.sub_title = vod.full_episode_title
|
|
288
249
|
device_state.duration = vod.duration
|
|
289
|
-
device_state.last_position_update = int(time.time() * 1000)
|
|
290
|
-
device_state.position = player_state.relative_position
|
|
291
|
-
|
|
292
250
|
device_state.image = await self._get_intent_image_url(vod.id)
|
|
251
|
+
await device_state.reset_progress()
|
|
293
252
|
|
|
294
253
|
async def _process_ndvr_state(
|
|
295
254
|
self, device_state: LGHorizonDeviceState, player_state: LGHorizonPlayerState
|
|
@@ -308,36 +267,13 @@ class LGHorizonDeviceStateProcessor:
|
|
|
308
267
|
service_path,
|
|
309
268
|
)
|
|
310
269
|
recording = LGHorizonRecordingSingle(recording_json)
|
|
311
|
-
device_state.
|
|
270
|
+
device_state.title = recording.title
|
|
271
|
+
device_state.sub_title = recording.full_episode_title
|
|
312
272
|
device_state.channel_id = recording.channel_id
|
|
313
273
|
if recording.channel_id:
|
|
314
274
|
channel = self._channels[recording.channel_id]
|
|
315
275
|
device_state.channel_name = channel.title
|
|
316
276
|
|
|
317
|
-
device_state.episode_title = recording.episode_title
|
|
318
|
-
device_state.season_number = recording.season_number
|
|
319
|
-
device_state.episode_number = recording.episode_number
|
|
320
|
-
device_state.last_position_update = player_state.last_speed_change_time
|
|
321
|
-
device_state.position = player_state.relative_position
|
|
322
|
-
if recording.start_time:
|
|
323
|
-
device_state.start_time = int(
|
|
324
|
-
dt.fromisoformat(
|
|
325
|
-
recording.start_time.replace("Z", "+00:00")
|
|
326
|
-
).timestamp()
|
|
327
|
-
)
|
|
328
|
-
if recording.end_time:
|
|
329
|
-
device_state.end_time = int(
|
|
330
|
-
dt.fromisoformat(recording.end_time.replace("Z", "+00:00")).timestamp()
|
|
331
|
-
)
|
|
332
|
-
if recording.start_time and recording.end_time:
|
|
333
|
-
device_state.duration = device_state.end_time - device_state.start_time
|
|
334
|
-
if recording.source == LGHorizonRecordingSource.SHOW:
|
|
335
|
-
device_state.show_title = recording.title
|
|
336
|
-
else:
|
|
337
|
-
device_state.show_title = recording.show_title
|
|
338
|
-
|
|
339
|
-
device_state.image = await self._get_intent_image_url(recording.id)
|
|
340
|
-
|
|
341
277
|
async def _get_intent_image_url(self, intent_id: str) -> Optional[str]:
|
|
342
278
|
"""Get intent image url."""
|
|
343
279
|
service_config = await self._auth.get_service_config()
|
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
"""LG Horizon API client."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from typing import Any, Dict, cast
|
|
5
|
-
|
|
6
|
-
from .lghorizon_device import LGHorizonDevice
|
|
7
|
-
from .
|
|
8
|
-
from .
|
|
9
|
-
from .
|
|
10
|
-
from .lghorizon_mqtt_client import LGHorizonMqttClient
|
|
11
|
-
from .
|
|
12
|
-
from .
|
|
13
|
-
from .
|
|
14
|
-
from .
|
|
15
|
-
from .
|
|
16
|
-
from .
|
|
17
|
-
from .
|
|
18
|
-
from .
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
LGHorizonShowRecordingList,
|
|
22
|
-
)
|
|
23
|
-
from .lghorizon_recording_factory import LGHorizonRecordingFactory
|
|
24
|
-
from .lghorizon_device_state_processor import LGHorizonDeviceStateProcessor
|
|
4
|
+
from typing import Any, Dict, cast
|
|
5
|
+
|
|
6
|
+
from .models.lghorizon_device import LGHorizonDevice
|
|
7
|
+
from .models.lghorizon_channel import LGHorizonChannel
|
|
8
|
+
from .models.lghorizon_auth import LGHorizonAuth
|
|
9
|
+
from .models.lghorizon_customer import LGHorizonCustomer
|
|
10
|
+
from .models.lghorizon_mqtt_client import LGHorizonMqttClient
|
|
11
|
+
from .models.lghorizon_config import LGHorizonServicesConfig
|
|
12
|
+
from .models.lghorizon_entitlements import LGHorizonEntitlements
|
|
13
|
+
from .models.lghorizon_profile import LGHorizonProfile
|
|
14
|
+
from .models.lghorizon_message import LGHorizonMessageType
|
|
15
|
+
from .message_factory import LGHorizonMessageFactory
|
|
16
|
+
from .models.lghorizon_message import LGHorizonStatusMessage, LGHorizonUIStatusMessage
|
|
17
|
+
from .models.lghorizon_device_state import LGHorizonRunningState
|
|
18
|
+
from .models.lghorizon_recordings import LGHorizonRecordingList, LGHorizonRecordingQuota
|
|
19
|
+
from .recording_factory import LGHorizonRecordingFactory
|
|
20
|
+
from .device_state_processor import LGHorizonDeviceStateProcessor
|
|
25
21
|
|
|
26
22
|
|
|
27
23
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -30,7 +26,7 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
30
26
|
class LGHorizonApi:
|
|
31
27
|
"""LG Horizon API client."""
|
|
32
28
|
|
|
33
|
-
_mqtt_client: LGHorizonMqttClient
|
|
29
|
+
_mqtt_client: LGHorizonMqttClient
|
|
34
30
|
auth: LGHorizonAuth
|
|
35
31
|
_service_config: LGHorizonServicesConfig
|
|
36
32
|
_customer: LGHorizonCustomer
|
|
@@ -45,18 +41,10 @@ class LGHorizonApi:
|
|
|
45
41
|
|
|
46
42
|
def __init__(self, auth: LGHorizonAuth, profile_id: str = "") -> None:
|
|
47
43
|
"""Initialize LG Horizon API client."""
|
|
48
|
-
"""Initialize LG Horizon API client.
|
|
49
|
-
|
|
50
|
-
Args:
|
|
51
|
-
auth: The authentication object for API requests.
|
|
52
|
-
profile_id: The ID of the user profile to use (optional).
|
|
53
|
-
"""
|
|
54
44
|
self.auth = auth
|
|
55
45
|
self._profile_id = profile_id
|
|
56
46
|
self._channels = {}
|
|
57
47
|
self._device_state_processor = None
|
|
58
|
-
self._mqtt_client = None
|
|
59
|
-
self._initialized = False
|
|
60
48
|
|
|
61
49
|
async def initialize(self) -> None:
|
|
62
50
|
"""Initialize the API client."""
|
|
@@ -74,20 +62,14 @@ class LGHorizonApi:
|
|
|
74
62
|
)
|
|
75
63
|
self._initialized = True
|
|
76
64
|
|
|
77
|
-
async def
|
|
78
|
-
self, token_refresh_callback: Callable[str, None]
|
|
79
|
-
) -> None:
|
|
80
|
-
"""Set the token refresh callback."""
|
|
81
|
-
self.auth.token_refresh_callback = token_refresh_callback
|
|
82
|
-
|
|
83
|
-
async def get_devices(self) -> dict[str, LGHorizonDevice]:
|
|
65
|
+
async def get_devices(self) -> Dict[str, LGHorizonDevice]:
|
|
84
66
|
"""Get devices."""
|
|
85
67
|
if not self._initialized:
|
|
86
68
|
raise RuntimeError("LGHorizonApi not initialized")
|
|
87
69
|
|
|
88
70
|
return self._devices
|
|
89
71
|
|
|
90
|
-
async def get_profiles(self) ->
|
|
72
|
+
async def get_profiles(self) -> Dict[str, LGHorizonProfile]:
|
|
91
73
|
"""Get profile IDs."""
|
|
92
74
|
if not self._initialized:
|
|
93
75
|
raise RuntimeError("LGHorizonApi not initialized")
|
|
@@ -95,16 +77,14 @@ class LGHorizonApi:
|
|
|
95
77
|
return self._customer.profiles
|
|
96
78
|
|
|
97
79
|
async def get_profile_channels(
|
|
98
|
-
self, profile_id:
|
|
80
|
+
self, profile_id: str
|
|
99
81
|
) -> Dict[str, LGHorizonChannel]:
|
|
100
82
|
"""Returns channels to display baed on profile."""
|
|
101
83
|
# Attempt to retrieve the profile by the given profile_id
|
|
102
|
-
if not profile_id:
|
|
103
|
-
profile_id = self._profile_id
|
|
104
84
|
profile = self._customer.profiles.get(profile_id)
|
|
105
85
|
|
|
106
86
|
# If the specified profile is not found, and there are other profiles available,
|
|
107
|
-
# default to the first profile in the customer's list
|
|
87
|
+
# default to the first profile in the customer's list.
|
|
108
88
|
if not profile and self._customer.profiles:
|
|
109
89
|
_LOGGER.debug(
|
|
110
90
|
"Profile with ID '%s' not found. Defaulting to first available profile.",
|
|
@@ -155,10 +135,6 @@ class LGHorizonApi:
|
|
|
155
135
|
self._initialized = False
|
|
156
136
|
|
|
157
137
|
async def _create_mqtt_client(self) -> LGHorizonMqttClient:
|
|
158
|
-
"""Create and configure the MQTT client.
|
|
159
|
-
|
|
160
|
-
Returns: An initialized LGHorizonMqttClient instance.
|
|
161
|
-
"""
|
|
162
138
|
mqtt_client = await LGHorizonMqttClient.create(
|
|
163
139
|
self.auth,
|
|
164
140
|
self._on_mqtt_connected,
|
|
@@ -168,10 +144,8 @@ class LGHorizonApi:
|
|
|
168
144
|
|
|
169
145
|
async def _on_mqtt_connected(self):
|
|
170
146
|
"""MQTT connected callback."""
|
|
171
|
-
await self._mqtt_client.subscribe("#")
|
|
172
147
|
await self._mqtt_client.subscribe(self.auth.household_id)
|
|
173
148
|
# await self._mqtt_client.subscribe(self.auth.household_id + "/#")
|
|
174
|
-
# await self._mqtt_client.subscribe(self.auth.household_id + "/+/#")
|
|
175
149
|
await self._mqtt_client.subscribe(
|
|
176
150
|
self.auth.household_id + "/" + self._mqtt_client.client_id
|
|
177
151
|
)
|
|
@@ -203,16 +177,12 @@ class LGHorizonApi:
|
|
|
203
177
|
case LGHorizonMessageType.STATUS:
|
|
204
178
|
message.__class__ = LGHorizonStatusMessage
|
|
205
179
|
status_message = cast(LGHorizonStatusMessage, message)
|
|
206
|
-
device = self._devices
|
|
207
|
-
if not device:
|
|
208
|
-
return
|
|
180
|
+
device = self._devices[status_message.source]
|
|
209
181
|
await device.handle_status_message(status_message)
|
|
210
182
|
case LGHorizonMessageType.UI_STATUS:
|
|
211
183
|
message.__class__ = LGHorizonUIStatusMessage
|
|
212
184
|
ui_status_message = cast(LGHorizonUIStatusMessage, message)
|
|
213
|
-
device = self._devices
|
|
214
|
-
if not device:
|
|
215
|
-
return
|
|
185
|
+
device = self._devices[ui_status_message.source]
|
|
216
186
|
if (
|
|
217
187
|
not device.device_state.state
|
|
218
188
|
== LGHorizonRunningState.ONLINE_RUNNING
|
|
@@ -220,7 +190,7 @@ class LGHorizonApi:
|
|
|
220
190
|
return
|
|
221
191
|
await device.handle_ui_status_message(ui_status_message)
|
|
222
192
|
|
|
223
|
-
async def _get_customer_info(self) ->
|
|
193
|
+
async def _get_customer_info(self) -> Any:
|
|
224
194
|
service_url = await self._service_config.get_service_url(
|
|
225
195
|
"personalizationService"
|
|
226
196
|
)
|
|
@@ -274,14 +244,14 @@ class LGHorizonApi:
|
|
|
274
244
|
|
|
275
245
|
async def get_show_recordings(
|
|
276
246
|
self, show_id: str, channel_id: str
|
|
277
|
-
) ->
|
|
247
|
+
) -> LGHorizonRecordingList:
|
|
278
248
|
"""Retrieve all recordings."""
|
|
279
249
|
_LOGGER.debug("Retrieving recordings fro show...")
|
|
280
250
|
service_url = await self._service_config.get_service_url("recordingService")
|
|
281
251
|
lang = await self._customer.get_profile_lang(self._profile_id)
|
|
282
252
|
episodes_json = await self.auth.request(
|
|
283
253
|
service_url,
|
|
284
|
-
f"/customers/
|
|
254
|
+
f"/customers/8436830_nl/episodes/shows/{show_id}?source=recording&isAdult=false&offset=0&limit=100&profileId={self._profile_id}&language={lang}&channelId={channel_id}&sort=time&sortOrder=asc",
|
|
285
255
|
)
|
|
286
256
|
recordings = await self._recording_factory.create_episodes(episodes_json)
|
|
287
257
|
return recordings
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"LG Horizon Message Factory."
|
|
2
2
|
|
|
3
|
-
from .
|
|
3
|
+
from .models.lghorizon_message import (
|
|
4
4
|
LGHorizonMessage,
|
|
5
5
|
LGHorizonStatusMessage,
|
|
6
6
|
LGHorizonUnknownMessage,
|
|
@@ -22,6 +22,7 @@ class LGHorizonMessageFactory:
|
|
|
22
22
|
case LGHorizonMessageType.STATUS:
|
|
23
23
|
return LGHorizonStatusMessage(payload, topic)
|
|
24
24
|
case LGHorizonMessageType.UI_STATUS:
|
|
25
|
+
# Placeholder for UI_STATUS message handling
|
|
25
26
|
return LGHorizonUIStatusMessage(payload, topic)
|
|
26
27
|
case LGHorizonMessageType.UNKNOWN:
|
|
27
28
|
return LGHorizonUnknownMessage(payload, topic)
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
from
|
|
2
|
-
from .lghorizon_models import (
|
|
1
|
+
from .models.lghorizon_recordings import (
|
|
3
2
|
LGHorizonRecordingList,
|
|
4
3
|
LGHorizonRecordingSingle,
|
|
5
4
|
LGHorizonRecordingSeason,
|
|
6
5
|
LGHorizonRecordingShow,
|
|
7
6
|
LGHorizonRecordingType,
|
|
8
|
-
LGHorizonShowRecordingList,
|
|
9
7
|
)
|
|
10
8
|
|
|
11
9
|
|
|
@@ -34,22 +32,10 @@ class LGHorizonRecordingFactory:
|
|
|
34
32
|
|
|
35
33
|
return LGHorizonRecordingList(recording_list)
|
|
36
34
|
|
|
37
|
-
async def create_episodes(self, episode_json: dict) ->
|
|
35
|
+
async def create_episodes(self, episode_json: dict) -> LGHorizonRecordingList:
|
|
38
36
|
"""Create a LGHorizonRecording list based for episodes."""
|
|
39
37
|
recording_list = []
|
|
40
|
-
show_title: Optional[str] = None
|
|
41
|
-
if "images" in episode_json:
|
|
42
|
-
images = episode_json["images"]
|
|
43
|
-
show_image = next(
|
|
44
|
-
(img["url"] for img in images if img.get("type") == "titleTreatment"),
|
|
45
|
-
images[0]["url"] if images else None,
|
|
46
|
-
)
|
|
47
|
-
else:
|
|
48
|
-
show_image = None
|
|
49
|
-
|
|
50
38
|
for recording in episode_json["data"]:
|
|
51
39
|
recording_single = LGHorizonRecordingSingle(recording)
|
|
52
|
-
if show_title is None:
|
|
53
|
-
show_title = recording_single.show_title or recording_single.title
|
|
54
40
|
recording_list.append(recording_single)
|
|
55
|
-
return
|
|
41
|
+
return LGHorizonRecordingList(recording_list)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lghorizon
|
|
3
|
+
Version: 0.9.0.dev1
|
|
4
|
+
Summary: Python client for Liberty Global Horizon settop boxes
|
|
5
|
+
Home-page: https://github.com/sholofly/LGHorizon-python
|
|
6
|
+
Author: Rudolf Offereins
|
|
7
|
+
Author-email: r.offereins@gmail.com
|
|
8
|
+
License: MIT license
|
|
9
|
+
Keywords: LG,Horizon,API,Settop box
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Natural Language :: English
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: paho-mqtt
|
|
24
|
+
Requires-Dist: requests>=2.22.0
|
|
25
|
+
Requires-Dist: backoff>=1.9.0
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: author-email
|
|
28
|
+
Dynamic: classifier
|
|
29
|
+
Dynamic: description
|
|
30
|
+
Dynamic: description-content-type
|
|
31
|
+
Dynamic: home-page
|
|
32
|
+
Dynamic: keywords
|
|
33
|
+
Dynamic: license
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
Dynamic: requires-dist
|
|
36
|
+
Dynamic: requires-python
|
|
37
|
+
Dynamic: summary
|
|
38
|
+
|
|
39
|
+
# LG Horizon Api
|
|
40
|
+
|
|
41
|
+
Python library to control multiple LG Horizon boxes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
lghorizon/__init__.py,sha256=Hv5lGe6Nb0OlkugLpNA0gyIPFRHOGkdnUvhLS0Sn0eA,863
|
|
2
|
+
lghorizon/const.py,sha256=HINlbyevEN9ZRnfIBbSGNc6i9J8WkIgpqkLZrwyqpGQ,5307
|
|
3
|
+
lghorizon/device_state_processor.py,sha256=MuItTJoXDUeSXHW5QjmPjbdQ72JFBGLWfuo6iMKOTYk,12141
|
|
4
|
+
lghorizon/helpers.py,sha256=SGlEN6V0kh2vqw1qCKmM1KhfeO-UvPyyQmnThgFLFhs,272
|
|
5
|
+
lghorizon/lghorizonapi.py,sha256=eZmcIlbA_Xgtcq65ShVSiBXAapFdAVUIxYuBOKVV8h8,11841
|
|
6
|
+
lghorizon/message_factory.py,sha256=Bk0k6fZSJNgpmcN_IxPL26lwc6V1fCbQe7acd65Km0g,1504
|
|
7
|
+
lghorizon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
lghorizon/recording_factory.py,sha256=xcEVzchn2SSV1ojRo9N17VfaEDlD9AaYLEo92uNNgaA,1766
|
|
9
|
+
lghorizon-0.9.0.dev1.dist-info/licenses/LICENSE,sha256=6Dh2tur1gMX3r3rITjVwUONBEJxyyPZDY8p6DZXtimE,1059
|
|
10
|
+
lghorizon-0.9.0.dev1.dist-info/METADATA,sha256=zwM_v9XJf8B7v5H32jxRv3Qpt_aphskTKturNLIcnk4,1287
|
|
11
|
+
lghorizon-0.9.0.dev1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
+
lghorizon-0.9.0.dev1.dist-info/top_level.txt,sha256=usii76_AxGfPI6gjrrh-NyZxcQQuF1B8_Q9kd7sID8Q,10
|
|
13
|
+
lghorizon-0.9.0.dev1.dist-info/RECORD,,
|
lghorizon/exceptions.py
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"""Exceptions for the LGHorizon API."""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class LGHorizonApiError(Exception):
|
|
5
|
-
"""Generic LGHorizon exception."""
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class LGHorizonApiConnectionError(LGHorizonApiError):
|
|
9
|
-
"""Exception for connection-related errors with the LG Horizon API."""
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class LGHorizonApiUnauthorizedError(Exception):
|
|
13
|
-
"""Exception for unauthorized access to the LG Horizon API."""
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class LGHorizonApiLockedError(LGHorizonApiUnauthorizedError):
|
|
17
|
-
"""Exception for locked account errors with the LG Horizon API."""
|