lghorizon 0.8.6__py3-none-any.whl → 0.9.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.
lghorizon/models.py DELETED
@@ -1,762 +0,0 @@
1
- """Models for LGHorizon API."""
2
-
3
- # pylint: disable=broad-exception-caught
4
- # pylint: disable=broad-exception-raised
5
- from datetime import datetime
6
- from typing import Callable, Dict
7
- import json
8
- import logging
9
- import paho.mqtt.client as mqtt
10
-
11
- from .const import (
12
- BOX_PLAY_STATE_CHANNEL,
13
- ONLINE_STANDBY,
14
- ONLINE_RUNNING,
15
- MEDIA_KEY_POWER,
16
- MEDIA_KEY_PLAY_PAUSE,
17
- MEDIA_KEY_STOP,
18
- MEDIA_KEY_CHANNEL_UP,
19
- MEDIA_KEY_CHANNEL_DOWN,
20
- MEDIA_KEY_ENTER,
21
- MEDIA_KEY_REWIND,
22
- MEDIA_KEY_FAST_FORWARD,
23
- MEDIA_KEY_RECORD,
24
- RECORDING_TYPE_SEASON,
25
- )
26
-
27
- from .helpers import make_id
28
-
29
- _logger = logging.getLogger(__name__)
30
-
31
-
32
- class LGHorizonAuth:
33
- """Class to hold LGHorizon authentication."""
34
-
35
- household_id: str
36
- access_token: str
37
- refresh_token: str
38
- refresh_token_expiry: datetime
39
- username: str
40
- mqtt_token: str = None
41
- access_token: str = None
42
-
43
- def __init__(self):
44
- """Initialize a session."""
45
-
46
- def fill(self, auth_json) -> None:
47
- """Fill the object."""
48
- self.household_id = auth_json["householdId"]
49
- self.access_token = auth_json["accessToken"]
50
- self.refresh_token = auth_json["refreshToken"]
51
- self.username = auth_json["username"]
52
- try:
53
- self.refresh_token_expiry = datetime.fromtimestamp(
54
- auth_json["refreshTokenExpiry"]
55
- )
56
- except ValueError:
57
- # VM uses milliseconds for the expiry time.
58
- # If the year is too high to be valid, it assumes it's milliseconds and divides it
59
- self.refresh_token_expiry = datetime.fromtimestamp(
60
- auth_json["refreshTokenExpiry"] // 1000
61
- )
62
-
63
- def is_expired(self) -> bool:
64
- """Check if refresh token is expired."""
65
- return self.refresh_token_expiry
66
-
67
-
68
- class LGHorizonPlayingInfo:
69
- """Represent current state of a box."""
70
-
71
- channel_id: str = None
72
- title: str = None
73
- image: str = None
74
- source_type: str = None
75
- paused: bool = False
76
- channel_title: str = None
77
- duration: float = None
78
- position: float = None
79
- last_position_update: datetime = None
80
-
81
- def __init__(self):
82
- """Initialize the playing info."""
83
-
84
- def set_paused(self, paused: bool):
85
- """Set pause state."""
86
- self.paused = paused
87
-
88
- def set_channel(self, channel_id):
89
- """Set channel."""
90
- self.channel_id = channel_id
91
-
92
- def set_title(self, title):
93
- """Set title."""
94
- self.title = title
95
-
96
- def set_channel_title(self, title):
97
- """Set channel title."""
98
- self.channel_title = title
99
-
100
- def set_image(self, image):
101
- """Set image."""
102
- self.image = image
103
-
104
- def set_source_type(self, source_type):
105
- """Set source type."""
106
- self.source_type = source_type
107
-
108
- def set_duration(self, duration: float):
109
- """Set duration."""
110
- self.duration = duration
111
-
112
- def set_position(self, position: float):
113
- """Set position."""
114
- self.position = position
115
-
116
- def set_last_position_update(self, last_position_update: datetime):
117
- """Set last position update."""
118
- self.last_position_update = last_position_update
119
-
120
- def reset_progress(self):
121
- """Reset the progress."""
122
- self.last_position_update = None
123
- self.duration = None
124
- self.position = None
125
-
126
- def reset(self):
127
- """Reset the channel"""
128
- self.channel_id = None
129
- self.title = None
130
- self.image = None
131
- self.source_type = None
132
- self.paused = False
133
- self.channel_title = None
134
- self.reset_progress()
135
-
136
-
137
- class LGHorizonChannel:
138
- """Represent a channel."""
139
-
140
- id: str
141
- title: str
142
- stream_image: str
143
- logo_image: str
144
- channel_number: str
145
-
146
- def __init__(self, channel_json):
147
- """Initialize a channel."""
148
- self.id = channel_json["id"]
149
- self.title = channel_json["name"]
150
- self.stream_image = self.get_stream_image(channel_json)
151
- if "logo" in channel_json and "focused" in channel_json["logo"]:
152
- self.logo_image = channel_json["logo"]["focused"]
153
- else:
154
- self.logo_image = ""
155
- self.channel_number = channel_json["logicalChannelNumber"]
156
-
157
- def get_stream_image(self, channel_json) -> str:
158
- """Returns the stream image."""
159
- image_stream = channel_json["imageStream"]
160
- if "full" in image_stream:
161
- return image_stream["full"]
162
- if "small" in image_stream:
163
- return image_stream["small"]
164
- if "logo" in channel_json and "focused" in channel_json["logo"]:
165
- return channel_json["logo"]["focused"]
166
- return ""
167
-
168
-
169
- class LGHorizonReplayEvent:
170
- """LGhorizon replay event."""
171
-
172
- episode_number: int = None
173
- channel_id: str = None
174
- event_id: str = None
175
- season_number: int = None
176
- title: str = None
177
- episode_name: str = None
178
-
179
- def __init__(self, raw_json: str):
180
- self.channel_id = raw_json["channelId"]
181
- self.event_id = raw_json["eventId"]
182
- self.title = raw_json["title"]
183
- if "episodeName" in raw_json:
184
- self.episode_name = raw_json["episodeName"]
185
- if "episodeNumber" in raw_json:
186
- self.episode_number = raw_json["episodeNumber"]
187
- if "seasonNumber" in raw_json:
188
- self.season_number = raw_json["seasonNumber"]
189
-
190
-
191
- class LGHorizonBaseRecording:
192
- """LgHorizon base recording."""
193
-
194
- recording_id: str = None
195
- title: str = None
196
- image: str = None
197
- recording_type: str = None
198
- channel_id: str = None
199
-
200
- def __init__(
201
- self,
202
- recording_id: str,
203
- title: str,
204
- image: str,
205
- channel_id: str,
206
- recording_type: str,
207
- ) -> None:
208
- self.recording_id = recording_id
209
- self.title = title
210
- self.image = image
211
- self.channel_id = channel_id
212
- self.recording_type = recording_type
213
-
214
-
215
- class LGHorizonRecordingSingle(LGHorizonBaseRecording):
216
- """Represents a single recording."""
217
-
218
- season_number: int = None
219
- episode_number: int = None
220
-
221
- def __init__(self, recording_json):
222
- """Init the single recording."""
223
- poster_url = None
224
- if "poster" in recording_json and "url" in recording_json["poster"]:
225
- poster_url = recording_json["poster"]["url"]
226
- LGHorizonBaseRecording.__init__(
227
- self,
228
- recording_json["id"],
229
- recording_json["title"],
230
- poster_url,
231
- recording_json["channelId"],
232
- recording_json["type"],
233
- )
234
- if "seasonNumber" in recording_json:
235
- self.season_number = recording_json["seasonNumber"]
236
- if "episodeNumber" in recording_json:
237
- self.episode_number = recording_json["episodeNumber"]
238
-
239
-
240
- class LGHorizonRecordingEpisode:
241
- """Represents a single recording."""
242
-
243
- episode_id: str = None
244
- episode_title: str = None
245
- season_number: int = None
246
- episode_number: int = None
247
- show_title: str = None
248
- recording_state: str = None
249
- image: str = None
250
-
251
- def __init__(self, recording_json):
252
- """Init the single recording."""
253
- self.episode_id = recording_json["episodeId"]
254
- self.episode_title = recording_json["episodeTitle"]
255
- self.show_title = recording_json["showTitle"]
256
- self.recording_state = recording_json["recordingState"]
257
- if "seasonNumber" in recording_json:
258
- self.season_number = recording_json["seasonNumber"]
259
- if "episodeNumber" in recording_json:
260
- self.episode_number = recording_json["episodeNumber"]
261
- if "poster" in recording_json and "url" in recording_json["poster"]:
262
- self.image = recording_json["poster"]["url"]
263
-
264
-
265
- class LGHorizonRecordingShow:
266
- """Represents a single recording."""
267
-
268
- episode_id: str = None
269
- show_title: str = None
270
- season_number: int = None
271
- episode_number: int = None
272
- recording_state: str = None
273
- image: str = None
274
-
275
- def __init__(self, recording_json):
276
- """Init the single recording."""
277
- self.episode_id = recording_json["episodeId"]
278
- self.show_title = recording_json["showTitle"]
279
- self.recording_state = recording_json["recordingState"]
280
- if "seasonNumber" in recording_json:
281
- self.season_number = recording_json["seasonNumber"]
282
- if "episodeNumber" in recording_json:
283
- self.episode_number = recording_json["episodeNumber"]
284
- if "poster" in recording_json and "url" in recording_json["poster"]:
285
- self.image = recording_json["poster"]["url"]
286
-
287
-
288
- class LGHorizonRecordingListSeasonShow(LGHorizonBaseRecording):
289
- """LGHorizon Season show list."""
290
-
291
- show_id: str = None
292
-
293
- def __init__(self, recording_season_json):
294
- """Init the single recording."""
295
-
296
- LGHorizonBaseRecording.__init__(
297
- self,
298
- recording_season_json["id"],
299
- recording_season_json["title"],
300
- recording_season_json["poster"]["url"],
301
- recording_season_json["channelId"],
302
- recording_season_json["type"],
303
- )
304
- if self.recording_type == RECORDING_TYPE_SEASON:
305
- self.show_id = recording_season_json["showId"]
306
- else:
307
- self.show_id = recording_season_json["id"]
308
-
309
-
310
- class LGHorizonVod:
311
- """LGHorizon video on demand."""
312
-
313
- title: str = None
314
- image: str = None
315
- duration: float = None
316
-
317
- def __init__(self, vod_json) -> None:
318
- self.title = vod_json["title"]
319
- self.duration = vod_json["duration"]
320
-
321
-
322
- class LGHorizonApp:
323
- """LGHorizon App."""
324
-
325
- title: str = None
326
- image: str = None
327
-
328
- def __init__(self, app_state_json: str) -> None:
329
- self.title = app_state_json["appName"]
330
- self.image = app_state_json["logoPath"]
331
- if not self.image.startswith("http:"):
332
- self.image = "https:" + self.image
333
-
334
-
335
- class LGHorizonMqttClient:
336
- """LGHorizon MQTT client."""
337
-
338
- _broker_url: str = None
339
- _mqtt_client: mqtt.Client
340
- _auth: LGHorizonAuth
341
- client_id: str = None
342
- _on_connected_callback: Callable = None
343
- _on_message_callback: Callable[[str, str], None] = None
344
-
345
- @property
346
- def is_connected(self):
347
- """Is client connected."""
348
- return self._mqtt_client.is_connected
349
-
350
- def __init__(
351
- self,
352
- auth: LGHorizonAuth,
353
- mqtt_broker_url: str,
354
- on_connected_callback: Callable = None,
355
- on_message_callback: Callable[[str], None] = None,
356
- ):
357
- self._auth = auth
358
- self._broker_url = mqtt_broker_url.replace("wss://", "").replace(
359
- ":443/mqtt", ""
360
- )
361
- self.client_id = make_id()
362
- self._mqtt_client = mqtt.Client(
363
- client_id=self.client_id,
364
- transport="websockets",
365
- )
366
-
367
- self._mqtt_client.ws_set_options(
368
- headers={"Sec-WebSocket-Protocol": "mqtt, mqttv3.1, mqttv3.11"}
369
- )
370
- self._mqtt_client.username_pw_set(
371
- self._auth.household_id, self._auth.mqtt_token
372
- )
373
- self._mqtt_client.tls_set()
374
- self._mqtt_client.enable_logger(_logger)
375
- self._mqtt_client.on_connect = self._on_mqtt_connect
376
- self._on_connected_callback = on_connected_callback
377
- self._on_message_callback = on_message_callback
378
-
379
- def _on_mqtt_connect(self, client, userdata, flags, result_code): # pylint: disable=unused-argument
380
- if result_code == 0:
381
- self._mqtt_client.on_message = self._on_client_message
382
- self._mqtt_client.subscribe(self._auth.household_id)
383
- self._mqtt_client.subscribe(self._auth.household_id + "/#")
384
- self._mqtt_client.subscribe(self._auth.household_id + "/" + self.client_id)
385
- self._mqtt_client.subscribe(self._auth.household_id + "/+/status")
386
- self._mqtt_client.subscribe(
387
- self._auth.household_id + "/+/networkRecordings"
388
- )
389
- self._mqtt_client.subscribe(
390
- self._auth.household_id + "/+/networkRecordings/capacity"
391
- )
392
- self._mqtt_client.subscribe(self._auth.household_id + "/+/localRecordings")
393
- self._mqtt_client.subscribe(
394
- self._auth.household_id + "/+/localRecordings/capacity"
395
- )
396
- self._mqtt_client.subscribe(self._auth.household_id + "/watchlistService")
397
- self._mqtt_client.subscribe(self._auth.household_id + "/purchaseService")
398
- self._mqtt_client.subscribe(
399
- self._auth.household_id + "/personalizationService"
400
- )
401
- self._mqtt_client.subscribe(self._auth.household_id + "/recordingStatus")
402
- self._mqtt_client.subscribe(
403
- self._auth.household_id + "/recordingStatus/lastUserAction"
404
- )
405
- if self._on_connected_callback:
406
- self._on_connected_callback()
407
- elif result_code == 5:
408
- self._mqtt_client.username_pw_set(
409
- self._auth.household_id, self._auth.mqtt_token
410
- )
411
- self.connect()
412
- else:
413
- _logger.error(
414
- "Cannot connect to MQTT server with resultCode: %s", result_code
415
- )
416
-
417
- def connect(self) -> None:
418
- """Connect the client."""
419
- self._mqtt_client.connect(self._broker_url, 443)
420
- self._mqtt_client.loop_start()
421
-
422
- def _on_client_message(self, client, userdata, message): # pylint: disable=unused-argument
423
- """Handle messages received by mqtt client."""
424
- _logger.debug("Received MQTT message. Topic: %s", message.topic)
425
- json_payload = json.loads(message.payload)
426
- _logger.debug("Message: %s", json_payload)
427
- if self._on_message_callback:
428
- self._on_message_callback(json_payload, message.topic)
429
-
430
- def publish_message(self, topic: str, json_payload: str) -> None:
431
- """Publish a MQTT message."""
432
- self._mqtt_client.publish(topic, json_payload, qos=2)
433
-
434
- def disconnect(self) -> None:
435
- """Disconnect the client."""
436
- if self._mqtt_client.is_connected():
437
- self._mqtt_client.disconnect()
438
-
439
-
440
- class LGHorizonBox:
441
- """The LGHorizon box."""
442
-
443
- device_id: str = None
444
- hashed_cpe_id: str = None
445
- device_friendly_name: str = None
446
- state: str = None
447
- playing_info: LGHorizonPlayingInfo = None
448
- manufacturer: str = None
449
- model: str = None
450
- recording_capacity: int = None
451
-
452
- _mqtt_client: LGHorizonMqttClient
453
- _change_callback: Callable = None
454
- _auth: LGHorizonAuth = None
455
- _channels: Dict[str, LGHorizonChannel] = None
456
- _message_stamp = None
457
-
458
- def __init__(
459
- self,
460
- box_json: str,
461
- platform_type: Dict[str, str],
462
- mqtt_client: LGHorizonMqttClient,
463
- auth: LGHorizonAuth,
464
- channels: Dict[str, LGHorizonChannel],
465
- ):
466
- self.device_id = box_json["deviceId"]
467
- self.hashed_cpe_id = box_json["hashedCPEId"]
468
- self.device_friendly_name = box_json["settings"]["deviceFriendlyName"]
469
- self._mqtt_client = mqtt_client
470
- self._auth = auth
471
- self._channels = channels
472
- self.playing_info = LGHorizonPlayingInfo()
473
- if platform_type:
474
- self.manufacturer = platform_type["manufacturer"]
475
- self.model = platform_type["model"]
476
-
477
- def update_channels(self, channels: Dict[str, LGHorizonChannel]):
478
- """Update the channels list."""
479
- self._channels = channels
480
-
481
- def register_mqtt(self) -> None:
482
- """Register the mqtt connection."""
483
- if not self._mqtt_client.is_connected:
484
- raise Exception("MQTT client not connected.")
485
- topic = f"{self._auth.household_id}/{self._mqtt_client.client_id}/status"
486
- payload = {
487
- "source": self._mqtt_client.client_id,
488
- "state": ONLINE_RUNNING,
489
- "deviceType": "HGO",
490
- }
491
- self._mqtt_client.publish_message(topic, json.dumps(payload))
492
-
493
- def set_callback(self, change_callback: Callable) -> None:
494
- """Set a callback function."""
495
- self._change_callback = change_callback
496
-
497
- def update_state(self, payload):
498
- """Register a new settop box."""
499
- state = payload["state"]
500
- if self.state == state:
501
- return
502
- self.state = state
503
- if state == ONLINE_STANDBY:
504
- self.playing_info.reset()
505
- if self._change_callback:
506
- self._change_callback(self.device_id)
507
- else:
508
- self._request_settop_box_state()
509
- self._request_settop_box_recording_capacity()
510
-
511
- def update_recording_capacity(self, payload) -> None:
512
- """Updates the recording capacity."""
513
- if "CPE.capacity" not in payload or "used" not in payload:
514
- return
515
- self.recording_capacity = payload["used"]
516
-
517
- def update_with_replay_event(
518
- self, source_type: str, event: LGHorizonReplayEvent, channel: LGHorizonChannel
519
- ) -> None:
520
- """Update box with replay event."""
521
- self.playing_info.set_source_type(source_type)
522
- self.playing_info.set_channel(channel.id)
523
- self.playing_info.set_channel_title(channel.title)
524
- title = event.title
525
- if event.episode_name:
526
- title += f": {event.episode_name}"
527
- self.playing_info.set_title(title)
528
- self.playing_info.set_image(channel.stream_image)
529
- self.playing_info.reset_progress()
530
- self._trigger_callback()
531
-
532
- def update_with_recording(
533
- self,
534
- source_type: str,
535
- recording: LGHorizonRecordingSingle,
536
- channel: LGHorizonChannel,
537
- start: float,
538
- end: float,
539
- last_speed_change: float,
540
- relative_position: float,
541
- ) -> None:
542
- """Update box with recording."""
543
- self.playing_info.set_source_type(source_type)
544
- self.playing_info.set_channel(channel.id)
545
- self.playing_info.set_channel_title(channel.title)
546
- self.playing_info.set_title(f"{recording.title}")
547
- self.playing_info.set_image(recording.image)
548
- start_dt = datetime.fromtimestamp(start / 1000.0)
549
- end_dt = datetime.fromtimestamp(end / 1000.0)
550
- duration = (end_dt - start_dt).total_seconds()
551
- self.playing_info.set_duration(duration)
552
- self.playing_info.set_position(relative_position / 1000.0)
553
- last_update_dt = datetime.fromtimestamp(last_speed_change / 1000.0)
554
- self.playing_info.set_last_position_update(last_update_dt)
555
- self._trigger_callback()
556
-
557
- def update_with_vod(
558
- self,
559
- source_type: str,
560
- vod: LGHorizonVod,
561
- last_speed_change: float,
562
- relative_position: float,
563
- ) -> None:
564
- """Update box with vod."""
565
- self.playing_info.set_source_type(source_type)
566
- self.playing_info.set_channel(None)
567
- self.playing_info.set_channel_title(None)
568
- self.playing_info.set_title(vod.title)
569
- self.playing_info.set_image(None)
570
- self.playing_info.set_duration(vod.duration)
571
- self.playing_info.set_position(relative_position / 1000.0)
572
- last_update_dt = datetime.fromtimestamp(last_speed_change / 1000.0)
573
- self.playing_info.set_last_position_update(last_update_dt)
574
- self._trigger_callback()
575
-
576
- def update_with_app(self, source_type: str, app: LGHorizonApp) -> None:
577
- """Update box with app."""
578
- self.playing_info.set_source_type(source_type)
579
- self.playing_info.set_channel(None)
580
- self.playing_info.set_channel_title(app.title)
581
- self.playing_info.set_title(app.title)
582
- self.playing_info.set_image(app.image)
583
- self.playing_info.reset_progress()
584
- self._trigger_callback()
585
-
586
- def _trigger_callback(self):
587
- if self._change_callback:
588
- _logger.debug("Callback called from box %s", self.device_id)
589
- self._change_callback(self.device_id)
590
-
591
- def turn_on(self) -> None:
592
- """Turn the settop box on."""
593
-
594
- if self.state == ONLINE_STANDBY:
595
- self.send_key_to_box(MEDIA_KEY_POWER)
596
-
597
- def turn_off(self) -> None:
598
- """Turn the settop box off."""
599
- if self.state == ONLINE_RUNNING:
600
- self.send_key_to_box(MEDIA_KEY_POWER)
601
- self.playing_info.reset()
602
-
603
- def pause(self) -> None:
604
- """Pause the given settopbox."""
605
- if self.state == ONLINE_RUNNING and not self.playing_info.paused:
606
- self.send_key_to_box(MEDIA_KEY_PLAY_PAUSE)
607
-
608
- def play(self) -> None:
609
- """Resume the settopbox."""
610
- if self.state == ONLINE_RUNNING and self.playing_info.paused:
611
- self.send_key_to_box(MEDIA_KEY_PLAY_PAUSE)
612
-
613
- def stop(self) -> None:
614
- """Stop the settopbox."""
615
- if self.state == ONLINE_RUNNING:
616
- self.send_key_to_box(MEDIA_KEY_STOP)
617
-
618
- def next_channel(self):
619
- """Select the next channel for given settop box."""
620
- if self.state == ONLINE_RUNNING:
621
- self.send_key_to_box(MEDIA_KEY_CHANNEL_UP)
622
-
623
- def previous_channel(self) -> None:
624
- """Select the previous channel for given settop box."""
625
- if self.state == ONLINE_RUNNING:
626
- self.send_key_to_box(MEDIA_KEY_CHANNEL_DOWN)
627
-
628
- def press_enter(self) -> None:
629
- """Press enter on the settop box."""
630
- if self.state == ONLINE_RUNNING:
631
- self.send_key_to_box(MEDIA_KEY_ENTER)
632
-
633
- def rewind(self) -> None:
634
- """Rewind the settop box."""
635
- if self.state == ONLINE_RUNNING:
636
- self.send_key_to_box(MEDIA_KEY_REWIND)
637
-
638
- def fast_forward(self) -> None:
639
- """Fast forward the settop box."""
640
- if self.state == ONLINE_RUNNING:
641
- self.send_key_to_box(MEDIA_KEY_FAST_FORWARD)
642
-
643
- def record(self):
644
- """Record on the settop box."""
645
- if self.state == ONLINE_RUNNING:
646
- self.send_key_to_box(MEDIA_KEY_RECORD)
647
-
648
- def is_available(self) -> bool:
649
- """Return the availability of the settop box."""
650
- return self.state == ONLINE_RUNNING or self.state == ONLINE_STANDBY
651
-
652
- def set_channel(self, source: str) -> None:
653
- """Change te channel from the settopbox."""
654
- channel = [src for src in self._channels.values() if src.title == source][0]
655
- payload = (
656
- '{"id":"'
657
- + make_id(8)
658
- + '","type":"CPE.pushToTV","source":{"clientId":"'
659
- + self._mqtt_client.client_id
660
- + '","friendlyDeviceName":"Home Assistant"},'
661
- + '"status":{"sourceType":"linear","source":{"channelId":"'
662
- + channel.id
663
- + '"},"relativePosition":0,"speed":1}}'
664
- )
665
-
666
- self._mqtt_client.publish_message(
667
- f"{self._auth.household_id}/{self.device_id}", payload
668
- )
669
-
670
- def play_recording(self, recording_id):
671
- """Play recording."""
672
- payload = (
673
- '{"id":"'
674
- + make_id(8)
675
- + '","type":"CPE.pushToTV","source":{"clientId":"'
676
- + self._mqtt_client.client_id
677
- + '","friendlyDeviceName":"Home Assistant"},'
678
- + '"status":{"sourceType":"nDVR","source":{"recordingId":"'
679
- + recording_id
680
- + '"},"relativePosition":0}}'
681
- )
682
- self._mqtt_client.publish_message(
683
- f"{self._auth.household_id}/{self.device_id}", payload
684
- )
685
-
686
- def send_key_to_box(self, key: str) -> None:
687
- """Send emulated (remote) key press to settopbox."""
688
- payload_dict = {
689
- "type": "CPE.KeyEvent",
690
- "runtimeType": "key",
691
- "id": "ha",
692
- "source": self.device_id.lower(),
693
- "status": {"w3cKey": key, "eventType": "keyDownUp"},
694
- }
695
- payload = json.dumps(payload_dict)
696
- self._mqtt_client.publish_message(
697
- f"{self._auth.household_id}/{self.device_id}", payload
698
- )
699
-
700
- def _set_unknown_channel_info(self) -> None:
701
- """Set unknown channel info."""
702
- _logger.warning("Couldn't set channel. Channel info set to unknown...")
703
- self.playing_info.set_source_type(BOX_PLAY_STATE_CHANNEL)
704
- self.playing_info.set_channel(None)
705
- self.playing_info.set_title("No information available")
706
- self.playing_info.set_image(None)
707
- self.playing_info.set_paused(False)
708
-
709
- def _request_settop_box_state(self) -> None:
710
- """Send mqtt message to receive state from settop box."""
711
- topic = f"{self._auth.household_id}/{self.device_id}"
712
- payload = {
713
- "id": make_id(8),
714
- "type": "CPE.getUiStatus",
715
- "source": self._mqtt_client.client_id,
716
- }
717
- self._mqtt_client.publish_message(topic, json.dumps(payload))
718
-
719
- def _request_settop_box_recording_capacity(self) -> None:
720
- """Send mqtt message to receive state from settop box."""
721
- topic = f"{self._auth.household_id}/{self.device_id}"
722
- payload = {
723
- "id": make_id(8),
724
- "type": "CPE.capacity",
725
- "source": self._mqtt_client.client_id,
726
- }
727
- self._mqtt_client.publish_message(topic, json.dumps(payload))
728
-
729
-
730
- class LGHorizonProfile:
731
- """LGHorizon profile."""
732
-
733
- profile_id: str = None
734
- name: str = None
735
- favorite_channels: list[str] = None
736
-
737
- def __init__(self, json_payload):
738
- self.profile_id = json_payload["profileId"]
739
- self.name = json_payload["name"]
740
- self.favorite_channels = json_payload["favoriteChannels"]
741
-
742
-
743
- class LGHorizonCustomer:
744
- """LGHorizon customer"""
745
-
746
- customer_id: str = None
747
- hashed_customer_id: str = None
748
- country_id: str = None
749
- city_id: int = 0
750
- settop_boxes: list[str] = None
751
- profiles: Dict[str, LGHorizonProfile] = {}
752
-
753
- def __init__(self, json_payload):
754
- self.customer_id = json_payload["customerId"]
755
- self.hashed_customer_id = json_payload["hashedCustomerId"]
756
- self.country_id = json_payload["countryId"]
757
- self.city_id = json_payload["cityId"]
758
- if "assignedDevices" in json_payload:
759
- self.settop_boxes = json_payload["assignedDevices"]
760
- if "profiles" in json_payload:
761
- for profile in json_payload["profiles"]:
762
- self.profiles[profile["profileId"]] = LGHorizonProfile(profile)