lghorizon 0.9.0__py3-none-any.whl → 0.9.0b0__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 +2 -67
- lghorizon/exceptions.py +3 -3
- lghorizon/lghorizon_api.py +11 -41
- lghorizon/lghorizon_device.py +28 -101
- lghorizon/lghorizon_device_state_processor.py +26 -90
- lghorizon/lghorizon_message_factory.py +1 -0
- lghorizon/lghorizon_models.py +69 -250
- lghorizon/lghorizon_mqtt_client.py +66 -278
- lghorizon/lghorizon_recording_factory.py +2 -16
- lghorizon-0.9.0b0.dist-info/METADATA +41 -0
- lghorizon-0.9.0b0.dist-info/RECORD +17 -0
- 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.0b0.dist-info}/WHEEL +0 -0
- {lghorizon-0.9.0.dist-info → lghorizon-0.9.0b0.dist-info}/licenses/LICENSE +0 -0
- {lghorizon-0.9.0.dist-info → lghorizon-0.9.0b0.dist-info}/top_level.txt +0 -0
lghorizon/__init__.py
CHANGED
|
@@ -2,70 +2,5 @@
|
|
|
2
2
|
|
|
3
3
|
from .lghorizon_api import LGHorizonApi
|
|
4
4
|
from .lghorizon_device import LGHorizonDevice
|
|
5
|
-
from .lghorizon_models import
|
|
6
|
-
|
|
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
|
-
)
|
|
31
|
-
from .exceptions import (
|
|
32
|
-
LGHorizonApiError,
|
|
33
|
-
LGHorizonApiConnectionError,
|
|
34
|
-
LGHorizonApiUnauthorizedError,
|
|
35
|
-
LGHorizonApiLockedError,
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
__all__ = [
|
|
39
|
-
"LGHorizonApi",
|
|
40
|
-
"LGHorizonDevice",
|
|
41
|
-
"LGHorizonAuth",
|
|
42
|
-
"LGHorizonChannel",
|
|
43
|
-
"LGHorizonCustomer",
|
|
44
|
-
"LGHorizonDeviceState",
|
|
45
|
-
"LGHorizonProfile",
|
|
46
|
-
"LGHorizonApiError",
|
|
47
|
-
"LGHorizonApiConnectionError",
|
|
48
|
-
"LGHorizonApiUnauthorizedError",
|
|
49
|
-
"LGHorizonApiLockedError",
|
|
50
|
-
"LGHorizonRecordingList",
|
|
51
|
-
"LGHorizonRecordingSeason",
|
|
52
|
-
"LGHorizonRecordingSingle",
|
|
53
|
-
"LGHorizonRecordingShow",
|
|
54
|
-
"LGHorizonRecordingQuota",
|
|
55
|
-
"LGHorizonRecordingType",
|
|
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
|
-
]
|
|
5
|
+
from .lghorizon_models import *
|
|
6
|
+
from .exceptions import *
|
lghorizon/exceptions.py
CHANGED
|
@@ -6,12 +6,12 @@ class LGHorizonApiError(Exception):
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class LGHorizonApiConnectionError(LGHorizonApiError):
|
|
9
|
-
"""
|
|
9
|
+
"""Generic LGHorizon exception."""
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class LGHorizonApiUnauthorizedError(Exception):
|
|
13
|
-
"""
|
|
13
|
+
"""Generic LGHorizon exception."""
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class LGHorizonApiLockedError(LGHorizonApiUnauthorizedError):
|
|
17
|
-
"""
|
|
17
|
+
"""Generic LGHorizon exception."""
|
lghorizon/lghorizon_api.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""LG Horizon API client."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from typing import Any, Dict, cast
|
|
4
|
+
from typing import Any, Dict, cast
|
|
5
5
|
|
|
6
6
|
from .lghorizon_device import LGHorizonDevice
|
|
7
7
|
from .lghorizon_models import LGHorizonChannel
|
|
@@ -15,11 +15,7 @@ from .lghorizon_models import LGHorizonMessageType
|
|
|
15
15
|
from .lghorizon_message_factory import LGHorizonMessageFactory
|
|
16
16
|
from .lghorizon_models import LGHorizonStatusMessage, LGHorizonUIStatusMessage
|
|
17
17
|
from .lghorizon_models import LGHorizonRunningState
|
|
18
|
-
from .lghorizon_models import
|
|
19
|
-
LGHorizonRecordingList,
|
|
20
|
-
LGHorizonRecordingQuota,
|
|
21
|
-
LGHorizonShowRecordingList,
|
|
22
|
-
)
|
|
18
|
+
from .lghorizon_models import LGHorizonRecordingList, LGHorizonRecordingQuota
|
|
23
19
|
from .lghorizon_recording_factory import LGHorizonRecordingFactory
|
|
24
20
|
from .lghorizon_device_state_processor import LGHorizonDeviceStateProcessor
|
|
25
21
|
|
|
@@ -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
|
|
@@ -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
|
lghorizon/lghorizon_device.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""LG Horizon Device."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
import json
|
|
6
6
|
import logging
|
|
7
7
|
from typing import Any, Callable, Coroutine, Dict, Optional
|
|
@@ -146,6 +146,8 @@ class LGHorizonDevice:
|
|
|
146
146
|
|
|
147
147
|
async def register_mqtt(self) -> None:
|
|
148
148
|
"""Register the mqtt connection."""
|
|
149
|
+
if not self._mqtt_client.is_connected:
|
|
150
|
+
raise LGHorizonApiConnectionError("MQTT client not connected.")
|
|
149
151
|
topic = f"{self._auth.household_id}/{self._mqtt_client.client_id}/status"
|
|
150
152
|
payload = {
|
|
151
153
|
"source": self._mqtt_client.client_id,
|
|
@@ -157,23 +159,14 @@ class LGHorizonDevice:
|
|
|
157
159
|
async def set_callback(
|
|
158
160
|
self, change_callback: Callable[[str], Coroutine[Any, Any, Any]]
|
|
159
161
|
) -> None:
|
|
160
|
-
"""Set a callback function
|
|
161
|
-
|
|
162
|
-
Args:
|
|
163
|
-
change_callback: An asynchronous callable that takes the device ID
|
|
164
|
-
as an argument.
|
|
165
|
-
"""
|
|
162
|
+
"""Set a callback function."""
|
|
166
163
|
self._change_callback = change_callback
|
|
167
164
|
await self.register_mqtt() # type: ignore [assignment] # Callback can be None
|
|
168
165
|
|
|
169
166
|
async def handle_status_message(
|
|
170
167
|
self, status_message: LGHorizonStatusMessage
|
|
171
168
|
) -> None:
|
|
172
|
-
"""
|
|
173
|
-
|
|
174
|
-
Args:
|
|
175
|
-
status_message: The status message received from the device.
|
|
176
|
-
"""
|
|
169
|
+
"""Register a new settop box."""
|
|
177
170
|
old_running_state = self.device_state.state
|
|
178
171
|
new_running_state = status_message.running_state
|
|
179
172
|
if (
|
|
@@ -207,11 +200,7 @@ class LGHorizonDevice:
|
|
|
207
200
|
self.recording_capacity = payload["used"] # Use the setter
|
|
208
201
|
|
|
209
202
|
async def _trigger_callback(self):
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
This method is called when the device's state changes and a callback is set.
|
|
213
|
-
"""
|
|
214
|
-
if self._change_callback is not None:
|
|
203
|
+
if self._change_callback:
|
|
215
204
|
_LOGGER.debug("Callback called from box %s", self.device_id)
|
|
216
205
|
await self._change_callback(self.device_id)
|
|
217
206
|
|
|
@@ -278,100 +267,38 @@ class LGHorizonDevice:
|
|
|
278
267
|
if self._device_state.state == LGHorizonRunningState.ONLINE_RUNNING:
|
|
279
268
|
await self.send_key_to_box(MEDIA_KEY_RECORD)
|
|
280
269
|
|
|
281
|
-
async def set_player_position(self, position: int) -> None:
|
|
282
|
-
"""Set the player position on the settop box."""
|
|
283
|
-
payload = {
|
|
284
|
-
"source": self.device_id,
|
|
285
|
-
"type": "CPE.setPlayerPosition",
|
|
286
|
-
"runtimeType": "setPlayerposition",
|
|
287
|
-
"id": await make_id(),
|
|
288
|
-
"version": "1.3.11",
|
|
289
|
-
"status": {"relativePosition": position},
|
|
290
|
-
}
|
|
291
|
-
payload_str = json.dumps(payload)
|
|
292
|
-
await self._mqtt_client.publish_message(
|
|
293
|
-
f"{self._auth.household_id}/{self.device_id}", payload_str
|
|
294
|
-
)
|
|
295
|
-
|
|
296
|
-
async def display_message(self, sourceType: str, message: str) -> None:
|
|
297
|
-
"""Display a message on the set-top box and repeat it for longer visibility.
|
|
298
|
-
|
|
299
|
-
# We sturen de payload 3 keer met een kortere tussentijd
|
|
300
|
-
|
|
301
|
-
This method sends the message payload multiple times to ensure it stays
|
|
302
|
-
visible on the screen for a longer duration, as the display time for
|
|
303
|
-
such messages is typically short.
|
|
304
|
-
|
|
305
|
-
Args:
|
|
306
|
-
sourceType: The type of source for the message (e.g., "linear").
|
|
307
|
-
message: The message string to display.
|
|
308
|
-
"""
|
|
309
|
-
for i in range(3):
|
|
310
|
-
payload = {
|
|
311
|
-
"id": await make_id(8),
|
|
312
|
-
"type": "CPE.pushToTV",
|
|
313
|
-
"source": {
|
|
314
|
-
"clientId": self._mqtt_client.client_id,
|
|
315
|
-
"friendlyDeviceName": f"\n\n{message}",
|
|
316
|
-
},
|
|
317
|
-
"status": {
|
|
318
|
-
"sourceType": sourceType,
|
|
319
|
-
"source": {"channelId": "1234"},
|
|
320
|
-
"title": "Nieuwe melding",
|
|
321
|
-
"relativePosition": 0,
|
|
322
|
-
"speed": 1,
|
|
323
|
-
},
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
await self._mqtt_client.publish_message(
|
|
327
|
-
f"{self._auth.household_id}/{self.device_id}", json.dumps(payload)
|
|
328
|
-
)
|
|
329
|
-
|
|
330
|
-
# Omdat de melding 3 seconden blijft staan, wachten we 3 seconden
|
|
331
|
-
# voor de volgende 'refresh'.
|
|
332
|
-
if i < 2:
|
|
333
|
-
await asyncio.sleep(3)
|
|
334
|
-
|
|
335
270
|
async def set_channel(self, source: str) -> None:
|
|
336
271
|
"""Change te channel from the settopbox."""
|
|
337
272
|
channel = [src for src in self._channels.values() if src.title == source][0]
|
|
338
|
-
payload =
|
|
339
|
-
"id":
|
|
340
|
-
|
|
341
|
-
"source":
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
"relativePosition": 0,
|
|
349
|
-
"speed": 1,
|
|
350
|
-
},
|
|
351
|
-
}
|
|
273
|
+
payload = (
|
|
274
|
+
'{"id":"'
|
|
275
|
+
+ await make_id(8)
|
|
276
|
+
+ '","type":"CPE.pushToTV","source":{"clientId":"'
|
|
277
|
+
+ self._mqtt_client.client_id
|
|
278
|
+
+ '","friendlyDeviceName":"Home Assistant"},'
|
|
279
|
+
+ '"status":{"sourceType":"linear","source":{"channelId":"'
|
|
280
|
+
+ channel.id
|
|
281
|
+
+ '"},"relativePosition":0,"speed":1}}'
|
|
282
|
+
)
|
|
352
283
|
|
|
353
284
|
await self._mqtt_client.publish_message(
|
|
354
|
-
f"{self._auth.household_id}/{self.device_id}",
|
|
285
|
+
f"{self._auth.household_id}/{self.device_id}", payload
|
|
355
286
|
)
|
|
356
287
|
|
|
357
288
|
async def play_recording(self, recording_id):
|
|
358
289
|
"""Play recording."""
|
|
359
|
-
payload =
|
|
360
|
-
"id":
|
|
361
|
-
|
|
362
|
-
"source":
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
"relativePosition": 0,
|
|
370
|
-
},
|
|
371
|
-
}
|
|
372
|
-
|
|
290
|
+
payload = (
|
|
291
|
+
'{"id":"'
|
|
292
|
+
+ await make_id(8)
|
|
293
|
+
+ '","type":"CPE.pushToTV","source":{"clientId":"'
|
|
294
|
+
+ self._mqtt_client.client_id
|
|
295
|
+
+ '","friendlyDeviceName":"Home Assistant"},'
|
|
296
|
+
+ '"status":{"sourceType":"nDVR","source":{"recordingId":"'
|
|
297
|
+
+ recording_id
|
|
298
|
+
+ '"},"relativePosition":0}}'
|
|
299
|
+
)
|
|
373
300
|
await self._mqtt_client.publish_message(
|
|
374
|
-
f"{self._auth.household_id}/{self.device_id}",
|
|
301
|
+
f"{self._auth.household_id}/{self.device_id}", payload
|
|
375
302
|
)
|
|
376
303
|
|
|
377
304
|
async def send_key_to_box(self, key: str) -> None:
|
|
@@ -3,9 +3,6 @@
|
|
|
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
|
|
|
@@ -18,10 +15,12 @@ from .lghorizon_models import (
|
|
|
18
15
|
LGHorizonReplaySource,
|
|
19
16
|
LGHorizonNDVRSource,
|
|
20
17
|
LGHorizonReviewBufferSource,
|
|
21
|
-
LGHorizonRecordingSource,
|
|
22
18
|
)
|
|
23
19
|
from .lghorizon_models import LGHorizonAuth
|
|
24
|
-
from .lghorizon_models import
|
|
20
|
+
from .lghorizon_models import (
|
|
21
|
+
LGHorizonReplayEvent,
|
|
22
|
+
LGHorizonVOD,
|
|
23
|
+
)
|
|
25
24
|
|
|
26
25
|
from .lghorizon_models import LGHorizonRecordingSingle
|
|
27
26
|
from .lghorizon_models import LGHorizonChannel
|
|
@@ -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()
|
|
@@ -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)
|