lghorizon 0.7.5b2__py3-none-any.whl → 0.8.0b1__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/helpers.py CHANGED
@@ -1,8 +1,9 @@
1
1
  """Helper functions."""
2
+
2
3
  import random
3
4
 
4
5
 
5
- def make_id(stringLength=10):
6
+ def make_id(string_length=10):
6
7
  """Create an id with given length."""
7
8
  letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
8
- return "".join(random.choice(letters) for i in range(stringLength))
9
+ return "".join(random.choice(letters) for i in range(string_length))
@@ -1,17 +1,22 @@
1
1
  """Python client for LGHorizon."""
2
+ # pylint: disable=broad-exception-caught
3
+ # pylint: disable=line-too-long
2
4
 
3
5
  import logging
4
6
  import json
5
- import sys, traceback
7
+ import re
8
+
9
+ from typing import Any, Callable, Dict, List
10
+ import backoff
11
+
12
+ from requests import Session, exceptions as request_exceptions
13
+
6
14
  from .exceptions import (
7
15
  LGHorizonApiUnauthorizedError,
8
16
  LGHorizonApiConnectionError,
9
17
  LGHorizonApiLockedError,
10
18
  )
11
- import backoff
12
- from requests import Session, exceptions as request_exceptions
13
- from paho.mqtt.client import WebsocketConnectionError
14
- import re
19
+
15
20
  from .models import (
16
21
  LGHorizonAuth,
17
22
  LGHorizonBox,
@@ -39,7 +44,7 @@ from .const import (
39
44
  RECORDING_TYPE_SEASON,
40
45
  RECORDING_TYPE_SHOW,
41
46
  )
42
- from typing import Any, Callable, Dict, List
47
+
43
48
 
44
49
  _logger = logging.getLogger(__name__)
45
50
  _supported_platforms = ["EOS", "EOS2", "HORIZON", "APOLLO"]
@@ -52,7 +57,7 @@ class LGHorizonApi:
52
57
  _session: Session = None
53
58
  settop_boxes: Dict[str, LGHorizonBox] = None
54
59
  customer: LGHorizonCustomer = None
55
- _mqttClient: LGHorizonMqttClient = None
60
+ _mqtt_client: LGHorizonMqttClient = None
56
61
  _channels: Dict[str, LGHorizonChannel] = None
57
62
  _country_settings = None
58
63
  _country_code: str = None
@@ -89,11 +94,9 @@ class LGHorizonApi:
89
94
  def _authorize(self) -> None:
90
95
  ctry_code = self._country_code[0:2]
91
96
  if ctry_code == "be":
92
- self.authorize_telenet()
97
+ self._authorize_telenet()
93
98
  elif ctry_code in ("gb", "ch"):
94
- self.authorize_with_refreshtoken()
95
- # elif ctry_code == "ch":
96
- # self.authorize_sunrise()
99
+ self._authorize_with_refresh_token()
97
100
  else:
98
101
  self._authorize_default()
99
102
 
@@ -124,7 +127,8 @@ class LGHorizonApi:
124
127
  self._auth.fill(auth_response.json())
125
128
  _logger.debug("Authorization succeeded")
126
129
 
127
- def authorize_with_refreshtoken(self) -> None:
130
+ def _authorize_with_refresh_token(self) -> None:
131
+ """Handle authorizzationg using request token."""
128
132
  _logger.debug("Authorizing via refresh")
129
133
  refresh_url = (
130
134
  f"{self._country_settings['api_url']}/auth-service/v1/authorization/refresh"
@@ -153,8 +157,8 @@ class LGHorizonApi:
153
157
  raise LGHorizonApiConnectionError("Unknown connection error")
154
158
 
155
159
  self._auth.fill(auth_response.json())
156
- self.refresh_token = self._auth.refreshToken
157
- self._session.cookies["ACCESSTOKEN"] = self._auth.accessToken
160
+ self.refresh_token = self._auth.refresh_token
161
+ self._session.cookies["ACCESSTOKEN"] = self._auth.access_token
158
162
 
159
163
  if self._refresh_callback:
160
164
  self._refresh_callback()
@@ -162,9 +166,11 @@ class LGHorizonApi:
162
166
  _logger.debug("Authorization succeeded")
163
167
 
164
168
  def set_callback(self, refresh_callback: Callable) -> None:
169
+ """Set the refresh callback."""
165
170
  self._refresh_callback = refresh_callback
166
171
 
167
- def authorize_telenet(self):
172
+ def _authorize_telenet(self):
173
+ """Authorize telenet users."""
168
174
  try:
169
175
  login_session = Session()
170
176
  # Step 1 - Get Authorization data
@@ -176,13 +182,13 @@ class LGHorizonApi:
176
182
  if not auth_response.ok:
177
183
  raise LGHorizonApiConnectionError("Can't connect to authorization URL")
178
184
  auth_response_json = auth_response.json()
179
- authorizationUri = auth_response_json["authorizationUri"]
180
- authValidtyToken = auth_response_json["validityToken"]
185
+ authorization_uri = auth_response_json["authorizationUri"]
186
+ authorization_validity_token = auth_response_json["validityToken"]
181
187
 
182
188
  # Step 2 - Get Authorization cookie
183
189
  _logger.debug("Step 2 - Get Authorization cookie")
184
190
 
185
- auth_cookie_response = login_session.get(authorizationUri)
191
+ auth_cookie_response = login_session.get(authorization_uri)
186
192
  if not auth_cookie_response.ok:
187
193
  raise LGHorizonApiConnectionError("Can't connect to authorization URL")
188
194
 
@@ -206,20 +212,20 @@ class LGHorizonApi:
206
212
  self._country_settings["oauth_redirect_header"]
207
213
  ]
208
214
 
209
- if not self._identifier is None:
215
+ if self._identifier is not None:
210
216
  redirect_url += f"&dtv_identifier={self._identifier}"
211
217
  redirect_response = login_session.get(redirect_url, allow_redirects=False)
212
218
  success_url = redirect_response.headers[
213
219
  self._country_settings["oauth_redirect_header"]
214
220
  ]
215
- codeMatches = re.findall(r"code=(.*)&", success_url)
221
+ code_matches = re.findall(r"code=(.*)&", success_url)
216
222
 
217
- authorizationCode = codeMatches[0]
223
+ authorization_code = code_matches[0]
218
224
 
219
225
  new_payload = {
220
226
  "authorizationGrant": {
221
- "authorizationCode": authorizationCode,
222
- "validityToken": authValidtyToken,
227
+ "authorizationCode": authorization_code,
228
+ "validityToken": authorization_validity_token,
223
229
  }
224
230
  }
225
231
  headers = {
@@ -229,8 +235,8 @@ class LGHorizonApi:
229
235
  auth_url, json.dumps(new_payload), headers=headers
230
236
  )
231
237
  self._auth.fill(post_result.json())
232
- self._session.cookies["ACCESSTOKEN"] = self._auth.accessToken
233
- except Exception as ex:
238
+ self._session.cookies["ACCESSTOKEN"] = self._auth.access_token
239
+ except Exception:
234
240
  pass
235
241
 
236
242
  def _obtain_mqtt_token(self):
@@ -238,7 +244,7 @@ class LGHorizonApi:
238
244
  mqtt_auth_url = self._config["authorizationService"]["URL"]
239
245
  mqtt_response = self._do_api_call(f"{mqtt_auth_url}/v1/mqtt/token")
240
246
  self._auth.mqttToken = mqtt_response["token"]
241
- _logger.debug(f"MQTT token: {self._auth.mqttToken}")
247
+ _logger.debug("MQTT token: %s", self._auth.mqttToken)
242
248
 
243
249
  @backoff.on_exception(
244
250
  backoff.expo,
@@ -251,11 +257,12 @@ class LGHorizonApi:
251
257
  ),
252
258
  )
253
259
  def connect(self) -> None:
260
+ """Start connection process."""
254
261
  self._config = self._get_config(self._country_code)
255
262
  _logger.debug("Connect to API")
256
263
  self._authorize()
257
264
  self._obtain_mqtt_token()
258
- self._mqttClient = LGHorizonMqttClient(
265
+ self._mqtt_client = LGHorizonMqttClient(
259
266
  self._auth,
260
267
  self._config["mqttBroker"]["URL"],
261
268
  self._on_mqtt_connected,
@@ -263,14 +270,14 @@ class LGHorizonApi:
263
270
  )
264
271
 
265
272
  self._register_customer_and_boxes()
266
- self._mqttClient.connect()
273
+ self._mqtt_client.connect()
267
274
 
268
275
  def disconnect(self):
269
276
  """Disconnect."""
270
277
  _logger.debug("Disconnect from API")
271
- if not self._mqttClient or not self._mqttClient.is_connected:
278
+ if not self._mqtt_client or not self._mqtt_client.is_connected:
272
279
  return
273
- self._mqttClient.disconnect()
280
+ self._mqtt_client.disconnect()
274
281
 
275
282
  def _on_mqtt_connected(self) -> None:
276
283
  _logger.debug("Connected to MQTT server. Registering all boxes...")
@@ -281,47 +288,46 @@ class LGHorizonApi:
281
288
  def _on_mqtt_message(self, message: str, topic: str) -> None:
282
289
  if "action" in message and message["action"] == "OPS.getProfilesUpdate":
283
290
  self._update_customer()
284
- box: LGHorizonBox
285
291
  elif "source" in message:
286
- deviceId = message["source"]
287
- if not isinstance(deviceId, str):
292
+ device_id = message["source"]
293
+ if not isinstance(device_id, str):
288
294
  _logger.debug("ignoring message - not a string")
289
295
  return
290
- if not deviceId in self.settop_boxes.keys():
296
+ if device_id not in self.settop_boxes:
291
297
  return
292
298
  try:
293
299
  if "deviceType" in message and message["deviceType"] == "STB":
294
- self.settop_boxes[deviceId].update_state(message)
300
+ self.settop_boxes[device_id].update_state(message)
295
301
  if "status" in message:
296
- self._handle_box_update(deviceId, message)
302
+ self._handle_box_update(device_id, message)
297
303
 
298
304
  except Exception:
299
305
  _logger.exception("Could not handle status message")
300
- _logger.warning(f"Full message: {str(message)}")
301
- self.settop_boxes[deviceId].playing_info.reset()
302
- self.settop_boxes[deviceId].playing_info.set_paused(False)
306
+ _logger.warning("Full message: %s", str(message))
307
+ self.settop_boxes[device_id].playing_info.reset()
308
+ self.settop_boxes[device_id].playing_info.set_paused(False)
303
309
  elif "CPE.capacity" in message:
304
310
  splitted_topic = topic.split("/")
305
311
  if len(splitted_topic) != 4:
306
312
  return
307
- deviceId = splitted_topic[1]
308
- if not deviceId in self.settop_boxes.keys():
313
+ device_id = splitted_topic[1]
314
+ if device_id not in self.settop_boxes:
309
315
  return
310
- self.settop_boxes[deviceId].update_recording_capacity(message)
316
+ self.settop_boxes[device_id].update_recording_capacity(message)
311
317
 
312
- def _handle_box_update(self, deviceId: str, raw_message: Any) -> None:
313
- statusPayload = raw_message["status"]
314
- if "uiStatus" not in statusPayload:
318
+ def _handle_box_update(self, device_id: str, raw_message: Any) -> None:
319
+ status_payload = raw_message["status"]
320
+ if "uiStatus" not in status_payload:
315
321
  return
316
- uiStatus = statusPayload["uiStatus"]
317
- if uiStatus == "mainUI":
318
- playerState = statusPayload["playerState"]
319
- if "sourceType" not in playerState or "source" not in playerState:
322
+ ui_status = status_payload["uiStatus"]
323
+ if ui_status == "mainUI":
324
+ player_state = status_payload["playerState"]
325
+ if "sourceType" not in player_state or "source" not in player_state:
320
326
  return
321
- source_type = playerState["sourceType"]
322
- state_source = playerState["source"]
323
- self.settop_boxes[deviceId].playing_info.set_paused(
324
- playerState["speed"] == 0
327
+ source_type = player_state["sourceType"]
328
+ state_source = player_state["source"]
329
+ self.settop_boxes[device_id].playing_info.set_paused(
330
+ player_state["speed"] == 0
325
331
  )
326
332
  if (
327
333
  source_type
@@ -332,27 +338,27 @@ class LGHorizonApi:
332
338
  )
333
339
  and "eventId" in state_source
334
340
  ):
335
- eventId = state_source["eventId"]
341
+ event_id = state_source["eventId"]
336
342
  raw_replay_event = self._do_api_call(
337
- f"{self._config['linearService']['URL']}/v2/replayEvent/{eventId}?returnLinearContent=true&language={self._country_settings['language']}"
343
+ f"{self._config['linearService']['URL']}/v2/replayEvent/{event_id}?returnLinearContent=true&language={self._country_settings['language']}"
338
344
  )
339
- replayEvent = LGHorizonReplayEvent(raw_replay_event)
340
- channel = self._channels[replayEvent.channelId]
341
- self.settop_boxes[deviceId].update_with_replay_event(
342
- source_type, replayEvent, channel
345
+ replay_event = LGHorizonReplayEvent(raw_replay_event)
346
+ channel = self._channels[replay_event.channel_id]
347
+ self.settop_boxes[device_id].update_with_replay_event(
348
+ source_type, replay_event, channel
343
349
  )
344
350
  elif source_type == BOX_PLAY_STATE_DVR:
345
- recordingId = state_source["recordingId"]
351
+ recording_id = state_source["recordingId"]
346
352
  session_start_time = state_source["sessionStartTime"]
347
353
  session_end_time = state_source["sessionEndTime"]
348
- last_speed_change_time = playerState["lastSpeedChangeTime"]
349
- relative_position = playerState["relativePosition"]
354
+ last_speed_change_time = player_state["lastSpeedChangeTime"]
355
+ relative_position = player_state["relativePosition"]
350
356
  raw_recording = self._do_api_call(
351
- f"{self._config['recordingService']['URL']}/customers/{self._auth.householdId}/details/single/{recordingId}?profileId=4504e28d-c1cb-4284-810b-f5eaab06f034&language={self._country_settings['language']}"
357
+ f"{self._config['recordingService']['URL']}/customers/{self._auth.household_id}/details/single/{recording_id}?profileId=4504e28d-c1cb-4284-810b-f5eaab06f034&language={self._country_settings['language']}"
352
358
  )
353
359
  recording = LGHorizonRecordingSingle(raw_recording)
354
- channel = self._channels[recording.channelId]
355
- self.settop_boxes[deviceId].update_with_recording(
360
+ channel = self._channels[recording.channel_id]
361
+ self.settop_boxes[device_id].update_with_recording(
356
362
  source_type,
357
363
  recording,
358
364
  channel,
@@ -362,35 +368,35 @@ class LGHorizonApi:
362
368
  relative_position,
363
369
  )
364
370
  elif source_type == BOX_PLAY_STATE_VOD:
365
- titleId = state_source["titleId"]
366
- last_speed_change_time = playerState["lastSpeedChangeTime"]
367
- relative_position = playerState["relativePosition"]
371
+ title_id = state_source["titleId"]
372
+ last_speed_change_time = player_state["lastSpeedChangeTime"]
373
+ relative_position = player_state["relativePosition"]
368
374
  raw_vod = self._do_api_call(
369
- f"{self._config['vodService']['URL']}/v2/detailscreen/{titleId}?language={self._country_settings['language']}&profileId=4504e28d-c1cb-4284-810b-f5eaab06f034&cityId={self.customer.cityId}"
375
+ f"{self._config['vodService']['URL']}/v2/detailscreen/{title_id}?language={self._country_settings['language']}&profileId=4504e28d-c1cb-4284-810b-f5eaab06f034&cityId={self.customer.city_id}"
370
376
  )
371
377
  vod = LGHorizonVod(raw_vod)
372
- self.settop_boxes[deviceId].update_with_vod(
378
+ self.settop_boxes[device_id].update_with_vod(
373
379
  source_type, vod, last_speed_change_time, relative_position
374
380
  )
375
- elif uiStatus == "apps":
376
- app = LGHorizonApp(statusPayload["appsState"])
377
- self.settop_boxes[deviceId].update_with_app("app", app)
381
+ elif ui_status == "apps":
382
+ app = LGHorizonApp(status_payload["appsState"])
383
+ self.settop_boxes[device_id].update_with_app("app", app)
378
384
 
379
385
  @backoff.on_exception(
380
386
  backoff.expo, LGHorizonApiConnectionError, max_tries=3, logger=_logger
381
387
  )
382
- def _do_api_call(self, url: str, tries: int = 0) -> str:
383
- _logger.info(f"Executing API call to {url}")
388
+ def _do_api_call(self, url: str) -> str:
389
+ _logger.info("Executing API call to %s", url)
384
390
  try:
385
391
  api_response = self._session.get(url)
386
392
  api_response.raise_for_status()
387
393
  json_response = api_response.json()
388
- except request_exceptions.HTTPError as httpEx:
394
+ except request_exceptions.HTTPError as http_ex:
389
395
  self._authorize()
390
396
  raise LGHorizonApiConnectionError(
391
- f"Unable to call {url}. Error:{str(httpEx)}"
392
- )
393
- _logger.debug(f"Result API call: {json_response}")
397
+ f"Unable to call {url}. Error:{str(http_ex)}"
398
+ ) from http_ex
399
+ _logger.debug("Result API call: %s", json_response)
394
400
  return json_response
395
401
 
396
402
  def _register_customer_and_boxes(self):
@@ -412,15 +418,15 @@ class LGHorizonApi:
412
418
  else:
413
419
  platform_type = None
414
420
  box = LGHorizonBox(
415
- device, platform_type, self._mqttClient, self._auth, self._channels
421
+ device, platform_type, self._mqtt_client, self._auth, self._channels
416
422
  )
417
- self.settop_boxes[box.deviceId] = box
418
- _logger.info("Box %s registered...", box.deviceId)
423
+ self.settop_boxes[box.device_id] = box
424
+ _logger.info("Box %s registered...", box.device_id)
419
425
 
420
426
  def _update_customer(self):
421
427
  _logger.info("Get customer data")
422
428
  personalisation_result = self._do_api_call(
423
- f"{self._config['personalizationService']['URL']}/v1/customer/{self._auth.householdId}?with=profiles%2Cdevices"
429
+ f"{self._config['personalizationService']['URL']}/v1/customer/{self._auth.household_id}?with=profiles%2Cdevices"
424
430
  )
425
431
  _logger.debug("Personalisation result: %s ", personalisation_result)
426
432
  self.customer = LGHorizonCustomer(personalisation_result)
@@ -429,7 +435,7 @@ class LGHorizonApi:
429
435
  self._update_entitlements()
430
436
  _logger.info("Retrieving channels...")
431
437
  channels_result = self._do_api_call(
432
- f"{self._config['linearService']['URL']}/v2/channels?cityId={self.customer.cityId}&language={self._country_settings['language']}&productClass=Orion-DASH"
438
+ f"{self._config['linearService']['URL']}/v2/channels?cityId={self.customer.city_id}&language={self._country_settings['language']}&productClass=Orion-DASH"
433
439
  )
434
440
  for channel in channels_result:
435
441
  if "isRadio" in channel and channel["isRadio"]:
@@ -441,9 +447,10 @@ class LGHorizonApi:
441
447
  continue
442
448
  channel_id = channel["id"]
443
449
  self._channels[channel_id] = LGHorizonChannel(channel)
444
- _logger.info(f"{len(self._channels)} retrieved.")
450
+ _logger.info("%s retrieved.", len(self._channels))
445
451
 
446
452
  def get_display_channels(self):
453
+ """Returns channels to display baed on profile."""
447
454
  all_channels = self._channels.values()
448
455
  if not self._profile_id or self._profile_id not in self.customer.profiles:
449
456
  return all_channels
@@ -455,11 +462,11 @@ class LGHorizonApi:
455
462
  channel for channel in all_channels if channel.id in profile_channel_ids
456
463
  ]
457
464
 
458
- def _get_replay_event(self, listingId) -> Any:
465
+ def _get_replay_event(self, listing_id) -> Any:
459
466
  """Get listing."""
460
467
  _logger.info("Retrieving replay event details...")
461
468
  response = self._do_api_call(
462
- f"{self._config['linearService']['URL']}/v2/replayEvent/{listingId}?returnLinearContent=true&language={self._country_settings['language']}"
469
+ f"{self._config['linearService']['URL']}/v2/replayEvent/{listing_id}?returnLinearContent=true&language={self._country_settings['language']}"
463
470
  )
464
471
  _logger.info("Replay event details retrieved")
465
472
  return response
@@ -473,38 +480,40 @@ class LGHorizonApi:
473
480
  try:
474
481
  _logger.info("Retrieving recordingcapacity...")
475
482
  quota_content = self._do_api_call(
476
- f"{self._config['recordingService']['URL']}/customers/{self._auth.householdId}/quota"
483
+ f"{self._config['recordingService']['URL']}/customers/{self._auth.household_id}/quota"
477
484
  )
478
- if not "quota" in quota_content and not "occupied" in quota_content:
485
+ if "quota" not in quota_content and "occupied" not in quota_content:
479
486
  _logger.error("Unable to fetch recording capacity...")
480
487
  return None
481
488
  capacity = (quota_content["occupied"] / quota_content["quota"]) * 100
482
489
  self.recording_capacity = round(capacity)
483
- _logger.debug(f"Remaining recordingcapacity {self.recording_capacity}%")
490
+ _logger.debug("Remaining recordingcapacity %s %%", self.recording_capacity)
484
491
  return self.recording_capacity
485
- except:
492
+ except Exception:
486
493
  _logger.error("Unable to fetch recording capacity...")
487
494
  return None
488
495
 
489
496
  def get_recordings(self) -> List[LGHorizonBaseRecording]:
497
+ """Returns recordings."""
490
498
  _logger.info("Retrieving recordings...")
491
499
  recording_content = self._do_api_call(
492
- f"{self._config['recordingService']['URL']}/customers/{self._auth.householdId}/recordings?sort=time&sortOrder=desc&language={self._country_settings['language']}"
500
+ f"{self._config['recordingService']['URL']}/customers/{self._auth.household_id}/recordings?sort=time&sortOrder=desc&language={self._country_settings['language']}"
493
501
  )
494
502
  recordings = []
495
503
  for recording_data_item in recording_content["data"]:
496
- type = recording_data_item["type"]
497
- if type == RECORDING_TYPE_SINGLE:
504
+ recording_type = recording_data_item["type"]
505
+ if recording_type == RECORDING_TYPE_SINGLE:
498
506
  recordings.append(LGHorizonRecordingSingle(recording_data_item))
499
- elif type in (RECORDING_TYPE_SEASON, RECORDING_TYPE_SHOW):
507
+ elif recording_type in (RECORDING_TYPE_SEASON, RECORDING_TYPE_SHOW):
500
508
  recordings.append(LGHorizonRecordingListSeasonShow(recording_data_item))
501
- _logger.info(f"{len(recordings)} recordings retrieved...")
509
+ _logger.info("%s recordings retrieved...", len(recordings))
502
510
  return recordings
503
511
 
504
- def get_recording_show(self, showId: str) -> list[LGHorizonRecordingSingle]:
512
+ def get_recording_show(self, show_id: str) -> list[LGHorizonRecordingSingle]:
513
+ """Returns show recording"""
505
514
  _logger.info("Retrieving show recordings...")
506
515
  show_recording_content = self._do_api_call(
507
- f"{self._config['recordingService']['URL']}/customers/{self._auth.householdId}/episodes/shows/{showId}?source=recording&language=nl&sort=time&sortOrder=asc"
516
+ f"{self._config['recordingService']['URL']}/customers/{self._auth.household_id}/episodes/shows/{show_id}?source=recording&language=nl&sort=time&sortOrder=asc"
508
517
  )
509
518
  recordings = []
510
519
  for item in show_recording_content["data"]:
@@ -512,21 +521,21 @@ class LGHorizonApi:
512
521
  recordings.append(LGHorizonRecordingShow(item))
513
522
  else:
514
523
  recordings.append(LGHorizonRecordingEpisode(item))
515
- _logger.info(f"{len(recordings)} showrecordings retrieved...")
524
+ _logger.info("%s showrecordings retrieved...", len(recordings))
516
525
  return recordings
517
526
 
518
527
  def _update_entitlements(self) -> None:
519
528
  _logger.info("Retrieving entitlements...")
520
529
  entitlements_json = self._do_api_call(
521
- f"{self._config['purchaseService']['URL']}/v2/customers/{self._auth.householdId}/entitlements?enableDaypass=true"
530
+ f"{self._config['purchaseService']['URL']}/v2/customers/{self._auth.household_id}/entitlements?enableDaypass=true"
522
531
  )
523
532
  self._entitlements.clear()
524
533
  for entitlement in entitlements_json["entitlements"]:
525
534
  self._entitlements.append(entitlement["id"])
526
535
 
527
536
  def _get_config(self, country_code: str):
528
- ctryCode = country_code[0:2]
529
- config_url = f"{self._country_settings['api_url']}/{ctryCode}/en/config-service/conf/web/backoffice.json"
537
+ base_country_code = country_code[0:2]
538
+ config_url = f"{self._country_settings['api_url']}/{base_country_code}/en/config-service/conf/web/backoffice.json"
530
539
  result = self._do_api_call(config_url)
531
540
  _logger.debug(result)
532
541
  return result
lghorizon/models.py CHANGED
@@ -1,14 +1,15 @@
1
+ """Models for LGHorizon API."""
2
+
3
+ # pylint: disable=broad-exception-caught
4
+ # pylint: disable=broad-exception-raised
1
5
  from datetime import datetime
2
- from typing import Any, Callable, Dict, List
6
+ from typing import Callable, Dict
7
+ import json
8
+ import logging
3
9
  import paho.mqtt.client as mqtt
4
- import requests
10
+
5
11
  from .const import (
6
- BOX_PLAY_STATE_BUFFER,
7
12
  BOX_PLAY_STATE_CHANNEL,
8
- BOX_PLAY_STATE_DVR,
9
- BOX_PLAY_STATE_REPLAY,
10
- BOX_PLAY_STATE_APP,
11
- BOX_PLAY_STATE_VOD,
12
13
  ONLINE_STANDBY,
13
14
  ONLINE_RUNNING,
14
15
  MEDIA_KEY_POWER,
@@ -21,46 +22,47 @@ from .const import (
21
22
  MEDIA_KEY_FAST_FORWARD,
22
23
  MEDIA_KEY_RECORD,
23
24
  RECORDING_TYPE_SEASON,
24
- RECORDING_TYPE_SHOW,
25
25
  )
26
26
 
27
- import json
28
27
  from .helpers import make_id
29
- import logging
30
28
 
31
29
  _logger = logging.getLogger(__name__)
32
30
 
33
31
 
34
32
  class LGHorizonAuth:
35
- householdId: str
36
- accessToken: str
37
- refreshToken: str
38
- refreshTokenExpiry: datetime
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
39
  username: str
40
- mqttToken: str = None
41
- accessToken: str = None
40
+ mqtt_token: str = None
41
+ access_token: str = None
42
42
 
43
43
  def __init__(self):
44
44
  """Initialize a session."""
45
- pass
46
45
 
47
46
  def fill(self, auth_json) -> None:
48
- self.householdId = auth_json["householdId"]
49
- self.accessToken = auth_json["accessToken"]
50
- self.refreshToken = auth_json["refreshToken"]
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
51
  self.username = auth_json["username"]
52
52
  try:
53
- self.refreshTokenExpiry = datetime.fromtimestamp(
53
+ self.refresh_token_expiry = datetime.fromtimestamp(
54
54
  auth_json["refreshTokenExpiry"]
55
55
  )
56
56
  except ValueError:
57
- # VM uses milliseconds for the expiry time; if the year is too high to be valid, it assumes it's milliseconds and divides it
58
- self.refreshTokenExpiry = datetime.fromtimestamp(
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(
59
60
  auth_json["refreshTokenExpiry"] // 1000
60
61
  )
61
62
 
62
63
  def is_expired(self) -> bool:
63
- return self.refreshTokenExpiry
64
+ """Check if refresh token is expired."""
65
+ return self.refresh_token_expiry
64
66
 
65
67
 
66
68
  class LGHorizonPlayingInfo:
@@ -78,7 +80,6 @@ class LGHorizonPlayingInfo:
78
80
 
79
81
  def __init__(self):
80
82
  """Initialize the playing info."""
81
- pass
82
83
 
83
84
  def set_paused(self, paused: bool):
84
85
  """Set pause state."""
@@ -117,11 +118,13 @@ class LGHorizonPlayingInfo:
117
118
  self.last_position_update = last_position_update
118
119
 
119
120
  def reset_progress(self):
121
+ """Reset the progress."""
120
122
  self.last_position_update = None
121
123
  self.duration = None
122
124
  self.position = None
123
125
 
124
126
  def reset(self):
127
+ """Reset the channel"""
125
128
  self.channel_id = None
126
129
  self.title = None
127
130
  self.image = None
@@ -152,6 +155,7 @@ class LGHorizonChannel:
152
155
  self.channel_number = channel_json["logicalChannelNumber"]
153
156
 
154
157
  def get_stream_image(self, channel_json) -> str:
158
+ """Returns the stream image."""
155
159
  image_stream = channel_json["imageStream"]
156
160
  if "full" in image_stream:
157
161
  return image_stream["full"]
@@ -163,47 +167,56 @@ class LGHorizonChannel:
163
167
 
164
168
 
165
169
  class LGHorizonReplayEvent:
166
- episodeNumber: int = None
167
- channelId: str = None
168
- eventId: str = None
169
- seasonNumber: int = None
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
170
176
  title: str = None
171
- episodeName: str = None
177
+ episode_name: str = None
172
178
 
173
179
  def __init__(self, raw_json: str):
174
- self.channelId = raw_json["channelId"]
175
- self.eventId = raw_json["eventId"]
180
+ self.channel_id = raw_json["channelId"]
181
+ self.event_id = raw_json["eventId"]
176
182
  self.title = raw_json["title"]
177
183
  if "episodeName" in raw_json:
178
- self.episodeName = raw_json["episodeName"]
184
+ self.episode_name = raw_json["episodeName"]
179
185
  if "episodeNumber" in raw_json:
180
- self.episodeNumber = raw_json["episodeNumber"]
186
+ self.episode_number = raw_json["episodeNumber"]
181
187
  if "seasonNumber" in raw_json:
182
- self.seasonNumber = raw_json["seasonNumber"]
188
+ self.season_number = raw_json["seasonNumber"]
183
189
 
184
190
 
185
191
  class LGHorizonBaseRecording:
186
- id: str = None
192
+ """LgHorizon base recording."""
193
+
194
+ recording_id: str = None
187
195
  title: str = None
188
196
  image: str = None
189
- type: str = None
190
- channelId: str = None
197
+ recording_type: str = None
198
+ channel_id: str = None
191
199
 
192
200
  def __init__(
193
- self, id: str, title: str, image: str, channelId: str, type: str
201
+ self,
202
+ recording_id: str,
203
+ title: str,
204
+ image: str,
205
+ channel_id: str,
206
+ recording_type: str,
194
207
  ) -> None:
195
- self.id = id
208
+ self.recording_id = recording_id
196
209
  self.title = title
197
210
  self.image = image
198
- self.channelId = channelId
199
- self.type = type
211
+ self.channel_id = channel_id
212
+ self.recording_type = recording_type
200
213
 
201
214
 
202
215
  class LGHorizonRecordingSingle(LGHorizonBaseRecording):
203
216
  """Represents a single recording."""
204
217
 
205
- seasonNumber: int = None
206
- episodeNumber: int = None
218
+ season_number: int = None
219
+ episode_number: int = None
207
220
 
208
221
  def __init__(self, recording_json):
209
222
  """Init the single recording."""
@@ -219,32 +232,32 @@ class LGHorizonRecordingSingle(LGHorizonBaseRecording):
219
232
  recording_json["type"],
220
233
  )
221
234
  if "seasonNumber" in recording_json:
222
- self.seasonNumber = recording_json["seasonNumber"]
235
+ self.season_number = recording_json["seasonNumber"]
223
236
  if "episodeNumber" in recording_json:
224
- self.episodeNumber = recording_json["episodeNumber"]
237
+ self.episode_number = recording_json["episodeNumber"]
225
238
 
226
239
 
227
240
  class LGHorizonRecordingEpisode:
228
241
  """Represents a single recording."""
229
242
 
230
- episodeId: str = None
231
- episodeTitle: str = None
232
- seasonNumber: int = None
233
- episodeNumber: int = None
234
- showTitle: str = None
235
- recordingState: str = None
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
236
249
  image: str = None
237
250
 
238
251
  def __init__(self, recording_json):
239
252
  """Init the single recording."""
240
- self.episodeId = recording_json["episodeId"]
241
- self.episodeTitle = recording_json["episodeTitle"]
242
- self.showTitle = recording_json["showTitle"]
243
- self.recordingState = recording_json["recordingState"]
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"]
244
257
  if "seasonNumber" in recording_json:
245
- self.seasonNumber = recording_json["seasonNumber"]
258
+ self.season_number = recording_json["seasonNumber"]
246
259
  if "episodeNumber" in recording_json:
247
- self.episodeNumber = recording_json["episodeNumber"]
260
+ self.episode_number = recording_json["episodeNumber"]
248
261
  if "poster" in recording_json and "url" in recording_json["poster"]:
249
262
  self.image = recording_json["poster"]["url"]
250
263
 
@@ -252,28 +265,30 @@ class LGHorizonRecordingEpisode:
252
265
  class LGHorizonRecordingShow:
253
266
  """Represents a single recording."""
254
267
 
255
- episodeId: str = None
256
- showTitle: str = None
257
- seasonNumber: int = None
258
- episodeNumber: int = None
259
- recordingState: str = None
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
260
273
  image: str = None
261
274
 
262
275
  def __init__(self, recording_json):
263
276
  """Init the single recording."""
264
- self.episodeId = recording_json["episodeId"]
265
- self.showTitle = recording_json["showTitle"]
266
- self.recordingState = recording_json["recordingState"]
277
+ self.episode_id = recording_json["episodeId"]
278
+ self.show_title = recording_json["showTitle"]
279
+ self.recording_state = recording_json["recordingState"]
267
280
  if "seasonNumber" in recording_json:
268
- self.seasonNumber = recording_json["seasonNumber"]
281
+ self.season_number = recording_json["seasonNumber"]
269
282
  if "episodeNumber" in recording_json:
270
- self.episodeNumber = recording_json["episodeNumber"]
283
+ self.episode_number = recording_json["episodeNumber"]
271
284
  if "poster" in recording_json and "url" in recording_json["poster"]:
272
285
  self.image = recording_json["poster"]["url"]
273
286
 
274
287
 
275
288
  class LGHorizonRecordingListSeasonShow(LGHorizonBaseRecording):
276
- showId: str = None
289
+ """LGHorizon Season show list."""
290
+
291
+ show_id: str = None
277
292
 
278
293
  def __init__(self, recording_season_json):
279
294
  """Init the single recording."""
@@ -286,13 +301,15 @@ class LGHorizonRecordingListSeasonShow(LGHorizonBaseRecording):
286
301
  recording_season_json["channelId"],
287
302
  recording_season_json["type"],
288
303
  )
289
- if self.type == RECORDING_TYPE_SEASON:
290
- self.showId = recording_season_json["showId"]
304
+ if self.recording_type == RECORDING_TYPE_SEASON:
305
+ self.show_id = recording_season_json["showId"]
291
306
  else:
292
- self.showId = recording_season_json["id"]
307
+ self.show_id = recording_season_json["id"]
293
308
 
294
309
 
295
310
  class LGHorizonVod:
311
+ """LGHorizon video on demand."""
312
+
296
313
  title: str = None
297
314
  image: str = None
298
315
  duration: float = None
@@ -303,6 +320,8 @@ class LGHorizonVod:
303
320
 
304
321
 
305
322
  class LGHorizonApp:
323
+ """LGHorizon App."""
324
+
306
325
  title: str = None
307
326
  image: str = None
308
327
 
@@ -314,15 +333,18 @@ class LGHorizonApp:
314
333
 
315
334
 
316
335
  class LGHorizonMqttClient:
317
- _brokerUrl: str = None
336
+ """LGHorizon MQTT client."""
337
+
338
+ _broker_url: str = None
318
339
  _mqtt_client: mqtt.Client
319
340
  _auth: LGHorizonAuth
320
- clientId: str = None
341
+ client_id: str = None
321
342
  _on_connected_callback: Callable = None
322
343
  _on_message_callback: Callable[[str, str], None] = None
323
344
 
324
345
  @property
325
346
  def is_connected(self):
347
+ """Is client connected."""
326
348
  return self._mqtt_client.is_connected
327
349
 
328
350
  def __init__(
@@ -333,80 +355,95 @@ class LGHorizonMqttClient:
333
355
  on_message_callback: Callable[[str], None] = None,
334
356
  ):
335
357
  self._auth = auth
336
- self._brokerUrl = mqtt_broker_url.replace("wss://", "").replace(":443/mqtt", "")
337
- self.clientId = make_id()
338
- self._mqtt_client = mqtt.Client(self.clientId, transport="websockets")
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
+ mqtt.CallbackAPIVersion.VERSION1,
364
+ client_id=self.client_id,
365
+ transport="websockets",
366
+ )
339
367
 
340
368
  self._mqtt_client.ws_set_options(
341
369
  headers={"Sec-WebSocket-Protocol": "mqtt, mqttv3.1, mqttv3.11"}
342
370
  )
343
- self._mqtt_client.username_pw_set(self._auth.householdId, self._auth.mqttToken)
371
+ self._mqtt_client.username_pw_set(
372
+ self._auth.household_id, self._auth.mqtt_token
373
+ )
344
374
  self._mqtt_client.tls_set()
345
375
  self._mqtt_client.enable_logger(_logger)
346
376
  self._mqtt_client.on_connect = self._on_mqtt_connect
347
377
  self._on_connected_callback = on_connected_callback
348
378
  self._on_message_callback = on_message_callback
349
379
 
350
- def _on_mqtt_connect(self, client, userdata, flags, resultCode):
351
- if resultCode == 0:
380
+ def _on_mqtt_connect(self, client, userdata, flags, result_code): # pylint: disable=unused-argument
381
+ if result_code == 0:
352
382
  self._mqtt_client.on_message = self._on_client_message
353
- self._mqtt_client.subscribe(self._auth.householdId)
354
- self._mqtt_client.subscribe(self._auth.householdId + "/#")
355
- self._mqtt_client.subscribe(self._auth.householdId + "/" + self.clientId)
356
- self._mqtt_client.subscribe(self._auth.householdId + "/+/status")
357
- self._mqtt_client.subscribe(self._auth.householdId + "/+/networkRecordings")
383
+ self._mqtt_client.subscribe(self._auth.household_id)
384
+ self._mqtt_client.subscribe(self._auth.household_id + "/#")
385
+ self._mqtt_client.subscribe(self._auth.household_id + "/" + self.client_id)
386
+ self._mqtt_client.subscribe(self._auth.household_id + "/+/status")
387
+ self._mqtt_client.subscribe(
388
+ self._auth.household_id + "/+/networkRecordings"
389
+ )
358
390
  self._mqtt_client.subscribe(
359
- self._auth.householdId + "/+/networkRecordings/capacity"
391
+ self._auth.household_id + "/+/networkRecordings/capacity"
360
392
  )
361
- self._mqtt_client.subscribe(self._auth.householdId + "/+/localRecordings")
393
+ self._mqtt_client.subscribe(self._auth.household_id + "/+/localRecordings")
362
394
  self._mqtt_client.subscribe(
363
- self._auth.householdId + "/+/localRecordings/capacity"
395
+ self._auth.household_id + "/+/localRecordings/capacity"
364
396
  )
365
- self._mqtt_client.subscribe(self._auth.householdId + "/watchlistService")
366
- self._mqtt_client.subscribe(self._auth.householdId + "/purchaseService")
397
+ self._mqtt_client.subscribe(self._auth.household_id + "/watchlistService")
398
+ self._mqtt_client.subscribe(self._auth.household_id + "/purchaseService")
367
399
  self._mqtt_client.subscribe(
368
- self._auth.householdId + "/personalizationService"
400
+ self._auth.household_id + "/personalizationService"
369
401
  )
370
- self._mqtt_client.subscribe(self._auth.householdId + "/recordingStatus")
402
+ self._mqtt_client.subscribe(self._auth.household_id + "/recordingStatus")
371
403
  self._mqtt_client.subscribe(
372
- self._auth.householdId + "/recordingStatus/lastUserAction"
404
+ self._auth.household_id + "/recordingStatus/lastUserAction"
373
405
  )
374
406
  if self._on_connected_callback:
375
407
  self._on_connected_callback()
376
- elif resultCode == 5:
408
+ elif result_code == 5:
377
409
  self._mqtt_client.username_pw_set(
378
- self._auth.householdId, self._auth.mqttToken
410
+ self._auth.household_id, self._auth.mqtt_token
379
411
  )
380
412
  self.connect()
381
413
  else:
382
414
  _logger.error(
383
- f"Cannot connect to MQTT server with resultCode: {resultCode}"
415
+ "Cannot connect to MQTT server with resultCode: %s", result_code
384
416
  )
385
417
 
386
418
  def connect(self) -> None:
387
- self._mqtt_client.connect(self._brokerUrl, 443)
419
+ """Connect the client."""
420
+ self._mqtt_client.connect(self._broker_url, 443)
388
421
  self._mqtt_client.loop_start()
389
422
 
390
- def _on_client_message(self, client, userdata, message):
423
+ def _on_client_message(self, client, userdata, message): # pylint: disable=unused-argument
391
424
  """Handle messages received by mqtt client."""
392
- _logger.debug(f"Received MQTT message. Topic: {message.topic}")
393
- jsonPayload = json.loads(message.payload)
394
- _logger.debug(f"Message: {jsonPayload}")
425
+ _logger.debug("Received MQTT message. Topic: %s", message.topic)
426
+ json_payload = json.loads(message.payload)
427
+ _logger.debug("Message: %s", json_payload)
395
428
  if self._on_message_callback:
396
- self._on_message_callback(jsonPayload, message.topic)
429
+ self._on_message_callback(json_payload, message.topic)
397
430
 
398
431
  def publish_message(self, topic: str, json_payload: str) -> None:
432
+ """Publish a MQTT message."""
399
433
  self._mqtt_client.publish(topic, json_payload, qos=2)
400
434
 
401
435
  def disconnect(self) -> None:
402
- if self._mqtt_client.is_connected:
436
+ """Disconnect the client."""
437
+ if self._mqtt_client.is_connected():
403
438
  self._mqtt_client.disconnect()
404
439
 
405
440
 
406
441
  class LGHorizonBox:
407
- deviceId: str = None
408
- hashedCPEId: str = None
409
- deviceFriendlyName: str = None
442
+ """The LGHorizon box."""
443
+
444
+ device_id: str = None
445
+ hashed_cpe_id: str = None
446
+ device_friendly_name: str = None
410
447
  state: str = None
411
448
  playing_info: LGHorizonPlayingInfo = None
412
449
  manufacturer: str = None
@@ -427,9 +464,9 @@ class LGHorizonBox:
427
464
  auth: LGHorizonAuth,
428
465
  channels: Dict[str, LGHorizonChannel],
429
466
  ):
430
- self.deviceId = box_json["deviceId"]
431
- self.hashedCPEId = box_json["hashedCPEId"]
432
- self.deviceFriendlyName = box_json["settings"]["deviceFriendlyName"]
467
+ self.device_id = box_json["deviceId"]
468
+ self.hashed_cpe_id = box_json["hashedCPEId"]
469
+ self.device_friendly_name = box_json["settings"]["deviceFriendlyName"]
433
470
  self._mqtt_client = mqtt_client
434
471
  self._auth = auth
435
472
  self._channels = channels
@@ -439,20 +476,23 @@ class LGHorizonBox:
439
476
  self.model = platform_type["model"]
440
477
 
441
478
  def update_channels(self, channels: Dict[str, LGHorizonChannel]):
479
+ """Update the channels list."""
442
480
  self._channels = channels
443
481
 
444
482
  def register_mqtt(self) -> None:
483
+ """Register the mqtt connection."""
445
484
  if not self._mqtt_client.is_connected:
446
485
  raise Exception("MQTT client not connected.")
447
- topic = f"{self._auth.householdId}/{self._mqtt_client.clientId}/status"
486
+ topic = f"{self._auth.household_id}/{self._mqtt_client.client_id}/status"
448
487
  payload = {
449
- "source": self._mqtt_client.clientId,
488
+ "source": self._mqtt_client.client_id,
450
489
  "state": ONLINE_RUNNING,
451
490
  "deviceType": "HGO",
452
491
  }
453
492
  self._mqtt_client.publish_message(topic, json.dumps(payload))
454
493
 
455
494
  def set_callback(self, change_callback: Callable) -> None:
495
+ """Set a callback function."""
456
496
  self._change_callback = change_callback
457
497
 
458
498
  def update_state(self, payload):
@@ -464,25 +504,27 @@ class LGHorizonBox:
464
504
  if state == ONLINE_STANDBY:
465
505
  self.playing_info.reset()
466
506
  if self._change_callback:
467
- self._change_callback(self.deviceId)
507
+ self._change_callback(self.device_id)
468
508
  else:
469
509
  self._request_settop_box_state()
470
510
  self._request_settop_box_recording_capacity()
471
511
 
472
512
  def update_recording_capacity(self, payload) -> None:
473
- if not "CPE.capacity" in payload or not "used" in payload:
513
+ """Updates the recording capacity."""
514
+ if "CPE.capacity" not in payload or "used" not in payload:
474
515
  return
475
516
  self.recording_capacity = payload["used"]
476
517
 
477
518
  def update_with_replay_event(
478
519
  self, source_type: str, event: LGHorizonReplayEvent, channel: LGHorizonChannel
479
520
  ) -> None:
521
+ """Update box with replay event."""
480
522
  self.playing_info.set_source_type(source_type)
481
523
  self.playing_info.set_channel(channel.id)
482
524
  self.playing_info.set_channel_title(channel.title)
483
525
  title = event.title
484
- if event.episodeName:
485
- title += f": {event.episodeName}"
526
+ if event.episode_name:
527
+ title += f": {event.episode_name}"
486
528
  self.playing_info.set_title(title)
487
529
  self.playing_info.set_image(channel.stream_image)
488
530
  self.playing_info.reset_progress()
@@ -498,6 +540,7 @@ class LGHorizonBox:
498
540
  last_speed_change: float,
499
541
  relative_position: float,
500
542
  ) -> None:
543
+ """Update box with recording."""
501
544
  self.playing_info.set_source_type(source_type)
502
545
  self.playing_info.set_channel(channel.id)
503
546
  self.playing_info.set_channel_title(channel.title)
@@ -519,6 +562,7 @@ class LGHorizonBox:
519
562
  last_speed_change: float,
520
563
  relative_position: float,
521
564
  ) -> None:
565
+ """Update box with vod."""
522
566
  self.playing_info.set_source_type(source_type)
523
567
  self.playing_info.set_channel(None)
524
568
  self.playing_info.set_channel_title(None)
@@ -531,6 +575,7 @@ class LGHorizonBox:
531
575
  self._trigger_callback()
532
576
 
533
577
  def update_with_app(self, source_type: str, app: LGHorizonApp) -> None:
578
+ """Update box with app."""
534
579
  self.playing_info.set_source_type(source_type)
535
580
  self.playing_info.set_channel(None)
536
581
  self.playing_info.set_channel_title(app.title)
@@ -541,8 +586,8 @@ class LGHorizonBox:
541
586
 
542
587
  def _trigger_callback(self):
543
588
  if self._change_callback:
544
- _logger.debug(f"Callback called from box {self.deviceId}")
545
- self._change_callback(self.deviceId)
589
+ _logger.debug("Callback called from box %s", self.device_id)
590
+ self._change_callback(self.device_id)
546
591
 
547
592
  def turn_on(self) -> None:
548
593
  """Turn the settop box on."""
@@ -620,10 +665,10 @@ class LGHorizonBox:
620
665
  )
621
666
 
622
667
  self._mqtt_client.publish_message(
623
- f"{self._auth.householdId}/{self.deviceId}", payload
668
+ f"{self._auth.household_id}/{self.device_id}", payload
624
669
  )
625
670
 
626
- def play_recording(self, recordingId):
671
+ def play_recording(self, recording_id):
627
672
  """Play recording."""
628
673
  payload = (
629
674
  '{"id":"'
@@ -632,11 +677,11 @@ class LGHorizonBox:
632
677
  + self._mqtt_client.clientId
633
678
  + '","friendlyDeviceName":"Home Assistant"},'
634
679
  + '"status":{"sourceType":"nDVR","source":{"recordingId":"'
635
- + recordingId
680
+ + recording_id
636
681
  + '"},"relativePosition":0}}'
637
682
  )
638
683
  self._mqtt_client.publish_message(
639
- f"{self._auth.householdId}/{self.deviceId}", payload
684
+ f"{self._auth.household_id}/{self.device_id}", payload
640
685
  )
641
686
 
642
687
  def send_key_to_box(self, key: str) -> None:
@@ -647,7 +692,7 @@ class LGHorizonBox:
647
692
  + '","eventType":"keyDownUp"}}'
648
693
  )
649
694
  self._mqtt_client.publish_message(
650
- f"{self._auth.householdId}/{self.deviceId}", payload
695
+ f"{self._auth.household_id}/{self.device_id}", payload
651
696
  )
652
697
 
653
698
  def _set_unknown_channel_info(self) -> None:
@@ -661,29 +706,31 @@ class LGHorizonBox:
661
706
 
662
707
  def _request_settop_box_state(self) -> None:
663
708
  """Send mqtt message to receive state from settop box."""
664
- topic = f"{self._auth.householdId}/{self.deviceId}"
709
+ topic = f"{self._auth.household_id}/{self.device_id}"
665
710
  payload = {
666
711
  "id": make_id(8),
667
712
  "type": "CPE.getUiStatus",
668
- "source": self._mqtt_client.clientId,
713
+ "source": self._mqtt_client.client_id,
669
714
  }
670
715
  self._mqtt_client.publish_message(topic, json.dumps(payload))
671
716
 
672
717
  def _request_settop_box_recording_capacity(self) -> None:
673
718
  """Send mqtt message to receive state from settop box."""
674
- topic = f"{self._auth.householdId}/{self.deviceId}"
719
+ topic = f"{self._auth.household_id}/{self.device_id}"
675
720
  payload = {
676
721
  "id": make_id(8),
677
722
  "type": "CPE.capacity",
678
- "source": self._mqtt_client.clientId,
723
+ "source": self._mqtt_client.client_id,
679
724
  }
680
725
  self._mqtt_client.publish_message(topic, json.dumps(payload))
681
726
 
682
727
 
683
728
  class LGHorizonProfile:
729
+ """LGHorizon profile."""
730
+
684
731
  profile_id: str = None
685
732
  name: str = None
686
- favorite_channels: [] = None
733
+ favorite_channels: list[str] = None
687
734
 
688
735
  def __init__(self, json_payload):
689
736
  self.profile_id = json_payload["profileId"]
@@ -692,18 +739,20 @@ class LGHorizonProfile:
692
739
 
693
740
 
694
741
  class LGHorizonCustomer:
695
- customerId: str = None
696
- hashedCustomerId: str = None
697
- countryId: str = None
698
- cityId: int = 0
699
- settop_boxes: [] = None
742
+ """LGHorizon customer"""
743
+
744
+ customer_id: str = None
745
+ hashed_customer_id: str = None
746
+ country_id: str = None
747
+ city_id: int = 0
748
+ settop_boxes: list[str] = None
700
749
  profiles: Dict[str, LGHorizonProfile] = {}
701
750
 
702
751
  def __init__(self, json_payload):
703
- self.customerId = json_payload["customerId"]
704
- self.hashedCustomerId = json_payload["hashedCustomerId"]
705
- self.countryId = json_payload["countryId"]
706
- self.cityId = json_payload["cityId"]
752
+ self.customer_id = json_payload["customerId"]
753
+ self.hashed_customer_id = json_payload["hashedCustomerId"]
754
+ self.country_id = json_payload["countryId"]
755
+ self.city_id = json_payload["cityId"]
707
756
  if "assignedDevices" in json_payload:
708
757
  self.settop_boxes = json_payload["assignedDevices"]
709
758
  if "profiles" in json_payload:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lghorizon
3
- Version: 0.7.5b2
3
+ Version: 0.8.0b1
4
4
  Summary: Python client for Liberty Global Horizon settop boxes
5
5
  Home-page: https://github.com/sholofly/LGHorizon-python
6
6
  Author: Rudolf Offereins
@@ -20,7 +20,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
20
  Requires-Python: >=3.9
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
- Requires-Dist: paho-mqtt <2.0.0
23
+ Requires-Dist: paho-mqtt >=2.0.0
24
24
  Requires-Dist: requests >=2.22.0
25
25
  Requires-Dist: backoff >=1.9.0
26
26
 
@@ -0,0 +1,12 @@
1
+ lghorizon/__init__.py,sha256=XqePG0hj8NnudOcfCtEWqPsWP5l2headzrQhkfuDlv8,544
2
+ lghorizon/const.py,sha256=8P-4cwRRg0wKtrzr5DTOuGb618HKjGRPMnIfmc6-VSM,4700
3
+ lghorizon/exceptions.py,sha256=-6v55KDTogBldGAg1wV9Mrxm5L5BsaVguhBgVMOeJHk,404
4
+ lghorizon/helpers.py,sha256=W7ppV9MJ1MyXH_49GliNtiu_kA7h1KRzMdmRdrC5kaw,266
5
+ lghorizon/lghorizon_api.py,sha256=xl2ISDPgkuLrdZtxXIBfBYjg2JfKIwBMY-BbMMqmmU0,22343
6
+ lghorizon/models.py,sha256=NSgQd2Zb-YE8VkbhJP6g5mHKMGEBFQiIB1DYqa7IhSI,25990
7
+ lghorizon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ lghorizon-0.8.0b1.dist-info/LICENSE,sha256=6Dh2tur1gMX3r3rITjVwUONBEJxyyPZDY8p6DZXtimE,1059
9
+ lghorizon-0.8.0b1.dist-info/METADATA,sha256=i5c5NMM5ODmRHrYjhNpd5k_rKE2FysyOVcNFoOdFNUI,1040
10
+ lghorizon-0.8.0b1.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
11
+ lghorizon-0.8.0b1.dist-info/top_level.txt,sha256=usii76_AxGfPI6gjrrh-NyZxcQQuF1B8_Q9kd7sID8Q,10
12
+ lghorizon-0.8.0b1.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- lghorizon/__init__.py,sha256=XqePG0hj8NnudOcfCtEWqPsWP5l2headzrQhkfuDlv8,544
2
- lghorizon/const.py,sha256=8P-4cwRRg0wKtrzr5DTOuGb618HKjGRPMnIfmc6-VSM,4700
3
- lghorizon/exceptions.py,sha256=-6v55KDTogBldGAg1wV9Mrxm5L5BsaVguhBgVMOeJHk,404
4
- lghorizon/helpers.py,sha256=ZWpi7B3hBvwGV02KWQQHVyj7FLLUDtIvKc-Iqsj5VHA,263
5
- lghorizon/lghorizon_api.py,sha256=ZrHHp-fSYLs566_KKi10n52P6T4vlaRyPh50UJqZWno,21993
6
- lghorizon/models.py,sha256=S_Pq8z9PpaN10b17aoi2j-mCIdZf-TK411pBGRPDkvc,24542
7
- lghorizon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- lghorizon-0.7.5b2.dist-info/LICENSE,sha256=6Dh2tur1gMX3r3rITjVwUONBEJxyyPZDY8p6DZXtimE,1059
9
- lghorizon-0.7.5b2.dist-info/METADATA,sha256=ftEhgIKdN1Nq257eTBw8-DPhqXHquorzEpl2IJtknJU,1039
10
- lghorizon-0.7.5b2.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
11
- lghorizon-0.7.5b2.dist-info/top_level.txt,sha256=usii76_AxGfPI6gjrrh-NyZxcQQuF1B8_Q9kd7sID8Q,10
12
- lghorizon-0.7.5b2.dist-info/RECORD,,