pax_global_header00006660000000000000000000000064147151644210014517gustar00rootroot0000000000000052 comment=6a7a93e4b0de705cb5c4f06cd53d80ac0da31d73 ha-philipsjs-3.3.3/000077500000000000000000000000001471516442100141205ustar00rootroot00000000000000ha-philipsjs-3.3.3/.github/000077500000000000000000000000001471516442100154605ustar00rootroot00000000000000ha-philipsjs-3.3.3/.github/workflows/000077500000000000000000000000001471516442100175155ustar00rootroot00000000000000ha-philipsjs-3.3.3/.github/workflows/ci.yaml000066400000000000000000000012171471516442100207750ustar00rootroot00000000000000name: CI on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: ["3.10", "3.11"] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install flake8 pytest python -m pip install -e .[tests] - name: Test with pytest run: | pytest -vv --cov haphilipsjsha-philipsjs-3.3.3/.github/workflows/python-publish.yml000066400000000000000000000015401471516442100232250ustar00rootroot00000000000000# This workflow will upload a Python Package using Twine when a release is created # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries name: Upload Python Package on: release: types: [created] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.x' - name: Install dependencies run: | python -m pip install --upgrade pip pip install setuptools wheel twine - name: Build and publish env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | python setup.py sdist bdist_wheel twine upload dist/* ha-philipsjs-3.3.3/.gitignore000066400000000000000000000001451471516442100161100ustar00rootroot00000000000000__pycache__ *.egg-info .pytest_cache dist .vscode .coverage coverage.xml test-results.xml venv build ha-philipsjs-3.3.3/LICENSE000066400000000000000000000020551471516442100151270ustar00rootroot00000000000000MIT License Copyright (c) 2016 Daniel Perna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ha-philipsjs-3.3.3/README.md000066400000000000000000000027071471516442100154050ustar00rootroot00000000000000# ha-philipsjs Wrapper library to integrate Philips TVs with jointSPACE API into Home-Assistant This is just being used to wrap the API into a nice object. Nothing fancy. * Support version 1 and 6 of the jointspace API * Support pairing process * Python typing information on endpoints * NotifyChange support for supported tv's Print system data ```python from haphilipsjs import PhilipsTV tv = PhilipsTV("tv2.example.com", 6) tv.getSystem() print(tv.system) ``` Pairing with TV ```python from haphilipsjs import PhilipsTV tv = PhilipsTV("tv2.example.com", 6) tv.getSystem() state = tv.pairRequest("my_test_app", "My Test App", "mox", "OSX", "native") pin = input("Enter pin:") username, password = tv.pairGrant(state, pin) print(username) print(password) ``` Command line utility: ``` usage: python -m haphilipsjs [-h] [-d] -i HOST -a API [-u USERNAME] [-p PASSWORD] {status,monitor,ambilight,pair} ... positional arguments: {status,monitor,ambilight,pair} commands status Show current tv status monitor Monitor current tv status ambilight Control ambilight pair Pair with tv optional arguments: -h, --help show this help message and exit -d, --debug Debug output -i HOST, --host HOST -a API, --api API -u USERNAME, --username USERNAME Username -p PASSWORD, --password PASSWORD Password ``` ha-philipsjs-3.3.3/haphilipsjs/000077500000000000000000000000001471516442100164365ustar00rootroot00000000000000ha-philipsjs-3.3.3/haphilipsjs/__init__.py000066400000000000000000001454231471516442100205600ustar00rootroot00000000000000from typing import Any, Dict, List, Optional, Tuple, TypeVar, Union, cast, overload, Iterable, TypeVar import httpx import logging import json import itertools from urllib.parse import quote from secrets import token_bytes, token_hex from base64 import b64decode, b64encode from functools import wraps from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives.padding import PKCS7 from cryptography.hazmat.primitives.hashes import SHA1 from cryptography.hazmat.primitives.hmac import HMAC from .typing import ( ActivitesTVType, AmbilightCurrentConfiguration, AmbilightLayersType, AmbilightSupportedStyleType, AmbilightSupportedStylesType, ApplicationIntentType, ApplicationsType, ChannelDbTv, ChannelListType, ChannelsCurrentType, ChannelsType, ChannelType, ContextType, FavoriteListType, RecordingsListed, MenuItemsSettingsCurrent, MenuItemsSettingsCurrentPost, MenuItemsSettingsCurrentValueValue, MenuItemsSettingsEntry, MenuItemsSettingsNode, MenuItemsSettingsStructure, MenuItemsSettingsUpdate, MenuItemsSettingsValueData, Strings, StringsRequest, SystemType, ApplicationType, ) from .auth import CachedDigestAuth LOG = logging.getLogger(__name__) TIMEOUT = 20.0 TIMEOUT_CONNECT = 5.0 TIMEOUT_NOTIFYREAD = 130 DEFAULT_API_VERSION = 1 AUTH_SHARED_KEY = b64decode( "ZmVay1EQVFOaZhwQ4Kv81ypLAZNczV9sG4KkseXWn1NEk6cXmPKO/MCa9sryslvLCFMnNe4Z4CPXzToowvhHvA==" ) """Key used for hmac signatures and decoding of cbc data.""" TV_PLAYBACK_INTENTS = [ { "component": { "className": "org.droidtv.playtv.PlayTvActivity", "packageName": "org.droidtv.playtv", } } ] HTTP_PORT = 1925 HTTPS_PORT = 1926 MAXIMUM_ITEMS_IN_REQUEST = 50 IGNORED_JSON_RESPONSES = {"", "Context Service not started", "}"} def hmac_signature(key: bytes, timestamp: str, data: str): """Calculate a timestamped signature.""" hmac = HMAC(key, SHA1()) hmac.update(timestamp.encode("utf-8")) hmac.update(data.encode("utf-8")) return b64encode(hmac.finalize()).decode("utf-8") def cbc_decode(key: bytes, data: str): """Decoded encrypted fields based on shared key.""" if data == "": return "" raw = b64decode(data) assert len(raw) >= 16, f"Lenght of data too short: '{data}'" decryptor = Cipher(algorithms.AES(key[0:16]), modes.CBC(raw[0:16])).decryptor() unpadder = PKCS7(128).unpadder() result = decryptor.update(raw[16:]) + decryptor.finalize() result = unpadder.update(result) + unpadder.finalize() return result.decode("utf-8") def cbc_encode(key: bytes, data: str): """Decoded encrypted fields based on shared key.""" raw = data.encode("utf-8") iv = token_bytes(16) encryptor = Cipher(algorithms.AES(key[0:16]), modes.CBC(iv)).encryptor() padder = PKCS7(128).padder() result = padder.update(raw) + padder.finalize() result = encryptor.update(result) + encryptor.finalize() return b64encode(iv + result).decode("utf-8") def decode_xtv_json(text: str): if text in IGNORED_JSON_RESPONSES: LOG.debug("Ignoring invalid json %s", text) return {} try: data = json.loads(text) except json.decoder.JSONDecodeError: LOG.debug("Invalid json received, trying adjusted version") text = text.replace("{,", "{") text = text.replace(",}", "}") while (p := text.find(",,")) >= 0: text = text[:p] + text[p + 1 :] try: data = json.loads(text) except json.decoder.JSONDecodeError as exception: raise NoneJsonData(text) from exception return data def decode_xtv_response(response: httpx.Response): try: text = response.text except UnicodeDecodeError: LOG.warning("Unable to decode unicode on endpoint, ignoring", exc_info=True) return {} if not response.headers.get("content-type", "").startswith("application/json"): if text: LOG.warning("Non json data: %s", text) return {} return decode_xtv_json(text) _T = TypeVar("_T") def chunked_iterator(size: int, iterable: Iterable[_T]) -> Iterable[Iterable[_T]]: """Create a chain of iterators that each will give a chunk of the original.""" step = iter(iterable) while True: chunk_it = itertools.islice(step, size) try: first_el = next(chunk_it) except StopIteration: return yield itertools.chain((first_el,), chunk_it) PASSTHROUGH_URI = "content://android.media.tv/passthrough" def passthrough_uri(data): return f"{PASSTHROUGH_URI}/{quote(data, safe='')}" CHANNEL_URI = "content://android.media.tv/channel" def channel_uri(channel): uri = CHANNEL_URI if channel is not None: uri += f"/{channel}" return uri class GeneralFailure(Exception): """Base class for component failures.""" class ConnectionFailure(GeneralFailure): """Failed to connect to tv it's likely turned off.""" class ProtocolFailure(GeneralFailure): """Wrapper to contain erros that are the server closing a connection before response.""" class AutenticationFailure(GeneralFailure): """Wrapper to contain failures due to authentication.""" class PairingFailure(GeneralFailure): def __init__(self, data): super().__init__(f"Failed to start pairing: {data}") self.data = data class NoneJsonData(GeneralFailure): """API Returned non json data when json was expected.""" def __init__(self, data): super().__init__(f"Non json data received: {data}") self.data = data T = TypeVar("T") def handle_httpx_exceptions(f): """Wrap up httpx exceptions in our wanted variants.""" @wraps(f) async def wrapper(*args, **kwds): try: try: return await f(*args, **kwds) except httpx.RemoteProtocolError as err: LOG.debug("%r. We retry once, could be a reused session that was closed", err) return await f(*args, **kwds) except (httpx.ConnectTimeout, httpx.ConnectError) as err: raise ConnectionFailure(err) from err except (httpx.ProtocolError, httpx.ReadError) as err: raise ProtocolFailure(err) from err except httpx.HTTPError as err: raise GeneralFailure(err) from err return wrapper class PhilipsTV(object): channels: ChannelsType """All available channels, with ccid as key.""" def __init__( self, host=None, api_version=DEFAULT_API_VERSION, secured_transport=None, username=None, password=None, verify=False, auth_shared_key=None, system = None, limits = None, ): self._host = host self._connfail = 0 self.api_version = int(api_version) self.on = False self.name: Optional[str] = None self.system: Optional[SystemType] = system self.strings: dict[str, str] = {} self.sources = {} self.source_id = None self.audio_volume = None self.channels = {} self.channel: Optional[Union[ActivitesTVType, ChannelsCurrentType]] = None self.channel_lists: Dict[str, ChannelListType] = {} self.favorite_lists: Dict[str, FavoriteListType] = {} self.applications: Dict[str, ApplicationType] = {} self.application: Optional[ApplicationIntentType] = None self.context: Optional[ContextType] = None self.screenstate: Optional[str] = None self.settings: Optional[MenuItemsSettingsStructure] = None self.settings_version = 0 self.ambilight_topology = None self.ambilight_mode_set = None self.ambilight_mode_raw: Optional[str] = None self.ambilight_cached: Optional[AmbilightLayersType] = None self.ambilight_measured: Optional[AmbilightLayersType] = None self.ambilight_processed: Optional[AmbilightLayersType] = None self.ambilight_power_raw: Optional[Dict] = None self.ambilight_styles: Dict[str, AmbilightSupportedStyleType] = {} self.ambilight_current_configuration: Optional[ AmbilightCurrentConfiguration ] = None self.recordings_list: Optional[RecordingsListed] = None self.huelamp_power: Optional[str] = None self.powerstate = None if auth_shared_key: self.auth_shared_key = auth_shared_key else: self.auth_shared_key = AUTH_SHARED_KEY if secured_transport or self.secured_transport: self.protocol = "https" else: self.protocol = "http" timeout = httpx.Timeout(timeout=TIMEOUT, connect=TIMEOUT_CONNECT) if limits is None: limits = httpx.Limits(max_keepalive_connections=3, max_connections=3) self.session = httpx.AsyncClient(limits=limits, timeout=timeout, verify=False) self.session.headers["Accept"] = "application/json" if username and password: self.session.auth = CachedDigestAuth(username, password) @property def quirk_playpause_spacebar(self): """Does this tv need workaround for playpause.""" if self.system: return self.system.get("os_type", "").startswith("MSAF_") else: return False @property def quirk_ambilight_styles_menuitems(self): if self.system: return self.system.get("os_type", "").startswith("MSAF_") else: return False @property def os_type(self): if self.system is None: return None # android system have in direclty in root os_type = self.system.get("os_type") if os_type: return os_type # saphi stores in in features os_type = ( self.system.get("featuring", {}).get("systemfeatures", {}).get("os_type") ) if os_type: return os_type return None @property def quirk_ambilight_mode_ignored(self): """Return if this tv need workaround for ambilight bugs. XTV app have bugs with their mode management for ambilight. It will forgot to actuate the command to set mode back to internal. But will actually do that if you give it an invalid mode. It will also not report a correct ambilight mode after being changed by call. So we need to remember last set mode. Versions known affected: - Android - 9.0.0 - Saphi - 4.6.0.2 Version known good - Legacy - QF1EU-0.150.102.0 """ os_type = self.os_type if os_type: if os_type.startswith("MSAF_"): return True if os_type == "Linux": return True return False @property def pairing_type(self): if self.system: return ( self.system.get("featuring", {}) .get("systemfeatures", {}) .get("pairing_type") ) else: return None @property def secured_transport(self) -> Optional[bool]: if self.system: return ( self.system.get("featuring", {}) .get("systemfeatures", {}) .get("secured_transport") in ("true", True) ) else: return None @property def notify_change_supported(self) -> Optional[str]: if self.system: return self.system.get("notifyChange", None) else: return None @overload def json_feature_supported(self, type: str) -> Optional[List[str]]: ... @overload def json_feature_supported(self, type: str, value: str) -> Optional[bool]: ... def json_feature_supported(self, type: str, value: Optional[str] = None): if self.system: features = cast( List[str], self.system.get("featuring", {}).get("jsonfeatures", {}).get(type, []), ) if value: return value in features else: return features else: return None @property def api_version_detected(self) -> Optional[int]: if self.system: return cast(Optional[int], self.system.get("api_version", {}).get("Major")) else: return None @property def channel_active(self): if self.context and "level1" in self.context: return self.context["level1"] in ("WatchTv", "WatchSatellite") if self.context and "activity" in self.context: return self.context["activity"] in ("WatchTv", "WatchSatellite") if self.application: return self.application in TV_PLAYBACK_INTENTS if self.source_id in ("tv", "11", None): return self.channel_id is not None return False @property def application_id(self): if self.application and "component" in self.application: component = self.application["component"] app_id = f"{component.get('className', 'None')}-{component.get('packageName', 'None')}" if app_id in self.applications: return app_id else: return None else: return None @property def min_volume(self): if self.audio_volume: return int(self.audio_volume["min"]) else: return None @property def max_volume(self): if self.audio_volume: return int(self.audio_volume["max"]) else: return None @property def volume(self): if self.audio_volume and int(self.audio_volume["max"]): return self.audio_volume["current"] / int(self.audio_volume["max"]) else: return None @property def muted(self): if self.audio_volume: return self.audio_volume["muted"] else: return None @property def channel_id(self): if self.api_version >= 5: r = cast(Optional[ActivitesTVType], self.channel) if not r: return None ccid = r.get("channel", {}).get("ccid") # it could be empty if HDMI is set if not ccid: return None return str(ccid) else: r = cast(Optional[ChannelsCurrentType], self.channel) if not r: return None if not self.channels.get(r["id"]): pos = r["id"].find("_") if pos > 0: return r["id"][pos + 1 :] return r["id"] @property def channel_list_id(self) -> str: if self.api_version >= 5: if not self.channel: return "all" r = cast(ActivitesTVType, self.channel) channel_list = r.get("channelList") if not channel_list: return "all" return channel_list.get("id", "all") return "all" @property def channels_current(self) -> List[ChannelType]: """All channels in the current favorite list.""" if self.api_version >= 5: favorite_list = self.favorite_lists.get(self.channel_list_id) if not favorite_list: return list(self.channels.values()) return [ { **channel, "preset": favorite.get("preset", "") } for favorite in favorite_list.get("channels", []) if (channel := self.channels.get(str(favorite.get("ccid")))) ] else: return [ { **channel, "ccid": key } for key, channel in self.channels.items() ] @property def ambilight_modes(self): modes = ["internal", "manual", "expert"] if self.api_version >= 6: features = self.json_feature_supported("ambilight") if features: if "LoungeLight" in features: modes.append("lounge") return modes else: return [] else: return modes @property def ambilight_mode(self) -> Optional[str]: if self.quirk_ambilight_mode_ignored: if self.ambilight_mode_set: return self.ambilight_mode_set return self.ambilight_mode_raw @property def ambilight_power(self) -> Optional[str]: if self.api_version >= 5: if self.ambilight_power_raw is None: return None return self.ambilight_power_raw.get("power") if self.api_version == 1: if self.ambilight_cached is None: return None if self.ambilight_mode == "manual": if any( color != 0 for layer in self.ambilight_cached.values() for side in layer.values() for pixel in side.values() for color in pixel.values() ): return "On" else: return "Off" else: return "On" return None async def aclose(self) -> None: await self.session.aclose() def _url(self, path, protocol = None): if protocol is None: protocol = self.protocol if protocol == "https": port = HTTPS_PORT else: port = HTTP_PORT return f"{protocol}://{self._host}:{port}/{self.api_version}/{path}" @handle_httpx_exceptions async def getReq(self, path, protocol = None) -> Optional[Dict]: resp = await self.session.get(self._url(path, protocol = protocol)) if resp.status_code == 401: raise AutenticationFailure("Authenticaion failed to device") if resp.status_code != 200: LOG.debug("Get failed: %s -> %d %s", path, resp.status_code, resp.text) return None LOG.debug("Get succeded: %s -> %s", path, resp.text) return decode_xtv_response(resp) @handle_httpx_exceptions async def _getBinary(self, path: str) -> Tuple[Optional[bytes], Optional[str]]: resp = await self.session.get(self._url(path)) if resp.status_code == 401: raise AutenticationFailure("Authenticaion failed to device") if resp.status_code != 200: return None, None return resp.content, resp.headers.get("content-type") @handle_httpx_exceptions async def postReq(self, path: str, data: Any, timeout=None, protocol=None) -> Optional[Dict]: try: resp = await self.session.post(self._url(path, protocol), json=data, timeout=timeout) if resp.status_code == 401: raise AutenticationFailure("Authenticaion failed to device") if resp.status_code == 200: LOG.debug("Post succeded: %s -> %s", data, resp.text) return decode_xtv_response(resp) LOG.warning("Post failed: %s -> %s", data, resp.text) return None except httpx.ReadTimeout: LOG.debug("Read time out on postReq", exc_info=True) return None async def pairRequest( self, app_id: str, app_name: str, device_name: str, device_os: str, type: str, device_id: Optional[str] = None, ): """Start up a pairing request.""" if device_id is None: device_id = token_hex(16) device = { "device_name": device_name, "device_os": device_os, "type": type, "id": device_id, "app_id": app_id, "app_name": app_name, } state = {"device": device} data = { "access": { "scope": ["read", "write", "control"] }, "device": device } if self.system: featuring = self.system.get("featuring", None) if featuring: data["access"]["featuring"] = featuring LOG.debug("pair/request request: %s", data) resp = await self.session.post(self._url("pair/request"), json=data, auth=None) if not resp.headers["content-type"].startswith("application/json"): raise NoneJsonData(resp.text) data_response = resp.json() LOG.debug("pair/request response: %s", data_response) if data_response.get("error_id") != "SUCCESS": raise PairingFailure(data_response) state["timestamp"] = data_response["timestamp"] state["auth_key"] = data_response["auth_key"] return state async def pairGrant(self, state: Dict[str, Any], pin: str): """Finish a pairing sequence""" auth_handler = httpx.DigestAuth(state["device"]["id"], state["auth_key"]) signature = hmac_signature(self.auth_shared_key, str(state["timestamp"]), pin) auth = { "auth_appId": "1", "auth_timestamp": state["timestamp"], "auth_signature": signature, "pin": pin, } data = {"auth": auth, "device": state["device"]} LOG.debug("pair/grant request: %s", data) resp = await self.session.post( self._url("pair/grant"), json=data, auth=auth_handler ) if not resp.headers["content-type"].startswith("application/json"): raise NoneJsonData(resp.text) data_response = resp.json() LOG.debug("pair/grant response: %s", data_response) if data_response.get("error_id") != "SUCCESS": raise PairingFailure(data_response) self.session.auth = auth_handler return state["device"]["id"], state["auth_key"] async def setTransport( self, secured_transport: Optional[bool] = None, api_version: Optional[int] = None, ): if secured_transport == True and self.protocol != "https": LOG.info("Switching to secured transport") self.protocol = "https" elif secured_transport == False and self.protocol != "http": LOG.info("Switching to unsecured transport") self.protocol = "http" if api_version and api_version != self.api_version: LOG.info("Switching to api_version %d", api_version) self.api_version = api_version async def update(self): try: if not self.on: await self.getSystem() await self.setTransport(self.secured_transport) await self.getSources() await self.getChannelLists() await self.getChannels() await self.getApplications() await self.getAmbilightSupportedStyles() await self.getAmbilightCached() await self.getMenuItemsSettingsStructure() await self.getPowerState() await self.getAudiodata() await self.getSourceId() await self.getChannelId() await self.getApplication() await self.getContext() await self.getScreenState() await self.getAmbilightMode() await self.getAmbilightPower() await self.getAmbilightCurrentConfiguration() await self.getHueLampPower() await self.getRecordings() self.on = True return True except ConnectionFailure as err: LOG.debug("TV not available: %s", repr(err)) self.on = False return False def _decodeSystem(self, system) -> SystemType: result = {} for key, value in system.items(): if key.endswith("_encrypted"): result[key[:-10]] = cbc_decode(self.auth_shared_key, value) else: result[key] = value return cast(SystemType, result) async def getSystem(self): # Newest TV software requires https for system-info. Therefore we will try both protocols. protocols = ["http", "https"] if protocols[0] != self.protocol: protocols = reversed(protocols) for prot in protocols: r = cast(Optional[SystemType], await self.getReq("system", protocol=prot)) if r: self.system = self._decodeSystem(r) self.name = r.get("name") return r else: self.system = {} self.name = None return r async def getAudiodata(self): r = await self.getReq("audio/volume") if r: self.audio_volume = r else: self.audio_volume = r return r async def getChannels(self): if self.api_version >= 5: self.channels = {} for channel_list in self.channel_lists.values(): for channel in channel_list.get("Channel", []): self.channels[str(channel.get("ccid"))] = channel return self.channels else: r = cast(Optional[ChannelsType], await self.getReq("channels")) if r: self.channels = r else: self.channels = {} return r async def getChannelId(self): if not self.channels: self.channel = None return None if self.api_version >= 5: r = cast(Optional[ActivitesTVType], await self.getReq("activities/tv")) else: r = cast( Optional[ChannelsCurrentType], await self.getReq("channels/current") ) self.channel = r return r async def getChannelName(self, ccid) -> Optional[str]: if not self.channels: return None return self.channels.get(ccid, dict()).get("name", None) async def getChannelLogo( self, ccid, channel_list="all" ) -> Tuple[Optional[bytes], Optional[str]]: if self.api_version >= 5: data, content_type = await self._getBinary( f"channeldb/tv/channelLists/{channel_list}/{ccid}/logo" ) else: data, content_type = await self._getBinary(f"channels/{ccid}/logo.png") return data, content_type async def getContext(self) -> Optional[ContextType]: r = cast(Optional[ContextType], await self.getReq(f"context")) self.context = r return r async def setChannel(self, ccid: Union[str, int], list_id: Optional[str] = None): channel: Union[ActivitesTVType, ChannelsCurrentType] if self.api_version >= 5: channel = {"channelList": {"id": list_id or "all"}, "channel": {"ccid": int(ccid)}} if await self.postReq("activities/tv", cast(Dict, channel)) is not None: self.channel = channel return True else: channel = {"id": str(ccid)} if await self.postReq("channels/current", cast(Dict, channel)) is not None: self.channel = channel return True return False async def getChannelLists(self): if self.api_version >= 5: r = cast(ChannelDbTv, await self.getReq("channeldb/tv")) if r: channel_lists = {} favorite_lists = {} for data in r.get("channelLists", []): list_id = data["id"] channel_list = cast( Optional[ChannelListType], await self.getReq(f"channeldb/tv/channelLists/{list_id}"), ) if channel_list: channel_lists[list_id] = channel_list for data in r.get("favoriteLists", []): list_id = data["id"] favorite_list = cast( Optional[FavoriteListType], await self.getReq(f"channeldb/tv/favoriteLists/{list_id}"), ) if favorite_list: favorite_lists[list_id] = favorite_list self.channel_lists = channel_lists self.favorite_lists = favorite_lists else: self.channel_lists = {} self.favorite_lists = {} return r async def getFavoriteList(self, list_id: str): if self.api_version >= 5: r = cast( Optional[FavoriteListType], await self.getReq(f"channeldb/tv/favoriteLists/{list_id}"), ) if r and "channels" in r: return r["channels"] else: return None async def getChannelList(self, list_id: str): if self.api_version >= 5: r = cast( Optional[ChannelListType], await self.getReq(f"channeldb/tv/channelLists/{list_id}"), ) if r and "Channel" in r: return r["Channel"] else: return None async def getSources(self): if self.json_feature_supported("activities", "intent"): self.sources = { channel_uri(None): {"name": "Watch TV"}, passthrough_uri("com.mediatek.tvinput/.hdmi.HDMIInputService/HW5"): { "name": "HDMI 1" }, passthrough_uri("com.mediatek.tvinput/.hdmi.HDMIInputService/HW6"): { "name": "HDMI 2" }, passthrough_uri("com.mediatek.tvinput/.hdmi.HDMIInputService/HW7"): { "name": "HDMI 3" }, passthrough_uri("com.mediatek.tvinput/.hdmi.HDMIInputService/HW8"): { "name": "HDMI 4" }, } return self.sources if self.api_version == 1: r = await self.getReq("sources") if r: self.sources = r else: self.sources = {} return r async def getSourceId(self): if self.api_version < 5: r = await self.getReq("sources/current") if r: self.source_id = r["id"] else: self.source_id = None return r async def getSourceName(self, srcid) -> Optional[str]: if not self.sources: return None return self.sources.get(srcid, dict()).get("name", None) async def setSource(self, source_id): if self.api_version == 1: r = await self.postReq("sources/current", {"id": source_id}) if r is not None: return True return False if self.json_feature_supported("activities", "intent"): if source_id == CHANNEL_URI and self.channel_id: source_id = channel_uri(self.channel_id) intent: ApplicationIntentType = { "extras": {"uri": source_id}, "action": "org.droidtv.playtv.SELECTURI", "component": { "packageName": "org.droidtv.playtv", "className": "org.droidtv.playtv.PlayTvActivity", }, } return await self.setApplication(intent) return False async def getApplications(self): if self.json_feature_supported("applications"): r = cast(ApplicationsType, await self.getReq("applications")) if r: self.applications = {app["id"]: app for app in r["applications"] if "id" in app} else: self.applications = {} return r async def getApplication(self): if self.json_feature_supported("applications"): r = cast(ApplicationIntentType, await self.getReq("activities/current")) if r: self.application = r else: self.application = None return r async def getApplicationIcon(self, id) -> Tuple[Optional[bytes], Optional[str]]: if self.json_feature_supported("applications"): data, content_type = await self._getBinary(f"applications/{id}/icon") return data, content_type else: return None, None async def getPowerState(self): r = await self.getReq("powerstate") if r: self.powerstate = cast(str, r["powerstate"]) else: self.powerstate = None return r async def setPowerState(self, state): data = {"powerstate": state} if await self.postReq("powerstate", data) is not None: self.powerstate = state return True return False async def getScreenState(self): if self.api_version >= 5: r = await self.getReq("screenstate") if r: self.screenstate = cast(str, r["screenstate"]) else: self.screenstate = None return r else: self.screenstate = None async def setScreenState(self, state): if self.api_version >= 5: data = {"screenstate": state} if await self.postReq("screenstate", data) is not None: self.screenstate = state return True return False async def getHueLampPower(self): if self.json_feature_supported("ambilight", "Hue"): r = await self.getReq("HueLamp/power") if r and "power" in r: self.huelamp_power = cast(str, r["power"]) else: self.huelamp_power = None return r return None async def setHueLampPower(self, state): if self.json_feature_supported("ambilight", "Hue"): data = {"power": state} if await self.postReq("HueLamp/power", data) is not None: self.huelamp_power = state return True return False async def setApplication(self, intent: ApplicationIntentType): if self.json_feature_supported("activities", "intent"): data = {"intent": intent} if await self.postReq("activities/launch", data) is not None: self.application = intent return True return False async def setVolume(self, level, muted=False): data = {} if self.audio_volume is None or self.min_volume is None or self.max_volume is None: await self.getAudiodata() assert self.audio_volume is not None assert self.max_volume is not None assert self.min_volume is not None if level is not None: try: targetlevel = int(level * self.max_volume) except ValueError: LOG.warning("Invalid audio level %s" % str(level)) return False if targetlevel < self.min_volume or targetlevel > self.max_volume: LOG.warning( "Level not in range (%i - %i)" % (self.min_volume, self.max_volume) ) return False data["current"] = targetlevel data["muted"] = muted if await self.postReq("audio/volume", data) is None: return False self.audio_volume.update(data) async def sendKey(self, key): res = await self.postReq("input/key", {"key": key}) is not None if res: if key == "Mute": if self.audio_volume: self.audio_volume["muted"] = not self.audio_volume["muted"] elif key == "VolumeUp": if ( self.audio_volume and self.audio_volume["current"] < self.audio_volume["max"] ): self.audio_volume["current"] += 1 elif key == "VolumeDown": if ( self.audio_volume and self.audio_volume["current"] > self.audio_volume["min"] ): self.audio_volume["current"] -= 1 async def sendUnicode(self, key: str): return await self.postReq("input/key", {"unicode": key}) is not None async def getAmbilightMode(self): data = await self.getReq("ambilight/mode") if data: self.ambilight_mode_raw = data["current"] if self.quirk_ambilight_mode_ignored: # we could probably disable quirk here if data["current"] != "internal" and self.ambilight_mode_set: LOG.warning( "TV properly report ambilight mode, quirk should be disabled" ) self.ambilight_mode_set = None return data["current"] else: self.ambilight_mode_raw = None async def setAmbilightMode(self, mode): data = {"current": mode} if await self.postReq("ambilight/mode", data) is None: return False if self.quirk_ambilight_mode_ignored: if mode == "internal": self.ambilight_mode_set = None if ( await self.postReq( "ambilight/mode", {"current": "internal_invalid"} ) is None ): LOG.info("Ignoring error for workaround for ambilight mode") else: self.ambilight_mode_set = mode self.ambilight_mode_raw = mode return True async def getAmbilightTopology(self): r = await self.getReq("ambilight/topology") if r: self.ambilight_topology = r else: self.ambilight_topology = None return r async def getAmbilightMeasured(self): r = await self.getReq("ambilight/measured") if r: self.ambilight_measured = r else: self.ambilight_measured = None return r async def getAmbilightProcessed(self): r = await self.getReq("ambilight/processed") if r: self.ambilight_processed = r else: self.ambilight_processed = None return r async def getAmbilightCached(self): """Grab cached ambilight data from device. There are some unconfirmed reports where it seems the server crashes while grabbing this data from time to time. So use sparingly. """ r = await self.getReq("ambilight/cached") if r: self.ambilight_cached = r else: self.ambilight_cached = None return r async def getAmbilightPower(self): if self.api_version >= 5: r = await self.getReq("ambilight/power") if r: self.ambilight_power_raw = r else: self.ambilight_power_raw = None return r async def setAmbilightPower(self, power: str): if self.api_version >= 5: data = {"power": power} if await self.postReq("ambilight/power", data) is None: return False self.ambilight_power_raw = data if self.quirk_ambilight_mode_ignored: if power == "On" and "lounge" in self.ambilight_modes: self.ambilight_mode_raw = "lounge" self.ambilight_mode_set = "lounge" return True if self.api_version == 1: if power == "On": await self.setAmbilightMode("internal") else: await self.setAmbilightMode("manual") await self.setAmbilightCached({"r": 0, "g": 0, "b": 0}) return True async def setAmbilightCached(self, data): if await self.postReq("ambilight/cached", data) is None: return False # won't do optimistic behavior here since # the data format can differ a lot await self.getAmbilightCached() if self.quirk_ambilight_mode_ignored: if self.ambilight_mode_set not in ("manual", "expert"): self.ambilight_mode_raw = "manual" self.ambilight_mode_set = "manual" return True async def getAmbilightSupportedStyles(self): if self.json_feature_supported("ambilight", "Ambilight"): r = cast( Optional[AmbilightSupportedStylesType], await self.getReq("ambilight/supportedstyles"), ) if r: self.ambilight_styles = { style["styleName"]: style for style in r["supportedStyles"] if "styleName" in style } if self.quirk_ambilight_styles_menuitems: style = self.ambilight_styles.setdefault( "FOLLOW_VIDEO", {"styleName": "FOLLOW_VIDEO"} ) style["menuSettings"] = [ "STANDARD", "VIVID", "IMMERSIVE", "NATURAL", "GAME", ] style = self.ambilight_styles.setdefault( "FOLLOW_AUDIO", {"styleName": "FOLLOW_AUDIO"} ) style["menuSettings"] = [ "ENERGY_ADAPTIVE_BRIGHTNESS", "VU_METER", "RANDOM_PIXEL_FLASH", ] if "Lounge light" in self.ambilight_styles: style = self.ambilight_styles["Lounge light"] elif "FOLLOW_COLOR" in self.ambilight_styles: style = self.ambilight_styles["FOLLOW_COLOR"] else: style = self.ambilight_styles.setdefault( "Lounge light", {"styleName": "Lounge light"} ) style["menuSettings"] = [ "HOT_LAVA", "DEEP_WATER", "FRESH_NATURE", "ISF", "CUSTOM_COLOR", ] else: self.ambilight_styles = {} return r async def getAmbilightCurrentConfiguration(self): if self.json_feature_supported("ambilight", "Ambilight"): r = cast( Optional[AmbilightCurrentConfiguration], await self.getReq("ambilight/currentconfiguration"), ) if r: self.ambilight_current_configuration = r else: self.ambilight_current_configuration = None return r async def setAmbilightCurrentConfiguration( self, config: AmbilightCurrentConfiguration ): if self.json_feature_supported("ambilight", "Ambilight"): r = await self.postReq("ambilight/currentconfiguration", cast(Dict, config)) if r is None: self.ambilight_current_configuration = None return False self.ambilight_current_configuration = config if self.quirk_ambilight_mode_ignored: self.ambilight_mode_set = None return True async def getRecordings(self): #Just known working with API level 6 currently if self.api_version == 6: if self.json_feature_supported("recordings", "List"): r = cast( Optional[RecordingsListed], await self.getReq("recordings/list"), ) if r: self.recordings_list = r else: self.recordings_list = None return r async def openURL(self, url: str): if self.json_feature_supported("activities", "browser"): r = await self.postReq("activities/browser", {"url": url}) return r is not None async def getStringsCached(self, string_ids: Iterable[str]) -> Union[Dict[str, str], None]: """Fetch translations for strings, making sure all items exist.""" string_ids_wanted = set(string_ids) string_ids_missing = string_ids_wanted - self.strings.keys() # fetch translations from TV if string_ids_missing: if (data := await self.getStrings(sorted(string_ids_missing))) is None: return None self.strings.update(data) # make sure untranslated strings is set 1 to 1 string_ids_missing = string_ids_wanted - set(self.strings.keys()) for string_id in string_ids_missing: self.strings[string_id] = string_id return self.strings async def getStrings( self, strings: Iterable[str], language: Optional[str] = None, country: Optional[str] = None, variant: Optional[str] = None, ) -> Dict[str, str]: res: Dict[str, str] = {} for group in chunked_iterator(MAXIMUM_ITEMS_IN_REQUEST, strings): data = await self._getStrings(group, language=language, country=country, variant=variant) res.update(data) return res async def _getStrings( self, strings: Iterable[str], language: Optional[str] = None, country: Optional[str] = None, variant: Optional[str] = None, ) -> Dict[str, str]: data: StringsRequest = { "strings": [{"string_id": string} for string in strings], "locale": { "language": language or "", "country": country or "", "variant": variant or "", }, } res = cast(Optional[Strings], await self.postReq("strings", data)) if res: return { translation["string_id"]: translation["string_translation"] for translation in res["translations"] if translation["string_translation"] } return {} async def getMenuItemsSettingsStructure(self, force=False) -> Optional[MenuItemsSettingsStructure]: if self.json_feature_supported("menuitems", "Setup_Menu") or force: r = cast(Optional[MenuItemsSettingsStructure], await self.getReq("menuitems/settings/structure") ) self.settings = r return r async def getMenuItemsSettingsCurrent(self, node_ids: Iterable[int], force=False) -> Optional[MenuItemsSettingsCurrent]: current: MenuItemsSettingsCurrent = {"values": [], "version": 0} for group in chunked_iterator(MAXIMUM_ITEMS_IN_REQUEST, node_ids): if (data := await self._getMenuItemsSettingsCurrent(group)) is None: return None current["values"].extend(data["values"]) if current["version"] == 0: current["version"] = data["version"] return current async def _getMenuItemsSettingsCurrent(self, node_ids: Iterable[int], force=False) -> Optional[MenuItemsSettingsCurrent]: if not node_ids: return None if self.json_feature_supported("menuitems", "Setup_Menu") or force: post: MenuItemsSettingsCurrentPost = { "nodes": [ { "nodeid": node_id } for node_id in node_ids ] } r = cast(Optional[MenuItemsSettingsCurrent], await self.postReq("menuitems/settings/current", cast(dict, post)) ) return r async def getMenuItemsSettingsCurrentValue(self, node_ids: List[int], force=False) -> Dict[int, Optional[MenuItemsSettingsCurrentValueValue]]: data = await self.getMenuItemsSettingsCurrent(node_ids, force=force) res: Dict[int, Optional[MenuItemsSettingsCurrentValueValue]] = {} if data: for value in data.get("values", {}): value_value = value["value"] # Sometimes the API places the data field outside of the value field. # This seem faulty, so let's move it in. if data := value.get("data"): value_value["data"] = data res[value["value"].get("Nodeid")] = value_value for node_id in node_ids: if node_id not in res: res[node_id] = None return res async def postMenuItemsSettingsUpdate(self, post: MenuItemsSettingsUpdate, force=False): if self.json_feature_supported("menuitems", "Setup_Menu") or force: return await self.postReq("menuitems/settings/update", cast(dict, post)) async def postMenuItemsSettingsUpdateData(self, value: Dict[int, MenuItemsSettingsValueData], force=False): post = { "values": [ { "value": { "Nodeid": node_id, "data": data }, } for node_id, data in value.items() ] } return await self.postMenuItemsSettingsUpdate(cast(MenuItemsSettingsUpdate, post), force=force) async def notifyChange(self, timeout=TIMEOUT_NOTIFYREAD): """Start a http connection waiting for changes.""" if not self.notify_change_supported: return None data = { "notification": { "activities/tv": self.channel, "activities/current": self.application, "powerstate": {"powerstate": self.powerstate}, "audio/volume": self.audio_volume, "context": self.context, "screenstate": {"screenstate": self.screenstate}, "menuitems/settings/version": {"version": self.settings_version} } } timeout_ctx = httpx.Timeout(timeout=TIMEOUT, connect=TIMEOUT_CONNECT, read=timeout) try: result = await self.postReq("notifychange", data=data, timeout=timeout_ctx) except ProtocolFailure as err: # not uncommon for tv to close connection on the lingering connection LOG.debug("Protocol failure from device: %s", repr(err)) result = False except ConnectionFailure as err: LOG.debug("Connection failure to device: %s", repr(err)) self.on = False result = None if result: if "activities/tv" in result: self.channel = result["activities/tv"] if "activities/current" in result: self.application = result["activities/current"] if "powerstate" in result: self.powerstate = result["powerstate"]["powerstate"] if "audio/volume" in result: self.audio_volume = result["audio/volume"] if "context" in result: self.context = result["context"] if "screenstate" in result: self.screenstate = result["screenstate"]["screenstate"] if "menuitems/settings/version" in result: self.settings_version = result["menuitems/settings/version"]["version"] return True if result is None: return None return False ha-philipsjs-3.3.3/haphilipsjs/__main__.py000066400000000000000000000276111471516442100205370ustar00rootroot00000000000000import curses import platform import json import sys import pprint from . import PhilipsTV from haphilipsjs.typing import AmbilightCurrentConfiguration import asyncio from ast import literal_eval pp = pprint.PrettyPrinter(indent=2) async def monitor_run(stdscr: curses.window, tv: PhilipsTV): stdscr.clear() stdscr.timeout(1000) await tv.update() def get_application_name(): if tv.applications: for app in tv.applications.values(): if app.get("intent") == tv.application: return app.get("label") if tv.application: return tv.application.get("component", {}).get("className") while True: stdscr.clear() stdscr.addstr(0, 0, "Source") if tv.source_id: stdscr.addstr(1, 0, await tv.getSourceName(tv.source_id) or "") stdscr.addstr(3, 15, "Menu Version") if tv.channel_id: stdscr.addstr(4, 15, str(tv.settings_version)) stdscr.addstr(0, 15, "Channel") if tv.channel_id: stdscr.addstr(1, 15, await tv.getChannelName(tv.channel_id) or "") stdscr.addstr(0, 30, "Application") if tv.application: stdscr.addstr(1, 30, get_application_name() or "") stdscr.addstr(0, 45, "Context") if tv.context: stdscr.addstr(1, 45, tv.context.get("level1", "")) stdscr.addstr(2, 45, tv.context.get("level2", "")) stdscr.addstr(3, 45, tv.context.get("level3", "")) stdscr.addstr(4, 45, tv.context.get("data", "")) stdscr.addstr(0, 70, "Channels") for idx, channel in enumerate(tv.channels_current): stdscr.addstr(1+idx, 70, str(channel.get("name", channel.get("ccid", "")))) def print_pixels(side, offset_y, offset_x): stdscr.addstr(offset_y, offset_x, "{}".format(side)) stdscr.addstr(offset_y + 1, offset_x, "-----------") if side not in layer: return for pixel, data in layer[side].items(): stdscr.addstr( offset_y + 2 + int(pixel), offset_x, "{:>3} {:>3} {:>3}".format(data["r"], data["g"], data["b"]), ) ambilight = await tv.getAmbilightProcessed() if ambilight: for idx, layer in enumerate(ambilight.values()): offset_y = 6 + 6 * idx print_pixels("left", offset_y, 0) print_pixels("top", offset_y, 15) print_pixels("right", offset_y, 30) print_pixels("bottom", offset_y, 45) stdscr.refresh() if not await tv.notifyChange(): await tv.update() key = stdscr.getch() if key == ord("q"): break async def monitor(tv): try: stdscr = curses.initscr() curses.noecho() curses.cbreak() stdscr.keypad(True) try: curses.start_color() except: pass return await monitor_run(stdscr, tv) finally: # Set everything back to normal if "stdscr" in locals(): stdscr.keypad(False) # type: ignore curses.echo() curses.nocbreak() curses.endwin() curses.wrapper(monitor_run, tv) async def main(): import argparse import logging import haphilipsjs import ast logger = logging.getLogger(__name__) parser = argparse.ArgumentParser(prog="python -m haphilipsjs") parser.add_argument( "-d", "--debug", help="Debug output", dest="debug", action="store_true", default=False, ) parser.add_argument("-i", "--host", dest="host", required=True) parser.add_argument("-a", "--api", dest="api", required=True) parser.add_argument( "-s", "--secured_transport", dest="secured_transport", action="store_true", default=False, ) parser.add_argument( "-u", "--username", dest="username", help="Username", required=False ) parser.add_argument( "-p", "--password", dest="password", help="Password", required=False ) subparsers = parser.add_subparsers(help="commands", dest="command") status = subparsers.add_parser("status", help="Show current tv status") mon = subparsers.add_parser("monitor", help="Monitor current tv status") ambilight = subparsers.add_parser("ambilight", help="Control ambilight") ambilight.add_argument( "--ambilight_mode", dest="ambilight_mode", type=str, required=False ) ambilight.add_argument( "--ambilight_cached", dest="ambilight_cached", type=ast.literal_eval, required=False, ) ambilight.add_argument( "--style", dest="ambilight_style", type=str, required=False ) ambilight.add_argument( "--setting", dest="ambilight_setting", type=str, required=False ) pair = subparsers.add_parser("pair", help="Pair with tv") get = subparsers.add_parser("get", help="Get data from endpoint") get.add_argument("path", help="Sub path to grab from tv") post = subparsers.add_parser("post", help="Post data to endpoint") post.add_argument("path", help="Sub path to grab from tv") post.add_argument("data", help="Json data to post") strings = subparsers.add_parser("strings", help="Get translations") strings.add_argument("string_ids", help="List of string id to request", nargs="+") strings.add_argument("--language", help="Language code to request") strings.add_argument("--country", help="Country code to request") strings.add_argument("--variant", help="Variant") settings = subparsers.add_parser("settings", help="Get setting") settings_commands = settings.add_subparsers(help="settings commands", dest="settings_command") settings_get = settings_commands.add_parser("get", help="Get current setting") settings_get.add_argument("node_id", help="node id to request", type=int) settings_structure = settings_commands.add_parser("structure", help="Get settings structure") settings_set = settings_commands.add_parser("set", help="Post a settings value") settings_set.add_argument("node_id", help="node id to request", type=int) settings_set.add_argument("data", help="Json data to post", type=json.loads) markdown = subparsers.add_parser("markdown", help="Print markdown for commandline") notify = subparsers.add_parser("notify", help="Look at notify changes, use with debug") args = parser.parse_args() logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO) haphilipsjs.TIMEOUT = 60.0 tv = haphilipsjs.PhilipsTV( args.host, int(args.api), username=args.username, password=args.password, secured_transport=args.secured_transport, ) try: await run(args, parser, tv) finally: await tv.aclose() async def run(args, parser, tv: PhilipsTV): if args.command == "status": await tv.update() print("System: {}".format(tv.system)) # Simulating homeassistant/components/media_player/philips_js.py print("Source: {}".format(await tv.getSourceName(tv.source_id))) if tv.sources: print( "Sources: {}".format( ", ".join( [ await tv.getSourceName(srcid) or "None" for srcid in tv.sources ] ) ) ) print( "Channel: {}: {}".format( tv.channel_id, await tv.getChannelName(tv.channel_id) ) ) if tv.channels: print( "Channels: {}".format( ", ".join( [ await tv.getChannelName(ccid) or "None" for ccid in list(tv.channels.keys()) ] ) ) ) print("Context: {}".format(tv.context)) print("Application:") pp.pprint(tv.application) print("Applications:") pp.pprint(list(tv.applications.values())) print("Power State: {}".format(tv.powerstate)) print("Screen State: {}".format(tv.screenstate)) await tv.getAmbilightPower() print("Ambilight power: {}".format(tv.ambilight_power)) print("Ambilight mode: {}".format(tv.ambilight_mode)) print("Ambilight topology:") pp.pprint(await tv.getAmbilightTopology()) print("Ambilight processed:") pp.pprint(await tv.getAmbilightProcessed()) print("Ambilight measured:") pp.pprint(await tv.getAmbilightMeasured()) print("Ambilight styles:") pp.pprint(list(tv.ambilight_styles.values())) print("Ambilight currentconfiguration:") pp.pprint(tv.ambilight_current_configuration) print("Ambilight+Hue State: {}".format(tv.huelamp_power)) elif args.command == "ambilight": await tv.getSystem() await tv.setTransport(tv.secured_transport, tv.api_version_detected) if args.ambilight_mode: if not await tv.setAmbilightMode(args.ambilight_mode): print("Failed to set mode") if args.ambilight_cached: if not await tv.setAmbilightCached(args.ambilight_cached): print("Failed to set ambilight cached") current: AmbilightCurrentConfiguration = {} if args.ambilight_style: current["styleName"] = args.ambilight_style if args.ambilight_setting: current["menuSetting"] = args.ambilight_setting if current: current["isExpert"] = False if await tv.setAmbilightCurrentConfiguration(current) is False: print("Failed to set ambilight config") elif args.command == "monitor": await monitor(tv) elif args.command == "pair": await tv.getSystem() await tv.setTransport(tv.secured_transport, tv.api_version_detected) state = await tv.pairRequest( "ha-philipsjs", "ha-philipsjs", platform.node(), platform.system(), "native" ) pin = input("Please enter pin displayed on screen: ") res = await tv.pairGrant(state, pin) print(f"Username: {res[0]}") print(f"Password: {res[1]}") elif args.command == "get": res = await tv.getReq(args.path) json.dump(res, sys.stdout, indent=2, ensure_ascii=False) elif args.command == "post": res = await tv.postReq(args.path, literal_eval(args.data)) json.dump(res, sys.stdout, indent=2, ensure_ascii=False) elif args.command == "strings": res = await tv.getStrings(args.string_ids, args.language, args.country, args.variant) json.dump(res, sys.stdout, indent=2, ensure_ascii=False) elif args.command == "settings": if args.settings_command == "structure": res = await tv.getMenuItemsSettingsStructure(force=True) json.dump(res, sys.stdout, indent=2, ensure_ascii=False) elif args.settings_command == "get": res = await tv.getMenuItemsSettingsCurrentValue([args.node_id], force=True) json.dump(res[args.node_id], sys.stdout, indent=2, ensure_ascii=False) print() elif args.settings_command == "set": res = await tv.postMenuItemsSettingsUpdateData({args.node_id: args.data}, force=True) json.dump(res, sys.stdout, indent=2, ensure_ascii=False) print() elif args.command == "markdown": try: import argmark # type: ignore except ImportError: print("Unable to find argmmark") return argmark.md_help(parser) elif args.command == "notify": await tv.update() while await tv.notifyChange() is not None: pass if __name__ == "__main__": asyncio.run(main()) ha-philipsjs-3.3.3/haphilipsjs/auth.py000066400000000000000000000163231471516442100177560ustar00rootroot00000000000000"""This is a copy of the DigestAuth logic from httpx auth module with additions for cached auth. Base copied from: https://github.com/encode/httpx/blob/32d37cfdf1697f0dbacbc2233ee34606369be87b/httpx/_auth.py Issue tracking the need for the copy: https://github.com/encode/httpx/issues/1467 """ import hashlib import os import re import time import typing from urllib.request import parse_http_list from httpx import ProtocolError from httpx import Request, Response from httpx import Auth def to_bytes(value: typing.Union[str, bytes], encoding: str = "utf-8") -> bytes: return value.encode(encoding) if isinstance(value, str) else value def to_str(value: typing.Union[str, bytes], encoding: str = "utf-8") -> str: return value if isinstance(value, str) else value.decode(encoding) def unquote(value: str) -> str: return value[1:-1] if value[0] == value[-1] == '"' else value class CachedDigestAuth(Auth): """Digest authentication with a cached challange response. Known issues: * Only a single challenge is every cached, no checks is done for hostname being correct * Nonce counting is not implemented since philips TV's don't seem to require it. """ _ALGORITHM_TO_HASH_FUNCTION: typing.Dict[str, typing.Callable] = { "MD5": hashlib.md5, "MD5-SESS": hashlib.md5, "SHA": hashlib.sha1, "SHA-SESS": hashlib.sha1, "SHA-256": hashlib.sha256, "SHA-256-SESS": hashlib.sha256, "SHA-512": hashlib.sha512, "SHA-512-SESS": hashlib.sha512, } def __init__( self, username: typing.Union[str, bytes], password: typing.Union[str, bytes] ) -> None: self._challenge = None self._username = to_bytes(username) self._password = to_bytes(password) def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: if self._challenge: request.headers["Authorization"] = self._build_auth_header(request, self._challenge) response = yield request if response.status_code != 401 or "www-authenticate" not in response.headers: # If the response is not a 401 then we don't # need to build an authenticated request. return for auth_header in response.headers.get_list("www-authenticate"): if auth_header.lower().startswith("digest "): break else: # If the response does not include a 'WWW-Authenticate: Digest ...' # header, then we don't need to build an authenticated request. return self._challenge = self._parse_challenge(request, response, auth_header) request.headers["Authorization"] = self._build_auth_header(request, self._challenge) yield request def _parse_challenge( self, request: Request, response: Response, auth_header: str ) -> "_DigestAuthChallenge": """ Returns a challenge from a Digest WWW-Authenticate header. These take the form of: `Digest realm="realm@host.com",qop="auth,auth-int",nonce="abc",opaque="xyz"` """ scheme, _, fields = auth_header.partition(" ") # This method should only ever have been called with a Digest auth header. assert scheme.lower() == "digest" header_dict: typing.Dict[str, str] = {} for field in parse_http_list(fields): key, value = field.strip().split("=", 1) header_dict[key] = unquote(value) try: realm = header_dict["realm"].encode() nonce = header_dict["nonce"].encode() algorithm = header_dict.get("algorithm", "MD5") opaque = header_dict["opaque"].encode() if "opaque" in header_dict else None qop = header_dict["qop"].encode() if "qop" in header_dict else None return _DigestAuthChallenge( realm=realm, nonce=nonce, algorithm=algorithm, opaque=opaque, qop=qop ) except KeyError as exc: message = "Malformed Digest WWW-Authenticate header" raise ProtocolError(message, request=request) from exc def _build_auth_header( self, request: Request, challenge: "_DigestAuthChallenge" ) -> str: hash_func = self._ALGORITHM_TO_HASH_FUNCTION[challenge.algorithm] def digest(data: bytes) -> bytes: return hash_func(data).hexdigest().encode() A1 = b":".join((self._username, challenge.realm, self._password)) path = request.url.raw_path A2 = b":".join((request.method.encode(), path)) # TODO: implement auth-int HA2 = digest(A2) nonce_count = 1 # TODO: implement nonce counting nc_value = b"%08x" % nonce_count cnonce = self._get_client_nonce(nonce_count, challenge.nonce) HA1 = digest(A1) if challenge.algorithm.lower().endswith("-sess"): HA1 = digest(b":".join((HA1, challenge.nonce, cnonce))) qop = self._resolve_qop(challenge.qop, request=request) if qop is None: digest_data = [HA1, challenge.nonce, HA2] else: digest_data = [challenge.nonce, nc_value, cnonce, qop, HA2] key_digest = b":".join(digest_data) format_args = { "username": self._username, "realm": challenge.realm, "nonce": challenge.nonce, "uri": path, "response": digest(b":".join((HA1, key_digest))), "algorithm": challenge.algorithm.encode(), } if challenge.opaque: format_args["opaque"] = challenge.opaque if qop: format_args["qop"] = b"auth" format_args["nc"] = nc_value format_args["cnonce"] = cnonce return "Digest " + self._get_header_value(format_args) def _get_client_nonce(self, nonce_count: int, nonce: bytes) -> bytes: s = str(nonce_count).encode() s += nonce s += time.ctime().encode() s += os.urandom(8) return hashlib.sha1(s).hexdigest()[:16].encode() def _get_header_value(self, header_fields: typing.Dict[str, bytes]) -> str: NON_QUOTED_FIELDS = ("algorithm", "qop", "nc") QUOTED_TEMPLATE = '{}="{}"' NON_QUOTED_TEMPLATE = "{}={}" header_value = "" for i, (field, value) in enumerate(header_fields.items()): if i > 0: header_value += ", " template = ( QUOTED_TEMPLATE if field not in NON_QUOTED_FIELDS else NON_QUOTED_TEMPLATE ) header_value += template.format(field, to_str(value)) return header_value def _resolve_qop( self, qop: typing.Optional[bytes], request: Request ) -> typing.Optional[bytes]: if qop is None: return None qops = re.split(b", ?", qop) if b"auth" in qops: return b"auth" if qops == [b"auth-int"]: raise NotImplementedError("Digest auth-int support is not yet implemented") message = f'Unexpected qop value "{qop!r}" in digest auth' raise ProtocolError(message, request=request) class _DigestAuthChallenge(typing.NamedTuple): realm: bytes nonce: bytes algorithm: str opaque: typing.Optional[bytes] qop: typing.Optional[bytes]ha-philipsjs-3.3.3/haphilipsjs/data/000077500000000000000000000000001471516442100173475ustar00rootroot00000000000000ha-philipsjs-3.3.3/haphilipsjs/data/__init__.py000066400000000000000000000000241471516442100214540ustar00rootroot00000000000000"""Data modules.""" ha-philipsjs-3.3.3/haphilipsjs/data/v1.py000066400000000000000000000147061471516442100202570ustar00rootroot00000000000000from typing import cast from haphilipsjs.typing import ( ChannelsCurrentType, SourcesType, SystemType, SourceCurrentType, ChannelsType, ) SYSTEM_55PFL6007T: SystemType = { "menulanguage": "English", "name": "Philips TV", "country": "Sweden", "serialnumber": "1234567890", "softwareversion": "abcd", "model": "55PFL6007T/12", } SYSTEM_47PFH6309: SystemType = { "menulanguage": "Spanish", "name": "PHILIPS TV", "country": "Spain", "nettvversion": "", "epgsource": "broadcast", } SYSTEM_47PFH6309_ENCRYPTED = cast( SystemType, { **SYSTEM_47PFH6309, "serialnumber_encrypted": "bf1BcncGiQyBVS47ZXFWjNXoynlKUNlqDhxQz5ikPEU=", "softwareversion_encrypted": "K2kseVsmQFgkd15gKkJ+ao+IN30u9WA8apvZ8LuQYkrUGEcxIhN1tkDM7wnn3V+5", "model_encrypted": "K2kseVsmQFgkd15gKkJ+akRXrm2rweXcLkCrwpCecFY=", "deviceid_encrypted": "", }, ) SYSTEM_47PFH6309_DECRYPTED = cast( SystemType, { **SYSTEM_47PFH6309, "serialnumber": "ABCDEFGHIJKLF", "softwareversion": "mt5880:TPN141E_010.003.086.128", "model": "1_47PFH6309/88", "deviceid": "", }, ) SYSTEM_65PUS6121: SystemType = { "menulanguage": "Dutch", "name": "65PUS6121", "country": "Netherlands", "nettvversion": "4.5.0", "epgsource": "broadcast", "featuring": { "jsonfeatures": { "recordings": ["List", "Schedule", "Manage"], "ambilight": ["LoungeLight"], "textentry": ["context_based", "initial_string_available"], "inputkey": ["key", "unicode"], "pointer": ["context_based"], "activities": ["browser"], }, "systemfeatures": {"tvtype": "consumer", "content": ["dmr", "dms_tad"]}, }, } SYSTEM_65PUS6121_ENCRYPTED = cast( SystemType, { **SYSTEM_65PUS6121, "serialnumber_encrypted": "bf1BcncGiQyBVS47ZXFWjNXoynlKUNlqDhxQz5ikPEU=", "softwareversion_encrypted": "K2kseVsmQFgkd15gKkJ+avGAg/2Fns000yNVcCibCqJc8lV5YE2qHdE/pFg2ib2M", "model_encrypted": "K2kseVsmQFgkd15gKkJ+ai592Vkjcax6Xprb+fzv08s=", "deviceid_encrypted": "K2kseVsmQFgkd15gKkJ+arY7DmesJhIol3fe863YYY0=", }, ) SYSTEM_65PUS6121_DECRYPTED = cast( SystemType, { **SYSTEM_65PUS6121, "serialnumber": "ABCDEFGHIJKLF", "softwareversion": "mt5891:TPL161E_012.003.039.001", "model": "1_65PUS6121/12", "deviceid": "54885869", }, ) SYSTEM_ENCRYPTED = { "55PFL6007T": SYSTEM_55PFL6007T, "47PFH6309": SYSTEM_47PFH6309_ENCRYPTED, "65PUS6121": SYSTEM_65PUS6121_ENCRYPTED, } SYSTEM_DECRYPTED = { "55PFL6007T": SYSTEM_55PFL6007T, "47PFH6309": SYSTEM_47PFH6309_DECRYPTED, "65PUS6121": SYSTEM_65PUS6121_DECRYPTED, } SOURCES: SourcesType = { "tv": {"name": "Watch TV"}, "satellite": {"name": "Watch satellite"}, "hdmi1": {"name": "HDMI 1"}, "hdmi2": {"name": "HDMI 2"}, "hdmi3": {"name": "HDMI 3"}, "hdmiside": {"name": "HDMI side"}, "ext1": {"name": "EXT 1"}, "ext2": {"name": "EXT 2"}, "ypbpr": {"name": "Y Pb Pr"}, "vga": {"name": "VGA"}, } SOURCES_CURRENT: SourceCurrentType = {"id": "hdmi1"} CHANNELS: ChannelsType = { "fingerprint-1": { "preset": "1", "name": "Flower", }, "fingerprint-2": {"preset": "2", "name": "Moving Colourbar"}, "fingerprint-3": {"preset": "12.3", "name": "Irdeto scrambled"}, "fingerprint-4": {"preset": "4", "name": "Codec 16:9 scrambled"}, } CHANNELS_CURRENT: ChannelsCurrentType = {"id": "fingerprint-1"} CHANNELLISTS = { "fingerprint-1": { "name": "All TV channels", "source": "tv", }, "fingerprint-2": { "name": "Favourite TV channels", "source": "tv", }, "fingerprint-3": { "name": "Radio TV channels", "source": "tv", }, "fingerprint-4": { "name": "Non-radio TV channels", "source": "tv", }, "fingerprint-5": { "name": "All satellite channels", "source": "satellite", }, "fingerprint-6": { "name": "Favourite satellite channels", "source": "satellite", }, "fingerprint-7": { "name": "Radio satellite channels", "source": "satellite", }, "fingerprint-8": { "name": "Non-radio satellite channels", "source": "satellite", }, } VOLUME = {"muted": False, "current": 18, "min": 0, "max": 60} AMBILIGHT = { "mode": {"current": "internal"}, "topology": {"layers": 1, "left": 4, "top": 0, "right": 4, "bottom": 0}, "measured": { "layer1": { "left": { "0": {"r": 56, "g": 43, "b": 40}, "1": {"r": 94, "g": 81, "b": 77}, "2": {"r": 76, "g": 70, "b": 60}, "3": {"r": 43, "g": 37, "b": 26}, }, "top": {}, "right": { "0": {"r": 69, "g": 70, "b": 58}, "1": {"r": 124, "g": 120, "b": 100}, "2": {"r": 83, "g": 87, "b": 90}, "3": {"r": 50, "g": 49, "b": 51}, }, "bottom": {}, } }, "processed": { "layer1": { "left": { "0": {"r": 37, "g": 77, "b": 182}, "1": {"r": 53, "g": 87, "b": 186}, "2": {"r": 64, "g": 96, "b": 188}, "3": {"r": 19, "g": 67, "b": 188}, }, "top": {}, "right": { "0": {"r": 32, "g": 79, "b": 188}, "1": {"r": 83, "g": 110, "b": 188}, "2": {"r": 113, "g": 110, "b": 112}, "3": {"r": 32, "g": 76, "b": 188}, }, "bottom": {}, } }, "cached": { "layer1": { "left": { "0": {"r": 0, "g": 0, "b": 0}, "1": {"r": 0, "g": 0, "b": 0}, "2": {"r": 0, "g": 0, "b": 0}, "3": {"r": 0, "g": 0, "b": 0}, }, "top": {}, "right": { "0": {"r": 0, "g": 0, "b": 0}, "1": {"r": 0, "g": 0, "b": 0}, "2": {"r": 0, "g": 0, "b": 0}, "3": {"r": 0, "g": 0, "b": 0}, }, "bottom": {}, } }, } POWERSTATE_65PUS6121 = {"powerstate": "On"} POWERSTATE = {"65PUS6121": POWERSTATE_65PUS6121} CONTEXT_65PUS6121 = { "activity": "WatchExtension", "menu": "Source Menu", "Recording": "Off", } CONTEXT = {"65PUS6121": CONTEXT_65PUS6121} ha-philipsjs-3.3.3/haphilipsjs/data/v6.py000066400000000000000000015053101471516442100202610ustar00rootroot00000000000000from typing import cast, Dict from haphilipsjs.typing import ( ActivitesTVType, AmbilightCurrentConfiguration, AmbilightSupportedStylesType, AmbilightSupportedStyleType, ApplicationIntentType, ApplicationsType, ChannelDbTv, ChannelListType, ComponentType, ContextType, FavoriteListType, MenuItemsSettingsCurrent, MenuItemsSettingsCurrentPost, MenuItemsSettingsNode, MenuItemsSettingsStructure, MenuItemsSettingsUpdate, SystemType, RecordingsListed, ) SYSTEM_ANDROID: SystemType = { "notifyChange": "http", "menulanguage": "Swedish", "name": "65OLED855/12", "country": "Sweden", "nettvversion": "9.0.0", "epgsource": "broadcast", "api_version": {"Major": 6, "Minor": 4, "Patch": 0}, "featuring": { "jsonfeatures": { "editfavorites": ["TVChannels", "SatChannels"], "recordings": ["List", "Schedule", "Manage"], "ambilight": ["LoungeLight", "Hue", "Ambilight", "HueStreaming"], "menuitems": ["Setup_Menu"], "textentry": ["not_available"], "applications": ["TV_Apps", "TV_Games", "TV_Settings"], "pointer": ["not_available"], "inputkey": ["key"], "activities": ["intent"], "channels": ["preset_string"], "mappings": ["server_mapping"], }, "systemfeatures": { "tvtype": "consumer", "content": ["dmr", "pvr"], "tvsearch": "intent", "pairing_type": "digest_auth_pairing", "secured_transport": "true", "companion_screen": "true", }, }, "os_type": "MSAF_2019_P", } SYSTEM_ANDROID_ENCRYPTED = cast( SystemType, { **SYSTEM_ANDROID, "serialnumber_encrypted": "bf1BcncGiQyBVS47ZXFWjNXoynlKUNlqDhxQz5ikPEU=\n", "softwareversion_encrypted": "o5VTq/nnyhUzdpj+ac65ItwU2KTv6j6bu8brxNxA+5J78u9D7fdZwqcAilvhFc9L\n", "model_encrypted": "3Cdh9HfKdQZb0UJPzeXau15tgTFcLdYcPGb0NqOreDg=\n", "deviceid_encrypted": "0dwhYxbc4pu9bo+yXXKsSaAI/GqIoxSMlQIs6osKlCI=\n", }, ) SYSTEM_ANDROID_DECRYPTED = cast( SystemType, { **SYSTEM_ANDROID, "serialnumber": "ABCDEFGHIJKLF", "softwareversion": "TPM191E_R.101.001.208.001", "model": "65OLED855/12", "deviceid": "1234567890", }, ) SYSTEM_SAPHI: SystemType = { "menulanguage": "Dutch", "name": "50PUS6804/12", "country": "Netherlands", "nettvversion": "4.6.0.1", "epgsource": "no_epg", "api_version": {"Major": 6, "Minor": 1, "Patch": 0}, "featuring": { "jsonfeatures": { "recordings": ["List", "Schedule", "Manage"], "ambilight": ["Hue", "HueStreaming", "Ambilight"], "textentry": ["context_based", "initial_string_available"], "inputkey": ["key", "unicode"], "pointer": ["context_based"], "activities": ["browser"], }, "systemfeatures": { "tvtype": "consumer", "content": ["dmr"], "pairing_type": "none", "companion_screen": True, "os_type": "Linux", }, }, } SYSTEM_SAPHI_ENCRYPTED = cast( SystemType, { **SYSTEM_SAPHI, "serialnumber_encrypted": "bf1BcncGiQyBVS47ZXFWjNXoynlKUNlqDhxQz5ikPEU=\n", "softwareversion_encrypted": "K2kseVsmQFgkd15gKkJ+amueMe3DHAmDQ8d/R/pbq75SQ3mIJ7KzpbV8Z0lz4DAg", "model_encrypted": "K2kseVsmQFgkd15gKkJ+ajj14v1k18xtrm2jX61cQ9Y=", "deviceid_encrypted": "0dwhYxbc4pu9bo+yXXKsSaAI/GqIoxSMlQIs6osKlCI=\n", }, ) SYSTEM_SAPHI_DECRYPTED = cast( SystemType, { **SYSTEM_SAPHI, "serialnumber": "ABCDEFGHIJKLF", "softwareversion": "mt5887:TPM196E_091.003.255.001", "model": "1_50PUS6804/12", "deviceid": "1234567890", }, ) VOLUME = {"muted": False, "current": 18, "min": 0, "max": 60} APPLICATIONS: ApplicationsType = { "version": 0, "applications": [ { "label": "Så här......", "intent": { "component": { "packageName": "org.droidtv.eum", "className": "org.droidtv.eum.onehelp.HowToTutorials.HowToVideosActivity", }, "action": "android.intent.action.MAIN", }, "order": 0, "id": "org.droidtv.eum.onehelp.HowToTutorials.HowToVideosActivity-org.droidtv.eum", "type": "app", }, { "label": "Play Butik", "intent": { "component": { "packageName": "com.android.vending", "className": "com.google.android.finsky.tvmainactivity.TvMainActivity", }, "action": "android.intent.action.MAIN", }, "order": 0, "id": "com.google.android.finsky.tvmainactivity.TvMainActivity-com.android.vending", "type": "app", }, { "label": "YouTube", "intent": { "component": { "packageName": "com.google.android.youtube.tv", "className": "com.google.android.apps.youtube.tv.activity.ShellActivity", }, "action": "android.intent.action.MAIN", }, "order": 0, "id": "com.google.android.apps.youtube.tv.activity.ShellActivity-com.google.android.youtube.tv", "type": "app", }, { "label": "TED", "intent": { "component": { "packageName": "com.ted.android.tv", "className": "com.ted.android.tv.view.MainActivity", }, "action": "android.intent.action.MAIN", }, "order": 0, "id": "com.ted.android.tv.view.MainActivity-com.ted.android.tv", "type": "app", }, { "label": "Play Spel", "intent": { "component": { "packageName": "com.google.android.play.games", "className": "com.google.android.apps.play.games.app.atv.features.home.HomeActivity", }, "action": "empty", }, "order": 0, "id": "com.google.android.apps.play.games.app.atv.features.home.HomeActivity-com.google.android.play.games", "type": "app", }, { "label": "Netflix", "intent": { "component": { "packageName": "com.netflix.ninja", "className": "com.netflix.ninja.MainActivity", }, "action": "android.intent.action.MAIN", }, "order": 0, "id": "com.netflix.ninja.MainActivity-com.netflix.ninja", "type": "app", }, { "label": "Amazon Alexa", "intent": { "component": { "packageName": "org.droidtv.amazonalexa", "className": "org.droidtv.amazonalexa.wizard.AlexaWizardActivity", }, "action": "android.intent.action.MAIN", }, "order": 0, "id": "org.droidtv.amazonalexa.wizard.AlexaWizardActivity-org.droidtv.amazonalexa", "type": "app", }, { "label": "TV-guide", "intent": { "component": { "packageName": "org.droidtv.channels", "className": "org.droidtv.channels.ChannelsActivity", }, "action": "android.intent.action.MAIN", }, "order": 0, "id": "org.droidtv.channels.ChannelsActivity-org.droidtv.channels", "type": "app", }, { "label": "Media", "intent": { "component": { "packageName": "org.droidtv.contentexplorer", "className": "org.droidtv.contentexplorer.MainActivity", }, "action": "android.intent.action.MAIN", }, "order": 0, "id": "org.droidtv.contentexplorer.MainActivity-org.droidtv.contentexplorer", "type": "app", }, { "label": "Demo Me", "intent": { "component": { "packageName": "org.droidtv.demome", "className": "org.droidtv.demome.DemoMeOptionsActivity", }, "action": "empty", }, "order": 0, "id": "org.droidtv.demome.DemoMeOptionsActivity-org.droidtv.demome", "type": "app", }, { "label": "Philips TV-samling", "intent": { "component": { "packageName": "org.droidtv.nettv.market", "className": "org.droidtv.nettv.market.MarketMainActivity", }, "action": "android.intent.action.MAIN", }, "order": 0, "id": "org.droidtv.nettv.market.MarketMainActivity-org.droidtv.nettv.market", "type": "app", }, { "label": "Toppval", "intent": { "component": { "packageName": "org.droidtv.nettvrecommender", "className": "org.droidtv.nettvrecommender.NetTvRecommenderMainActivity", }, "action": "android.intent.action.MAIN", }, "order": 0, "id": "org.droidtv.nettvrecommender.NetTvRecommenderMainActivity-org.droidtv.nettvrecommender", "type": "app", }, { "label": "TV", "intent": { "component": { "packageName": "org.droidtv.playtv", "className": "org.droidtv.playtv.PlayTvActivity", }, "action": "android.intent.action.MAIN", }, "order": 0, "id": "org.droidtv.playtv.PlayTvActivity-org.droidtv.playtv", "type": "app", }, { "label": "Viafree", "intent": { "component": { "packageName": "se.viafree.android", "className": "com.viafree.android.SplashActivity", }, "action": "android.intent.action.MAIN", }, "order": 0, "id": "com.viafree.android.SplashActivity-se.viafree.android", "type": "app", }, ], } ACTIVITIES_CURRENT: ApplicationIntentType = { "component": { "packageName": "org.droidtv.nettv.market", "className": "org.droidtv.nettv.market.MarketMainActivity", } } ACTIVITIES_TV_ANDROID: ActivitesTVType = { "channel": {"ccid": 1648}, "channelList": { "id": "1", "version": "10" } } ACTIVITIES_TV_SAPHI: ActivitesTVType = { "channel": {"ccid": 1648}, } ACTIVITIES_TV = { "android": ACTIVITIES_TV_ANDROID, "saphi": ACTIVITIES_TV_SAPHI } CHANNELDB_TV_CHANNELLISTS_ALL: ChannelListType = { "id": "all", "version": 10, "listType": "MixedSources", "medium": "mixed", "Channel": [ {"ccid": 1648, "preset": "1", "name": "Irdeto scrambled"}, {"ccid": 1649, "preset": "2"}, ], } CHANNELDB_TV_FAVORITELISTS_ALLTER: FavoriteListType = { "id": "allter", "version": 10, "listType": "MixedSources", "medium": "mixed", "channels": [ {"ccid": 1648, "preset": "1"}, {"ccid": 1649, "preset": "2"}, ], } CHANNELDB_TV_FAVORITELISTS_1: FavoriteListType = { "id": "1", "version": 10, "listType": "MixedSources", "medium": "mixed", "channels": [ {"ccid": 1649, "preset": "1"}, ], } CHANNELDB_TV_ANDROID: ChannelDbTv = { "channelLists": [ { "id": "all", "version": 10, "listType": "MixedSources", "medium": "mixed", "active": True, "virtual": True, "modifiable": False, } ], "favoriteLists": [ { "id": "allter", "version": 1545826184134, "parentId": "all", "listType": "MixedSources", "medium": "mixed", "virtual": False, "modifiable": False, "name": "All teresterial", }, { "id": "1", "version": "0", "listType": "MixedSources", "medium": "mixed", "name": "Favourites 1", "parentId": "all", "virtual": False, "modifiable": True, }, ], } CHANNELDB_TV_SAPHI: ChannelDbTv = { "channelLists": [{ "id": "all", "version": 10, "listType": "MixedSources", "medium": "mixed", "active": True, "virtual": True, "modifiable": False, }], } AMBILIGHT = { "mode": {"current": "internal"}, "currentconfiguration": { "styleName": "FOLLOW_VIDEO", "isExpert": False, "menuSetting": "STANDARD", }, "lounge": { "color": {"hue": 0, "saturation": 0, "brightness": 0}, "colordelta": {"hue": 0, "saturation": 0, "brightness": 0}, "speed": 0, "mode": "Default", }, "power": {"power": "On"}, "supportedstyles": { "supportedStyles": [ {"styleName": "OFF"}, {"styleName": "FOLLOW_VIDEO"}, { "styleName": "FOLLOW_AUDIO", "algorithms": [ "ENERGY_ADAPTIVE_BRIGHTNESS", "ENERGY_ADAPTIVE_COLORS", "VU_METER", "SPECTRUM_ANALYZER", "KNIGHT_RIDER_CLOCKWISE", "KNIGHT_RIDER_ALTERNATING", "RANDOM_PIXEL_FLASH", "STROBO", "PARTY", ], "maxTuning": 2, }, { "styleName": "FOLLOW_COLOR", "algorithms": ["MANUAL_HUE", "AUTOMATIC_HUE"], "maxSpeed": 255, }, {"styleName": "LOUNGE"}, {"styleName": "MANUAL"}, {"styleName": "EXPERT"}, {"styleName": "GRID"}, ] }, "topology": {"layers": 1, "left": 4, "top": 0, "right": 4, "bottom": 0}, "measured": { "layer1": { "left": { "0": {"r": 56, "g": 43, "b": 40}, "1": {"r": 94, "g": 81, "b": 77}, "2": {"r": 76, "g": 70, "b": 60}, "3": {"r": 43, "g": 37, "b": 26}, }, "top": {}, "right": { "0": {"r": 69, "g": 70, "b": 58}, "1": {"r": 124, "g": 120, "b": 100}, "2": {"r": 83, "g": 87, "b": 90}, "3": {"r": 50, "g": 49, "b": 51}, }, "bottom": {}, } }, "processed": { "layer1": { "left": { "0": {"r": 37, "g": 77, "b": 182}, "1": {"r": 53, "g": 87, "b": 186}, "2": {"r": 64, "g": 96, "b": 188}, "3": {"r": 19, "g": 67, "b": 188}, }, "top": {}, "right": { "0": {"r": 32, "g": 79, "b": 188}, "1": {"r": 83, "g": 110, "b": 188}, "2": {"r": 113, "g": 110, "b": 112}, "3": {"r": 32, "g": 76, "b": 188}, }, "bottom": {}, } }, "cached": { "layer1": { "left": { "0": {"r": 0, "g": 0, "b": 0}, "1": {"r": 0, "g": 0, "b": 0}, "2": {"r": 0, "g": 0, "b": 0}, "3": {"r": 0, "g": 0, "b": 0}, }, "top": {}, "right": { "0": {"r": 0, "g": 0, "b": 0}, "1": {"r": 0, "g": 0, "b": 0}, "2": {"r": 0, "g": 0, "b": 0}, "3": {"r": 0, "g": 0, "b": 0}, }, "bottom": {}, } }, "power": {"power": "On"}, } AMBILIGHT_SUPPORTED_STYLES: AmbilightSupportedStylesType = { "supportedStyles": [ {}, {}, { "styleName": "FOLLOW_AUDIO", "algorithms": [ "ENERGY_ADAPTIVE_BRIGHTNESS", "ENERGY_ADAPTIVE_COLORS", "VU_METER", "SPECTRUM_ANALYZER", "KNIGHT_RIDER_CLOCKWISE", "KNIGHT_RIDER_ALTERNATING", "RANDOM_PIXEL_FLASH", "STROBO", "PARTY", ], "maxTuning": 2, }, { "styleName": "Lounge light", "algorithms": ["MANUAL_HUE", "AUTOMATIC_HUE"], "maxSpeed": 255, }, {}, {}, {}, {}, {}, {}, ] } AMBILIGHT_SUPPORTED_STYLES_EXTENDED_ANDROID: Dict[str, AmbilightSupportedStyleType] = { "FOLLOW_VIDEO": { "styleName": "FOLLOW_VIDEO", "menuSettings": ["STANDARD", "VIVID", "IMMERSIVE", "NATURAL", "GAME"], }, "FOLLOW_AUDIO": { "styleName": "FOLLOW_AUDIO", "algorithms": [ "ENERGY_ADAPTIVE_BRIGHTNESS", "ENERGY_ADAPTIVE_COLORS", "VU_METER", "SPECTRUM_ANALYZER", "KNIGHT_RIDER_CLOCKWISE", "KNIGHT_RIDER_ALTERNATING", "RANDOM_PIXEL_FLASH", "STROBO", "PARTY", ], "menuSettings": ["ENERGY_ADAPTIVE_BRIGHTNESS", "VU_METER", "RANDOM_PIXEL_FLASH"], "maxTuning": 2, }, "Lounge light": { "styleName": "Lounge light", "algorithms": ["MANUAL_HUE", "AUTOMATIC_HUE"], "menuSettings": [ "HOT_LAVA", "DEEP_WATER", "FRESH_NATURE", "ISF", "CUSTOM_COLOR", ], "maxSpeed": 255, }, } AMBILIGHT_SUPPORTED_STYLES_EXTENDED_SAPHI: Dict[str, AmbilightSupportedStyleType] = { "FOLLOW_AUDIO": { "styleName": "FOLLOW_AUDIO", "algorithms": [ "ENERGY_ADAPTIVE_BRIGHTNESS", "ENERGY_ADAPTIVE_COLORS", "VU_METER", "SPECTRUM_ANALYZER", "KNIGHT_RIDER_CLOCKWISE", "KNIGHT_RIDER_ALTERNATING", "RANDOM_PIXEL_FLASH", "STROBO", "PARTY", ], "maxTuning": 2, }, "Lounge light": { "styleName": "Lounge light", "algorithms": ["MANUAL_HUE", "AUTOMATIC_HUE"], "maxSpeed": 255, }, } AMBILIGHT_SUPPORTED_STYLES_EXTENDED = { "android": AMBILIGHT_SUPPORTED_STYLES_EXTENDED_ANDROID, "saphi": AMBILIGHT_SUPPORTED_STYLES_EXTENDED_SAPHI } AMBILIGHT_CURRENT_CONFIGURATION: AmbilightCurrentConfiguration = { "styleName": "FOLLOW_VIDEO", "isExpert": False, "menuSetting": "STANDARD", "stringValue": "Standard", } POWERSTATE = {"powerstate": "On"} SCREENSTATE = {"screenstate": "On"} HUELAMPPOWER = {"power": "On"} CONTEXT: ContextType = { "data": "NA", "level1": "WatchTv", "level2": "Playstate", "level3": "NA", } PAIR_REQUEST_DEVICE = { "id": "aaabbbcccdddeee", "device_name": "Android device", "device_os": "Android 12", "app_id": "com.tpvision.philipstvapp2", "app_name": "Philips TV Remote", "type": "native" } PAIR_REQUEST_SAPHI = { "device": PAIR_REQUEST_DEVICE, "access": { "featuring": SYSTEM_SAPHI["featuring"], "scope": ["read", "write", "control"] } } PAIR_REQUEST_ANDROID = { "device": PAIR_REQUEST_DEVICE, "access": { "featuring": SYSTEM_ANDROID["featuring"], "scope": ["read", "write", "control"] } } PAIR_RESPONSE = { "error_id": "SUCCESS", "error_text": "Authorization required", "auth_key": "ccddeeffgghh", "timestamp": 1128, "timeout": 30 } MENUITEMS_SETTINGS_STRUCTURE: MenuItemsSettingsStructure = { "node": { "node_id": 2131230753, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_VB_SETUP", "context": "Setup_Menu", "data": { "nodes": [ { "node_id": 2131230857, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_PICTURE", "context": "picture", "data": { "nodes": [ { "node_id": 2131230858, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PICTURE_STYLE", "context": "picture_style", "data": { "enums": [ { "enum_id": 23, "string_id": "org.droidtv.ui.strings.R.string.MISC_AI" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PERSONAL" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_VIVID" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NATURAL" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_STANDARD", "icon": "org.droidtv.ui.tvwidget2k15.R.drawable.eco_16x12_146" }, { "enum_id": 5, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MOVIE" }, { "enum_id": 6, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GAME" }, { "enum_id": 14, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MONITOR" }, { "enum_id": 24, "string_id": "org.droidtv.ui.strings.R.string.MISC_HDR_AI" }, { "enum_id": 13, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HDR_PERSONAL" }, { "enum_id": 9, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HDR_VIVID" }, { "enum_id": 20, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HDR_DOLBY_VISION_BRIGHT" }, { "enum_id": 21, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HDR_DOLBY_VISION_DARK" }, { "enum_id": 10, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HDR_NATURAL" }, { "enum_id": 11, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HDR_MOVIE" }, { "enum_id": 12, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HDR_GAME" }, { "enum_id": 7, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ISF_DAY", "icon": "org.droidtv.ui.tvwidget2k15.R.drawable.isf_unlocked_d" }, { "enum_id": 8, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ISF_NIGHT", "icon": "org.droidtv.ui.tvwidget2k15.R.drawable.isf_unlocked_d" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230859, "string_id": "org.droidtv.ui.strings.R.string.MAIN_COLOUR", "context": "colour", "data": { "slider_data": { "min": 0, "max": 100, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230860, "context": "contrast", "data": { "slider_data": { "min": 0, "max": 100, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230861, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SHARPNESS", "context": "sharpness", "data": { "slider_data": { "min": 0, "max": 10, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230862, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BRIGHTNESS", "context": "brightness", "data": { "slider_data": { "min": 0, "max": 100, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230863, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_ADVANCED_PICTURE", "context": "advanced_picture", "data": { "nodes": [ { "node_id": 2131230864, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_COLOUR", "context": "colour_menu", "data": { "nodes": [ { "node_id": 2131230865, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TINT_HUE", "context": "tint", "data": { "slider_data": { "min": -50, "max": 50, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230866, "string_id": "org.droidtv.ui.strings.R.string.MAIN_COLOUR_ENHANCEMENT", "context": "colour_enhancement", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230867, "string_id": "org.droidtv.ui.strings.R.string.MAIN_COLOUR_GAMUT", "context": "colour_gamut", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NORMAL" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WIDE" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230868, "string_id": "org.droidtv.ui.strings.R.string.MAIN_COLOUR_TEMPERATURE", "context": "colour_temperature", "data": { "enums": [ { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NORMAL" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WARM" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_COOL" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CUSTOM" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230869, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WHITEPOINT_ALIGNMENT", "context": "whitepoint_alignment_item", "data": { "sliders": [ { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_RED_WP", "slider_data": { "min": 0, "max": 127, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_GREEN_WP", "slider_data": { "min": 0, "max": 127, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_BLUE_WP", "slider_data": { "min": 0, "max": 127, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_RED_BL", "slider_data": { "min": -7, "max": 8, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_GREEN_BL", "slider_data": { "min": -7, "max": 8, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_BLUE_BL", "slider_data": { "min": -7, "max": 8, "step_size": 1 } } ] }, "type": "MULTIPLE_SLIDER" }, { "node_id": 2131230870, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_WHITEPOINT_ALIGNMENT", "context": "whitepoint_alignment_menu", "data": { "nodes": [ { "node_id": 2131230871, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WHITEPOINT_ALIGNMENT_2POINT", "context": "whitepoint_alignment", "data": { "sliders": [ { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_RED_WP", "slider_data": { "min": 0, "max": 127, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_GREEN_WP", "slider_data": { "min": 0, "max": 127, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_BLUE_WP", "slider_data": { "min": 0, "max": 127, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_RED_BL", "slider_data": { "min": -7, "max": 8, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_GREEN_BL", "slider_data": { "min": -7, "max": 8, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_BLUE_BL", "slider_data": { "min": -7, "max": 8, "step_size": 1 } } ] }, "type": "MULTIPLE_SLIDER" }, { "node_id": 2131230872, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WHITEPOINT_ALIGNMENT_20POINT", "context": "whitepoint_alignment", "data": { "sliders": [ { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_LEVEL", "slider_data": { "min": 1, "max": 20, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_RED_OFFSET", "slider_data": { "min": -15, "max": 15, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_GREEN_OFFSET", "slider_data": { "min": -15, "max": 15, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_BLUE_OFFSET", "slider_data": { "min": -15, "max": 15, "step_size": 1 } } ] }, "type": "MULTIPLE_SLIDER" } ] } }, { "node_id": 2131230873, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_COLOUR_CONTROL", "context": "colour_control", "data": { "nodes": [ { "node_id": 2131230874, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MS_RED", "context": "colour_control", "data": { "sliders": [ { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_HUE", "slider_data": { "min": -15, "max": 15, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_SATURATION", "slider_data": { "min": -15, "max": 15, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_INTENSITY", "slider_data": { "min": -15, "max": 15, "step_size": 1 } } ] }, "type": "MULTIPLE_SLIDER" }, { "node_id": 2131230875, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MS_YELLOW", "context": "colour_control", "data": { "sliders": [ { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_HUE", "slider_data": { "min": -15, "max": 15, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_SATURATION", "slider_data": { "min": -15, "max": 15, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_INTENSITY", "slider_data": { "min": -15, "max": 15, "step_size": 1 } } ] }, "type": "MULTIPLE_SLIDER" }, { "node_id": 2131230876, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MS_GREEN", "context": "colour_control", "data": { "sliders": [ { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_HUE", "slider_data": { "min": -15, "max": 15, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_SATURATION", "slider_data": { "min": -15, "max": 15, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_INTENSITY", "slider_data": { "min": -15, "max": 15, "step_size": 1 } } ] }, "type": "MULTIPLE_SLIDER" }, { "node_id": 2131230877, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MS_CYAN", "context": "colour_control", "data": { "sliders": [ { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_HUE", "slider_data": { "min": -15, "max": 15, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_SATURATION", "slider_data": { "min": -15, "max": 15, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_INTENSITY", "slider_data": { "min": -15, "max": 15, "step_size": 1 } } ] }, "type": "MULTIPLE_SLIDER" }, { "node_id": 2131230878, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MS_BLUE", "context": "colour_control", "data": { "sliders": [ { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_HUE", "slider_data": { "min": -15, "max": 15, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_SATURATION", "slider_data": { "min": -15, "max": 15, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_INTENSITY", "slider_data": { "min": -15, "max": 15, "step_size": 1 } } ] }, "type": "MULTIPLE_SLIDER" }, { "node_id": 2131230879, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MS_MAGENTA", "context": "colour_control", "data": { "sliders": [ { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_HUE", "slider_data": { "min": -15, "max": 15, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_SATURATION", "slider_data": { "min": -15, "max": 15, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_INTENSITY", "slider_data": { "min": -15, "max": 15, "step_size": 1 } } ] }, "type": "MULTIPLE_SLIDER" }, { "node_id": 2131230880, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_RESET_ALL", "context": "reset_all", "data": {} } ] } }, { "node_id": 2131230881, "string_id": "org.droidtv.ui.strings.R.string.MAIN_RGB_ONLY_MODE", "context": "rgb_only_mode", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_RED" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GREEN" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BLUE" } ] }, "type": "LIST_NODE" } ] } }, { "node_id": 2131230882, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_CONTRAST_MENU", "context": "contrast_menu", "data": { "nodes": [ { "node_id": 2131230883, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CONTRAST_MODE", "context": "contrast_modes", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NORMAL" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OPTMIZED_FOR_PICTURE" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OPTMIZED_FOR_ENERGY_SAVING" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230884, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HDR_UPSCALING", "context": "hdr_upscaling", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230885, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PNR", "context": "perfect_natural_reality", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230886, "context": "hd_ultra_hdr", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AUTO_3D_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230887, "context": "perfect_contrast", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230888, "context": "video_contrast", "data": { "slider_data": { "min": 0, "max": 100, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230889, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LIGHT_SENSOR", "context": "light_sensor", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230890, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GAMMA", "context": "gamma", "data": { "slider_data": { "min": -4, "max": 4, "step_size": 1 } }, "type": "SLIDER_NODE" } ] } }, { "node_id": 2131230891, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_SHARPNESS", "context": "sharpness_menu", "data": { "nodes": [ { "node_id": 2131230892, "context": "super_resolution", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" } ] } }, { "node_id": 2131230893, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_PICTURE_CLEAN", "context": "picture_clean_menu", "data": { "nodes": [ { "node_id": 2131230894, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NOISE_REDUCTION", "context": "noise_reduction", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230895, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MPEG_ARTEFACT_REDUCTION", "context": "mpeg_artefact_reduction", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" } ] }, "type": "LIST_NODE" } ] } }, { "node_id": 2131230896, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_MOTION_MENU", "context": "motion_menu", "data": { "nodes": [ { "node_id": 2131230897, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MOTION_STYLE", "context": "motion_style", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PURE_CINEMA" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MOVIE" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_STANDARD" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SMOOTH" }, { "enum_id": 5, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PERSONAL" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230898, "context": "hd_natural_motion", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230899, "string_id": "org.droidtv.ui.strings.R.string.MISC_PERFECT_CLEAN_MOTION", "context": "clear_lcd", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" } ] }, "type": "LIST_NODE" } ] } } ] } }, { "node_id": 2131230900, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_ADVANCED_PICTURE_AI_SETTINGS", "context": "advanced_ai_settings", "data": { "nodes": [ { "node_id": 2131230901, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PERFECT_CONTRAST_AI", "context": "advanced_ai_settings", "data": { "enums": [ { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230902, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PERFECT_COLOUR_AI", "context": "advanced_ai_settings", "data": { "enums": [ { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230903, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PERFECT_SHARPNESS_AI", "context": "advanced_ai_settings", "data": { "enums": [ { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230904, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SOURCE_PERFECTION_AI", "context": "advanced_ai_settings", "data": { "enums": [ { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230905, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PERFECT_MOTION_AI", "context": "advanced_ai_settings", "data": { "enums": [ { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" } ] }, "type": "LIST_NODE" } ] } }, { "node_id": 2131230906, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_PICTURE_FORMAT", "context": "picture_format", "data": {} }, { "node_id": 2131230907, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DOLBY_VISION_NOTIFICATION", "context": "dolby_vision_notification", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230908, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_QUICK_PICTURE_SETTINGS", "context": "quick_picture_settings", "data": {} } ] } }, { "node_id": 2131230910, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_SOUND", "context": "sound", "data": { "nodes": [ { "node_id": 2131230911, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SOUND_STYLE", "context": "sound_style", "data": { "enums": [] }, "type": "LIST_NODE" }, { "node_id": 2131230912, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SOUND_STYLE", "context": "sound_style", "data": { "enums": [] }, "type": "LIST_NODE" }, { "node_id": 2131230913, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HEADPHONE_VOLUME", "context": "headphones_volume", "data": { "slider_data": { "min": 0, "max": 60, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230914, "type": "PARENT_NODE", "context": "sound_expert_mode", "data": { "nodes": [ { "node_id": 2131230915, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPEAKER_VIRTUALIZER", "context": "personal_dolby_atmos", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DOLBY_ATMOS_AUTO" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230916, "string_id": "org.droidtv.ui.strings.R.string.MISC_CLEAR_DIALOGUE", "context": "clear_dialogue", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230917, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AI_EQUALIZER", "context": "expert_equalizer", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230918, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CUSTOM_AI_EQUALIZER", "context": "expert_equalizer", "data": { "sliders": [ { "slider_id": "org.droidtv.ui.strings.R.string.MISC_100_HZ", "slider_data": { "min": -8, "max": 8, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MISC_300_HZ", "slider_data": { "min": -8, "max": 8, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MISC_1_KHZ", "slider_data": { "min": -8, "max": 8, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MISC_3_KHZ", "slider_data": { "min": -8, "max": 8, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MISC_10_KHZ", "slider_data": { "min": -8, "max": 8, "step_size": 1 } } ] }, "type": "MULTIPLE_SLIDER" } ] } }, { "node_id": 2131230919, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TV_POSITION", "context": "tv_placement", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WALL_MOUNTED" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON_TV_STAND" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230920, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_DTS_PLAY_FI", "context": "DTS_Play_Fi", "data": {} }, { "node_id": 2131230921, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_ADVANCED_SOUND", "context": "advanced_sound", "data": { "nodes": [ { "node_id": 2131230922, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AUTO_VOLUME", "context": "auto_volume_leveling", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NIGHT_MODE" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230923, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DELTA_VOLUME", "context": "DeltaVolume", "data": { "slider_data": { "min": -12, "max": 12, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230924, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AUDIO_OUT", "context": "tv_speakers", "data": { "enums": [] }, "type": "LIST_NODE" }, { "node_id": 2131230925, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DIGITAL_OUTPUT_FORMAT", "context": "digital_output_format", "data": { "enums": [ { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MULTICHANNEL" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.SPDIF_OUTPUT_3" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PCM" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230926, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DIGITAL_OUTPUT_LEVEL", "context": "digital_output_level", "data": { "enums": [ { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPDIF_MORE" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPDIF_MEDIUM" }, { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPDIF_LESS" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230927, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AUDIO_OUT_DELAY", "context": "audio_out_delay", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NO_LATENCY" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_IN_LIPSYNC" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230928, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AUDIO_OUT_OFFSET", "context": "audio_out_offset", "data": { "slider_data": { "min": 0, "max": 60, "step_size": 5 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230929, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SUBWOOFER_OUT", "context": "subwoofer_out", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230930, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CENTRE_SPEAKER_VOLUME", "context": "centre_speaker_volume", "data": { "slider_data": { "min": -3, "max": 3, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230931, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HEIGHT_SPEAKER_VOLUME", "context": "height_speaker_volume", "data": { "slider_data": { "min": -3, "max": 3, "step_size": 1 } }, "type": "SLIDER_NODE" } ] } }, { "node_id": 2131230932, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DOLBY_ATMOS_NOTIFICATION", "context": "dolby_atmos_notification", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" } ] } }, { "node_id": 2131230765, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MISC_AMBILIGHT", "context": "ambilight", "data": { "nodes": [ { "node_id": 2131230766, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_STYLE", "context": "ambilight_style", "data": { "nodes": [ { "node_id": 2131230767, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_STYLE_OFF", "context": "ambilight_off", "data": {} }, { "node_id": 2131230768, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FOLLOW_VIDEO", "context": "ambilight_follow_video", "data": { "enums": [ { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_STANDARD" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_NATURAL" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_SPORTS" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_VIVID" }, { "enum_id": 5, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_GAME" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230769, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FOLLOW_AUDIO", "context": "ambilight_follow_audio", "data": { "enums": [ { "enum_id": 101, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FOLLOW_AUDIO_STYLE_1" }, { "enum_id": 103, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FOLLOW_AUDIO_STYLE_3" }, { "enum_id": 107, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FOLLOW_AUDIO_STYLE_6" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230770, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_FOLLOW_COLOUR_2K20", "context": "ambilight_lounge_light", "data": { "enums": [ { "enum_id": 201, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LOUNGE_LIGHT_MODE_2" }, { "enum_id": 202, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LOUNGE_LIGHT_MODE_3" }, { "enum_id": 203, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LOUNGE_LIGHT_MODE_1" }, { "enum_id": 207, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WARM_WHITE" }, { "enum_id": 208, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CUSTOM_COLOUR" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230771, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_FOLLOW_FLAG", "context": "ambilight_follow_flag", "data": {} }, { "node_id": 2131230772, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_FOLLOW_APP", "context": "ambilight_follow_app", "data": {} } ] } }, { "node_id": 2131230773, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CUSTOM_COLOUR", "context": "ambilight_custom_colour", "data": { "colors": [ -14411265, -15097089, -15074561, -15073484, -1442023, -19943, -59111, -58908, -11647489, -12208897, -12190721, -12189861, -1179835, -16059, -47803, -47639, -8883713, -9320705, -9306881, -9306238, -917647, -12175, -36495, -36370, -6119937, -6432769, -6423041, -6422615, -589923, -8547, -25187, -25101, -3356161, -3544577, -3539201, -3538993, -327735, -4663, -13879, -13831, -2323, -1807, -1035, -520, -4, -67329, -461313, -789761 ] }, "type": "COLOR_PICKER_NODE" }, { "node_id": 2131230774, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBISLEEP", "context": "ambisleep", "data": { "nodes": [ { "node_id": 2131230775, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBISLEEP_ON", "context": "ambisleep_on", "data": {} }, { "node_id": 2131230776, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBISLEEP_DURATION", "context": "ambisleep_duration", "data": { "slider_data": { "min": 1, "max": 60, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230777, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBISLEEP_BRIGHTNESS", "context": "ambisleep_brightness", "data": { "slider_data": { "min": 1, "max": 9, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230778, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBISLEEP_COLOUR", "context": "ambisleep_colour", "data": { "colors": [ -62976, -45056, -37376, -29696, -14336 ] }, "type": "COLOR_PICKER_NODE" }, { "node_id": 2131230779, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBISLEEP_SOUND", "context": "ambisleep_sound", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBISLEEP_SOUND_CAMPFIRE" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBISLEEP_SOUND_HOWLING_WIND" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBISLEEP_SOUND_RAIN" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBISLEEP_SOUND_RAINFOREST" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBISLEEP_SOUND_SUMMER_NIGHT" }, { "enum_id": 5, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBISLEEP_SOUND_TROPICAL_BEACH" }, { "enum_id": 6, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBISLEEP_SOUND_WATERFALL" }, { "enum_id": 7, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBISLEEP_SOUND_NO_SOUND" } ] }, "type": "LIST_NODE" } ] } }, { "node_id": 2131230780, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_EXTENSION", "context": "ambilight_extension", "data": { "nodes": [ { "node_id": 2131230781, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_PLUS_HUE", "context": "ambilight_hue_menu", "data": { "nodes": [ { "node_id": 2131230782, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_HUE_CONFIGURE", "context": "ambilight_hue_wizard", "data": {} }, { "node_id": 2131230783, "context": "ambilight_hue_off", "data": {}, "type": "TOGGLE_NODE" }, { "node_id": 2131230784, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_HUE_FOLLOW_AMBILIGHT", "context": "ambilight_hue_follow_ambilight", "data": { "slider_data": { "min": 0, "max": 10, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230785, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LOUNGE_LIGHT_HUE", "context": "ambilight_lounge_light_hue", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230786, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_HUE_STATUS", "context": "ambilight_hue_status", "data": {} }, { "node_id": 2131230787, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_HUE_RESET", "context": "ambilight_hue_reset_configuration", "data": {} } ] } }, { "node_id": 2131230788, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_AIR", "context": "ambilight_light_play", "data": { "nodes": [ { "node_id": 2131230789, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_AIR_CONFIGURE", "context": "ambilight_light_play", "data": {} }, { "node_id": 2131230790, "type": "PARENT_NODE", "context": "ambilight_light_play", "data": {} }, { "node_id": 2131230791, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LOUNGE_LIGHT_AIR", "context": "ambilight_light_play", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LOUNGE_LIGHT_AIR_LABEL_1" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LOUNGE_LIGHT_AIR_LABEL_2" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230792, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_AIR_STATUS", "context": "ambilight_light_play", "data": {} }, { "node_id": 2131230793, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_AIR_RESET", "context": "ambilight_light_play", "data": {} } ] } } ] } }, { "node_id": 2131230794, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_ADVANCED_AMBILIGHT", "context": "ambilight_advanced", "data": { "nodes": [ { "node_id": 2131230795, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_BRIGHTNESS", "context": "ambilight_brightness", "data": { "slider_data": { "min": 0, "max": 10, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230796, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_SATURATION", "context": "ambilight_saturation", "data": { "slider_data": { "min": -2, "max": 2, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230797, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AMBILIGHT_BOTTOM_SIDE", "context": "ambilight_bottom_side", "data": { "enums": [ { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230798, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BRIGHTNESS_BOTTOM_SIDE", "context": "ambilight_brightness_bottom_side", "data": { "slider_data": { "min": 0, "max": 10, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230799, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WALL_COLOUR", "context": "ambilight_wall_colour", "data": { "colors": [ -1, -1651276, -3342439, -3347201, -3355393, -13108, -6451, -103, -12566464, -3693173, -6565376, -10053121, -2982751, -33664, -81560, -256, -15724528, -7508135, -10452480, -15513423, -5293203, -1888983, -5682169, -26368 ] }, "type": "WALL_COLOR_NODE" }, { "node_id": 2131230800, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TV_SWITCH_OFF", "context": "ambilight_tv_switch_off", "data": { "enums": [ { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FADE_OUT" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_IMMEDIATE_SWITCH_OFF" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230801, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ISF_TUNING", "context": "ambilight_isf_tuning", "data": { "sliders": [ { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_RED", "slider_data": { "min": 0, "max": 100, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_GREEN", "slider_data": { "min": 0, "max": 100, "step_size": 1 } }, { "slider_id": "org.droidtv.ui.strings.R.string.MAIN_MS_BLUE", "slider_data": { "min": 0, "max": 100, "step_size": 1 } } ] }, "type": "MULTIPLE_SLIDER" } ] } } ] } }, { "node_id": 2131230829, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_ECO_SETTINGS", "context": "eco_settings", "data": { "nodes": [ { "node_id": 2131230830, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ENERGY_SAVING", "context": "energy_saving", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230831, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_MUTE_SCREEN", "context": "screen_off", "data": {} }, { "node_id": 2131230832, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_LIGHT_SENSOR", "context": "eco_settings_light_sensor", "data": {} }, { "node_id": 2131230833, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AUTOMATIC_SWITCH_OFF", "context": "switch_off_timer", "data": { "slider_data": { "min": 0, "max": 240, "step_size": 30 } }, "type": "SLIDER_NODE" } ] } }, { "node_id": 2131230938, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_GENERAL_SETTINGS", "context": "general_settings", "data": { "nodes": [ { "node_id": 2131230939, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_USB_STORAGE", "context": "usb_storage", "data": {} }, { "node_id": 2131230940, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_USB_KEYBOARD_SETTINGS", "context": "usb_keyboard_settings", "data": {} }, { "node_id": 2131230941, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PHILIPS_WORDMARK", "context": "philips_wordmark", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MINIMUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MEDIUM" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAXIMUM" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230942, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LOCATION", "context": "location", "data": { "enums": [ { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HOME" }, { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SHOP" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230943, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_VB_SHOP_CONFIGURATION", "context": "shop_setup", "data": {} }, { "node_id": 2131230944, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MISC_EASYLINK", "context": "easylink_menu", "data": { "nodes": [ { "node_id": 2131230945, "string_id": "org.droidtv.ui.strings.R.string.MISC_EASYLINK", "context": "easylink", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230946, "string_id": "org.droidtv.ui.strings.R.string.MAIN_EASYLINK_REMOTE_CONTROL", "context": "easylink_remote_control", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" } ] } }, { "node_id": 2131230947, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_PD_EXPERIENCE_LIGHT_SETTINGS", "context": "pdel", "data": { "nodes": [ { "node_id": 2131230948, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PD_EXPERIENCE_LIGHT_ENABLE_ON_STANDBY", "context": "pdel", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230949, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PD_EXPERIENCE_LIGHT_BRIGHTNESS", "context": "pdel", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PD_EXPERIENCE_LIGHT_BRIGHTNESS_LOW" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PD_EXPERIENCE_LIGHT_BRIGHTNESS_MEDIUM" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PD_EXPERIENCE_LIGHT_BRIGHTNESS_HIGH" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PD_EXPERIENCE_LIGHT_BRIGHTNESS_MAXIMUM" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230950, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PD_EXPERIENCE_LIGHT_DURATION", "context": "pdel", "data": { "enums": [ { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PD_EXPERIENCE_LIGHT_DURATION_10_MINUTE" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PD_EXPERIENCE_LIGHT_DURATION_20_MINUTE" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PD_EXPERIENCE_LIGHT_DURATION_30_MINUTE" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PD_EXPERIENCE_LIGHT_DURATION_40_MINUTE" }, { "enum_id": 5, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PD_EXPERIENCE_LIGHT_DURATION_50_MINUTE" }, { "enum_id": 6, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PD_EXPERIENCE_LIGHT_DURATION_60_MINUTE" } ] }, "type": "LIST_NODE" } ] } }, { "node_id": 2131230951, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MISC_HDMI_ULTRA_HD", "context": "hdmi_ultra_hd", "data": { "nodes": [ { "node_id": 2131230952, "string_id": "org.droidtv.ui.strings.R.string.MISC_HDMI1", "context": "hdmi_ultra_hd", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UHD_STANDARD_2K18" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UHD_OPTIMAL_2K18" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230953, "string_id": "org.droidtv.ui.strings.R.string.MISC_HDMI2", "context": "hdmi_ultra_hd", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UHD_STANDARD_2K18" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UHD_OPTIMAL_2K18" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230954, "string_id": "org.droidtv.ui.strings.R.string.MISC_HDMI3", "context": "hdmi_ultra_hd", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UHD_STANDARD_2K18" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UHD_OPTIMAL_2K18" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230955, "string_id": "org.droidtv.ui.strings.R.string.MISC_HDMI4", "context": "hdmi_ultra_hd", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UHD_STANDARD_2K18" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UHD_OPTIMAL_2K18" } ] }, "type": "LIST_NODE" } ] } }, { "node_id": 2131230956, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_HDMI_AUTO_GAME_MODE", "context": "hdmi_auto_game_mode", "data": { "nodes": [ { "node_id": 2131230957, "string_id": "org.droidtv.ui.strings.R.string.MISC_HDMI1", "context": "hdmi_auto_game_mode", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230958, "string_id": "org.droidtv.ui.strings.R.string.MISC_HDMI2", "context": "hdmi_auto_game_mode", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230959, "string_id": "org.droidtv.ui.strings.R.string.MISC_HDMI3", "context": "hdmi_auto_game_mode", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230960, "string_id": "org.droidtv.ui.strings.R.string.MISC_HDMI4", "context": "hdmi_auto_game_mode", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" } ] } }, { "node_id": 2131230961, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HDMI_AUTO_MOVIE_MODE", "context": "auto_movie_mode", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230962, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_ADVANCED_INSTALLATION", "context": "advanced_general_settings", "data": { "nodes": [ { "node_id": 2131230963, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_HBBTV_SETTINGS", "context": "hbbtv_settings", "data": { "nodes": [ { "node_id": 2131230964, "string_id": "org.droidtv.ui.strings.R.string.MISC_HBB_TV", "context": "hbbtv", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230965, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HBBTV_TRACK_USAGE", "context": "hbbtv_track_usage", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230966, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HBBTV_COOKIES", "context": "hbbtv_cookies", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230967, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_CLEAR_APPROVED_APP_LISTING", "context": "hbbtv_clear_listing", "data": {} }, { "node_id": 2131230968, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_HBBTV_DEVICEID_RESET", "context": "hbbtv_device_id_reset", "data": {} } ] } }, { "node_id": 2131230969, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_OLED_SCREEN_SETTINGS", "context": "OLED_screen_settings", "data": { "nodes": [ { "node_id": 2131230970, "context": "still_image_protection", "data": { "enums": [ { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NORMAL" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HIGH" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230909, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_CLEAR_IMAGE_RESIDUAL", "context": "clear_image_residual", "data": {} } ] } } ] } }, { "node_id": 2131230971, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_VIEWING_DATA", "context": "viewing_data", "data": { "nodes": [ { "node_id": 2131230972, "string_id": "org.droidtv.ui.strings.R.string.MAIN_VIEWING_DATA_SETTINGS", "context": "viewing_data_settings", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230973, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_PRIVACY_POLICY", "context": "privacy_policy", "data": {} } ] } }, { "node_id": 2131230974, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_FACTORY_SETTINGS", "context": "factory_settings", "data": {} }, { "node_id": 2131230975, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_REINSTALL_TV", "context": "reinstall_tv", "data": {} }, { "node_id": 2131230976, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.CAM_PROFILE_NAME", "context": "CAM_profile_name", "data": {} } ] } }, { "node_id": 2131230834, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_REGION_AND_LANGUAGE", "context": "region_languages", "data": { "nodes": [ { "node_id": 2131230835, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_LANGUAGES", "context": "languages", "data": { "nodes": [ { "node_id": 2131230836, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MENU_LANGUAGE", "context": "menu_language", "data": { "enums": [ { "enum_id": 83, "string_id": "org.droidtv.ui.strings.R.string.MISC_AZERBAIJANI" }, { "enum_id": 48, "string_id": "org.droidtv.ui.strings.R.string.MISC_BAHASA_MELAYU" }, { "enum_id": 50, "string_id": "org.droidtv.ui.strings.R.string.MISC_INDONESIAN" }, { "enum_id": 37, "string_id": "org.droidtv.ui.strings.R.string.MISC_BULGARIAN" }, { "enum_id": 6, "string_id": "org.droidtv.ui.strings.R.string.MISC_CZECH" }, { "enum_id": 14, "string_id": "org.droidtv.ui.strings.R.string.MISC_DANISH" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MISC_GERMAN" }, { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MISC_ENGLISH" }, { "enum_id": 5, "string_id": "org.droidtv.ui.strings.R.string.MISC_SPANISH" }, { "enum_id": 26, "string_id": "org.droidtv.ui.strings.R.string.MISC_ESTONIAN" }, { "enum_id": 10, "string_id": "org.droidtv.ui.strings.R.string.MISC_GREEK" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MISC_FRENCH" }, { "enum_id": 43, "string_id": "org.droidtv.ui.strings.R.string.MISC_IRISH" }, { "enum_id": 13, "string_id": "org.droidtv.ui.strings.R.string.MISC_CROATIAN" }, { "enum_id": 75, "string_id": "org.droidtv.ui.strings.R.string.MISC_ICELANDIC" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MISC_ITALIAN" }, { "enum_id": 41, "string_id": "org.droidtv.ui.strings.R.string.MISC_KAZAKH" }, { "enum_id": 40, "string_id": "org.droidtv.ui.strings.R.string.MISC_LATVIAN" }, { "enum_id": 39, "string_id": "org.droidtv.ui.strings.R.string.MISC_LITHUANIAN" }, { "enum_id": 52, "string_id": "org.droidtv.ui.strings.R.string.MISC_MACEDONIAN" }, { "enum_id": 30, "string_id": "org.droidtv.ui.strings.R.string.MISC_HUNGARIAN" }, { "enum_id": 78, "string_id": "org.droidtv.ui.strings.R.string.MISC_MONGOLIAN" }, { "enum_id": 15, "string_id": "org.droidtv.ui.strings.R.string.MISC_DUTCH" }, { "enum_id": 19, "string_id": "org.droidtv.ui.strings.R.string.MISC_NORWEGIAN" }, { "enum_id": 7, "string_id": "org.droidtv.ui.strings.R.string.MISC_POLISH" }, { "enum_id": 20, "string_id": "org.droidtv.ui.strings.R.string.MISC_PORTUGUESE" }, { "enum_id": 36, "string_id": "org.droidtv.ui.strings.R.string.MISC_PORTUGUESE_BRAZILIAN" }, { "enum_id": 9, "string_id": "org.droidtv.ui.strings.R.string.MISC_RUSSIAN" }, { "enum_id": 25, "string_id": "org.droidtv.ui.strings.R.string.MISC_ROMANIAN" }, { "enum_id": 74, "string_id": "org.droidtv.ui.strings.R.string.MISC_ALBANIAN" }, { "enum_id": 21, "string_id": "org.droidtv.ui.strings.R.string.MISC_SERBIAN" }, { "enum_id": 22, "string_id": "org.droidtv.ui.strings.R.string.MISC_SLOVAK" }, { "enum_id": 23, "string_id": "org.droidtv.ui.strings.R.string.MISC_SLOVENIAN" }, { "enum_id": 16, "string_id": "org.droidtv.ui.strings.R.string.MISC_FINNISH" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MISC_SWEDISH" }, { "enum_id": 8, "string_id": "org.droidtv.ui.strings.R.string.MISC_TURKISH" }, { "enum_id": 51, "string_id": "org.droidtv.ui.strings.R.string.MISC_VIETNAMESE" }, { "enum_id": 27, "string_id": "org.droidtv.ui.strings.R.string.MISC_UKRAINIAN" }, { "enum_id": 49, "string_id": "org.droidtv.ui.strings.R.string.MISC_TRADITIONAL_CHINESE" }, { "enum_id": 34, "string_id": "org.droidtv.ui.strings.R.string.MISC_CHINESE" }, { "enum_id": 28, "string_id": "org.droidtv.ui.strings.R.string.MISC_ARABIC" }, { "enum_id": 29, "string_id": "org.droidtv.ui.strings.R.string.MISC_HEBREW" }, { "enum_id": 45, "string_id": "org.droidtv.ui.strings.R.string.MISC_THAI" }, { "enum_id": 58, "string_id": "org.droidtv.ui.strings.R.string.MISC_HINDI" }, { "enum_id": 79, "string_id": "org.droidtv.ui.strings.R.string.MISC_BENGALI" }, { "enum_id": 80, "string_id": "org.droidtv.ui.strings.R.string.MISC_TELUGU" }, { "enum_id": 81, "string_id": "org.droidtv.ui.strings.R.string.MISC_MARATHI" }, { "enum_id": 53, "string_id": "org.droidtv.ui.strings.R.string.MISC_TAMIL" }, { "enum_id": 82, "string_id": "org.droidtv.ui.strings.R.string.MISC_ARMENIAN" }, { "enum_id": 84, "string_id": "org.droidtv.ui.strings.R.string.MISC_GEORGIAN" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230837, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PRIMARY_AUDIO", "context": "primary_audio", "data": { "enums": [ { "enum_id": 74, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ALBANIAN_TRANSLATED" }, { "enum_id": 85, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ALTERNATIVE_LANGUAGE_A" }, { "enum_id": 86, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ALTERNATIVE_LANGUAGE_B" }, { "enum_id": 28, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ARABIC_TRANSLATED" }, { "enum_id": 82, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ARMENIAN_TRANSLATED" }, { "enum_id": 83, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AZERBAIJANI_TRANSLATED" }, { "enum_id": 48, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BAHASA_MELAYU_TRANSLATED" }, { "enum_id": 11, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BASQUE_TRANSLATED" }, { "enum_id": 37, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BULGARIAN_TRANSLATED" }, { "enum_id": 12, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CATALAN_TRANSLATED" }, { "enum_id": 49, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CHINESE_TRANSLATED" }, { "enum_id": 61, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MANDARIN_TRANSLATED" }, { "enum_id": 76, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CANTONESE_TRANSLATED" }, { "enum_id": 13, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CROATIAN_TRANSLATED" }, { "enum_id": 6, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CZECH_TRANSLATED" }, { "enum_id": 14, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DANISH_TRANSLATED" }, { "enum_id": 15, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DUTCH_TRANSLATED" }, { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ENGLISH_TRANSLATED" }, { "enum_id": 26, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ESTONIAN_TRANSLATED" }, { "enum_id": 16, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FINNISH_TRANSLATED" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FRENCH_TRANSLATED" }, { "enum_id": 18, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GALLEGAN_TRANSLATED" }, { "enum_id": 84, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GEORGIAN_TRANSLATED" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GERMAN_TRANSLATED" }, { "enum_id": 10, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GREEK_TRANSLATED" }, { "enum_id": 29, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HEBREW_TRANSLATED" }, { "enum_id": 58, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HINDI_TRANSLATED" }, { "enum_id": 30, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HUNGARIAN_TRANSLATED" }, { "enum_id": 46, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ORIGINAL_AUDIO" }, { "enum_id": 59, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SECOND_AUDIO" }, { "enum_id": 60, "string_id": "org.droidtv.ui.strings.R.string.MAIN_THIRD_AUDIO" }, { "enum_id": 47, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MULTIPLE_LANGUAGES" }, { "enum_id": 75, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ICELANDIC_TRANSLATED" }, { "enum_id": 50, "string_id": "org.droidtv.ui.strings.R.string.MAIN_INDONESIAN_TRANSLATED" }, { "enum_id": 43, "string_id": "org.droidtv.ui.strings.R.string.MAIN_IRISH_TRANSLATED" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ITALIAN_TRANSLATED" }, { "enum_id": 57, "string_id": "org.droidtv.ui.strings.R.string.MAIN_JAPANESE_TRANSLATED" }, { "enum_id": 41, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KAZAKH_TRANSLATED" }, { "enum_id": 55, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KOREAN_TRANSLATED" }, { "enum_id": 40, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LATVIAN_TRANSLATED" }, { "enum_id": 39, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LITHUANIAN_TRANSLATED" }, { "enum_id": 52, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MACEDONIAN_TRANSLATED" }, { "enum_id": 54, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAORI_TRANSLATED" }, { "enum_id": 19, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NORWEGIAN_TRANSLATED" }, { "enum_id": 44, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ORIGINAL_LANGUAGE" }, { "enum_id": 7, "string_id": "org.droidtv.ui.strings.R.string.MAIN_POLISH_TRANSLATED" }, { "enum_id": 20, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PORTUGUESE_TRANSLATED" }, { "enum_id": 25, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ROMANIAN_TRANSLATED" }, { "enum_id": 9, "string_id": "org.droidtv.ui.strings.R.string.MAIN_RUSSIAN_TRANSLATED" }, { "enum_id": 72, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SAMI_TRANSLATED" }, { "enum_id": 17, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GAELIC_TRANSLATED" }, { "enum_id": 34, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SIMPLIFIED_CHINESE_TRANSLATED" }, { "enum_id": 22, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SLOVAK_TRANSLATED" }, { "enum_id": 23, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SLOVENIAN_TRANSLATED" }, { "enum_id": 21, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SERBIAN_TRANSLATED" }, { "enum_id": 5, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPANISH_TRANSLATED" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SWEDISH_TRANSLATED" }, { "enum_id": 53, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TAMIL_TRANSLATED" }, { "enum_id": 45, "string_id": "org.droidtv.ui.strings.R.string.MAIN_THAI_TRANSLATED" }, { "enum_id": 8, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TURKISH_TRANSLATED" }, { "enum_id": 27, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UKRAINIAN_TRANSLATED" }, { "enum_id": 51, "string_id": "org.droidtv.ui.strings.R.string.MAIN_VIETNAMESE_TRANSLATED" }, { "enum_id": 24, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WELSH_TRANSLATED" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230838, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SECONDARY_AUDIO", "context": "secondary_audio", "data": { "enums": [ { "enum_id": 74, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ALBANIAN_TRANSLATED" }, { "enum_id": 85, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ALTERNATIVE_LANGUAGE_A" }, { "enum_id": 86, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ALTERNATIVE_LANGUAGE_B" }, { "enum_id": 28, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ARABIC_TRANSLATED" }, { "enum_id": 82, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ARMENIAN_TRANSLATED" }, { "enum_id": 83, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AZERBAIJANI_TRANSLATED" }, { "enum_id": 48, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BAHASA_MELAYU_TRANSLATED" }, { "enum_id": 11, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BASQUE_TRANSLATED" }, { "enum_id": 37, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BULGARIAN_TRANSLATED" }, { "enum_id": 12, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CATALAN_TRANSLATED" }, { "enum_id": 49, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CHINESE_TRANSLATED" }, { "enum_id": 61, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MANDARIN_TRANSLATED" }, { "enum_id": 76, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CANTONESE_TRANSLATED" }, { "enum_id": 13, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CROATIAN_TRANSLATED" }, { "enum_id": 6, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CZECH_TRANSLATED" }, { "enum_id": 14, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DANISH_TRANSLATED" }, { "enum_id": 15, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DUTCH_TRANSLATED" }, { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ENGLISH_TRANSLATED" }, { "enum_id": 26, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ESTONIAN_TRANSLATED" }, { "enum_id": 16, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FINNISH_TRANSLATED" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FRENCH_TRANSLATED" }, { "enum_id": 18, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GALLEGAN_TRANSLATED" }, { "enum_id": 84, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GEORGIAN_TRANSLATED" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GERMAN_TRANSLATED" }, { "enum_id": 10, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GREEK_TRANSLATED" }, { "enum_id": 29, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HEBREW_TRANSLATED" }, { "enum_id": 58, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HINDI_TRANSLATED" }, { "enum_id": 30, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HUNGARIAN_TRANSLATED" }, { "enum_id": 46, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ORIGINAL_AUDIO" }, { "enum_id": 59, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SECOND_AUDIO" }, { "enum_id": 60, "string_id": "org.droidtv.ui.strings.R.string.MAIN_THIRD_AUDIO" }, { "enum_id": 47, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MULTIPLE_LANGUAGES" }, { "enum_id": 75, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ICELANDIC_TRANSLATED" }, { "enum_id": 50, "string_id": "org.droidtv.ui.strings.R.string.MAIN_INDONESIAN_TRANSLATED" }, { "enum_id": 43, "string_id": "org.droidtv.ui.strings.R.string.MAIN_IRISH_TRANSLATED" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ITALIAN_TRANSLATED" }, { "enum_id": 57, "string_id": "org.droidtv.ui.strings.R.string.MAIN_JAPANESE_TRANSLATED" }, { "enum_id": 41, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KAZAKH_TRANSLATED" }, { "enum_id": 55, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KOREAN_TRANSLATED" }, { "enum_id": 40, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LATVIAN_TRANSLATED" }, { "enum_id": 39, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LITHUANIAN_TRANSLATED" }, { "enum_id": 52, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MACEDONIAN_TRANSLATED" }, { "enum_id": 54, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAORI_TRANSLATED" }, { "enum_id": 19, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NORWEGIAN_TRANSLATED" }, { "enum_id": 44, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ORIGINAL_LANGUAGE" }, { "enum_id": 7, "string_id": "org.droidtv.ui.strings.R.string.MAIN_POLISH_TRANSLATED" }, { "enum_id": 20, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PORTUGUESE_TRANSLATED" }, { "enum_id": 25, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ROMANIAN_TRANSLATED" }, { "enum_id": 9, "string_id": "org.droidtv.ui.strings.R.string.MAIN_RUSSIAN_TRANSLATED" }, { "enum_id": 72, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SAMI_TRANSLATED" }, { "enum_id": 17, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GAELIC_TRANSLATED" }, { "enum_id": 34, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SIMPLIFIED_CHINESE_TRANSLATED" }, { "enum_id": 22, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SLOVAK_TRANSLATED" }, { "enum_id": 23, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SLOVENIAN_TRANSLATED" }, { "enum_id": 21, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SERBIAN_TRANSLATED" }, { "enum_id": 5, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPANISH_TRANSLATED" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SWEDISH_TRANSLATED" }, { "enum_id": 53, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TAMIL_TRANSLATED" }, { "enum_id": 45, "string_id": "org.droidtv.ui.strings.R.string.MAIN_THAI_TRANSLATED" }, { "enum_id": 8, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TURKISH_TRANSLATED" }, { "enum_id": 27, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UKRAINIAN_TRANSLATED" }, { "enum_id": 51, "string_id": "org.droidtv.ui.strings.R.string.MAIN_VIETNAMESE_TRANSLATED" }, { "enum_id": 24, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WELSH_TRANSLATED" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230839, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PRIMARY_SUBTITLES", "context": "primary_subtitles", "data": { "enums": [ { "enum_id": 74, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ALBANIAN_TRANSLATED" }, { "enum_id": 28, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ARABIC_TRANSLATED" }, { "enum_id": 82, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ARMENIAN_TRANSLATED" }, { "enum_id": 83, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AZERBAIJANI_TRANSLATED" }, { "enum_id": 48, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BAHASA_MELAYU_TRANSLATED" }, { "enum_id": 11, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BASQUE_TRANSLATED" }, { "enum_id": 37, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BULGARIAN_TRANSLATED" }, { "enum_id": 12, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CATALAN_TRANSLATED" }, { "enum_id": 49, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CHINESE_TRANSLATED" }, { "enum_id": 61, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MANDARIN_TRANSLATED" }, { "enum_id": 76, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CANTONESE_TRANSLATED" }, { "enum_id": 13, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CROATIAN_TRANSLATED" }, { "enum_id": 6, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CZECH_TRANSLATED" }, { "enum_id": 14, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DANISH_TRANSLATED" }, { "enum_id": 15, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DUTCH_TRANSLATED" }, { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ENGLISH_TRANSLATED" }, { "enum_id": 26, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ESTONIAN_TRANSLATED" }, { "enum_id": 16, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FINNISH_TRANSLATED" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FRENCH_TRANSLATED" }, { "enum_id": 18, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GALLEGAN_TRANSLATED" }, { "enum_id": 84, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GEORGIAN_TRANSLATED" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GERMAN_TRANSLATED" }, { "enum_id": 10, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GREEK_TRANSLATED" }, { "enum_id": 29, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HEBREW_TRANSLATED" }, { "enum_id": 58, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HINDI_TRANSLATED" }, { "enum_id": 30, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HUNGARIAN_TRANSLATED" }, { "enum_id": 46, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ORIGINAL_AUDIO" }, { "enum_id": 59, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SECOND_AUDIO" }, { "enum_id": 60, "string_id": "org.droidtv.ui.strings.R.string.MAIN_THIRD_AUDIO" }, { "enum_id": 47, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MULTIPLE_LANGUAGES" }, { "enum_id": 75, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ICELANDIC_TRANSLATED" }, { "enum_id": 50, "string_id": "org.droidtv.ui.strings.R.string.MAIN_INDONESIAN_TRANSLATED" }, { "enum_id": 43, "string_id": "org.droidtv.ui.strings.R.string.MAIN_IRISH_TRANSLATED" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ITALIAN_TRANSLATED" }, { "enum_id": 57, "string_id": "org.droidtv.ui.strings.R.string.MAIN_JAPANESE_TRANSLATED" }, { "enum_id": 41, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KAZAKH_TRANSLATED" }, { "enum_id": 55, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KOREAN_TRANSLATED" }, { "enum_id": 40, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LATVIAN_TRANSLATED" }, { "enum_id": 39, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LITHUANIAN_TRANSLATED" }, { "enum_id": 52, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MACEDONIAN_TRANSLATED" }, { "enum_id": 54, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAORI_TRANSLATED" }, { "enum_id": 19, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NORWEGIAN_TRANSLATED" }, { "enum_id": 44, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ORIGINAL_LANGUAGE" }, { "enum_id": 7, "string_id": "org.droidtv.ui.strings.R.string.MAIN_POLISH_TRANSLATED" }, { "enum_id": 20, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PORTUGUESE_TRANSLATED" }, { "enum_id": 25, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ROMANIAN_TRANSLATED" }, { "enum_id": 9, "string_id": "org.droidtv.ui.strings.R.string.MAIN_RUSSIAN_TRANSLATED" }, { "enum_id": 72, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SAMI_TRANSLATED" }, { "enum_id": 17, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GAELIC_TRANSLATED" }, { "enum_id": 34, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SIMPLIFIED_CHINESE_TRANSLATED" }, { "enum_id": 22, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SLOVAK_TRANSLATED" }, { "enum_id": 23, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SLOVENIAN_TRANSLATED" }, { "enum_id": 21, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SERBIAN_TRANSLATED" }, { "enum_id": 5, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPANISH_TRANSLATED" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SWEDISH_TRANSLATED" }, { "enum_id": 53, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TAMIL_TRANSLATED" }, { "enum_id": 45, "string_id": "org.droidtv.ui.strings.R.string.MAIN_THAI_TRANSLATED" }, { "enum_id": 8, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TURKISH_TRANSLATED" }, { "enum_id": 27, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UKRAINIAN_TRANSLATED" }, { "enum_id": 51, "string_id": "org.droidtv.ui.strings.R.string.MAIN_VIETNAMESE_TRANSLATED" }, { "enum_id": 24, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WELSH_TRANSLATED" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230840, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SECONDARY_SUBTITLES", "context": "secondary_subtitles", "data": { "enums": [ { "enum_id": 74, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ALBANIAN_TRANSLATED" }, { "enum_id": 28, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ARABIC_TRANSLATED" }, { "enum_id": 82, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ARMENIAN_TRANSLATED" }, { "enum_id": 83, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AZERBAIJANI_TRANSLATED" }, { "enum_id": 48, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BAHASA_MELAYU_TRANSLATED" }, { "enum_id": 11, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BASQUE_TRANSLATED" }, { "enum_id": 37, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BULGARIAN_TRANSLATED" }, { "enum_id": 12, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CATALAN_TRANSLATED" }, { "enum_id": 49, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CHINESE_TRANSLATED" }, { "enum_id": 61, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MANDARIN_TRANSLATED" }, { "enum_id": 76, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CANTONESE_TRANSLATED" }, { "enum_id": 13, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CROATIAN_TRANSLATED" }, { "enum_id": 6, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CZECH_TRANSLATED" }, { "enum_id": 14, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DANISH_TRANSLATED" }, { "enum_id": 15, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DUTCH_TRANSLATED" }, { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ENGLISH_TRANSLATED" }, { "enum_id": 26, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ESTONIAN_TRANSLATED" }, { "enum_id": 16, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FINNISH_TRANSLATED" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FRENCH_TRANSLATED" }, { "enum_id": 18, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GALLEGAN_TRANSLATED" }, { "enum_id": 84, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GEORGIAN_TRANSLATED" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GERMAN_TRANSLATED" }, { "enum_id": 10, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GREEK_TRANSLATED" }, { "enum_id": 29, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HEBREW_TRANSLATED" }, { "enum_id": 58, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HINDI_TRANSLATED" }, { "enum_id": 30, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HUNGARIAN_TRANSLATED" }, { "enum_id": 46, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ORIGINAL_AUDIO" }, { "enum_id": 59, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SECOND_AUDIO" }, { "enum_id": 60, "string_id": "org.droidtv.ui.strings.R.string.MAIN_THIRD_AUDIO" }, { "enum_id": 47, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MULTIPLE_LANGUAGES" }, { "enum_id": 75, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ICELANDIC_TRANSLATED" }, { "enum_id": 50, "string_id": "org.droidtv.ui.strings.R.string.MAIN_INDONESIAN_TRANSLATED" }, { "enum_id": 43, "string_id": "org.droidtv.ui.strings.R.string.MAIN_IRISH_TRANSLATED" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ITALIAN_TRANSLATED" }, { "enum_id": 57, "string_id": "org.droidtv.ui.strings.R.string.MAIN_JAPANESE_TRANSLATED" }, { "enum_id": 41, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KAZAKH_TRANSLATED" }, { "enum_id": 55, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KOREAN_TRANSLATED" }, { "enum_id": 40, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LATVIAN_TRANSLATED" }, { "enum_id": 39, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LITHUANIAN_TRANSLATED" }, { "enum_id": 52, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MACEDONIAN_TRANSLATED" }, { "enum_id": 54, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAORI_TRANSLATED" }, { "enum_id": 19, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NORWEGIAN_TRANSLATED" }, { "enum_id": 44, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ORIGINAL_LANGUAGE" }, { "enum_id": 7, "string_id": "org.droidtv.ui.strings.R.string.MAIN_POLISH_TRANSLATED" }, { "enum_id": 20, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PORTUGUESE_TRANSLATED" }, { "enum_id": 25, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ROMANIAN_TRANSLATED" }, { "enum_id": 9, "string_id": "org.droidtv.ui.strings.R.string.MAIN_RUSSIAN_TRANSLATED" }, { "enum_id": 72, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SAMI_TRANSLATED" }, { "enum_id": 17, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GAELIC_TRANSLATED" }, { "enum_id": 34, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SIMPLIFIED_CHINESE_TRANSLATED" }, { "enum_id": 22, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SLOVAK_TRANSLATED" }, { "enum_id": 23, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SLOVENIAN_TRANSLATED" }, { "enum_id": 21, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SERBIAN_TRANSLATED" }, { "enum_id": 5, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPANISH_TRANSLATED" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SWEDISH_TRANSLATED" }, { "enum_id": 53, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TAMIL_TRANSLATED" }, { "enum_id": 45, "string_id": "org.droidtv.ui.strings.R.string.MAIN_THAI_TRANSLATED" }, { "enum_id": 8, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TURKISH_TRANSLATED" }, { "enum_id": 27, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UKRAINIAN_TRANSLATED" }, { "enum_id": 51, "string_id": "org.droidtv.ui.strings.R.string.MAIN_VIETNAMESE_TRANSLATED" }, { "enum_id": 24, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WELSH_TRANSLATED" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230841, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PRIMARY_TELETEXT", "context": "primary_text", "data": { "enums": [ { "enum_id": 74, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ALBANIAN_TRANSLATED" }, { "enum_id": 28, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ARABIC_TRANSLATED" }, { "enum_id": 82, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ARMENIAN_TRANSLATED" }, { "enum_id": 83, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AZERBAIJANI_TRANSLATED" }, { "enum_id": 48, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BAHASA_MELAYU_TRANSLATED" }, { "enum_id": 11, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BASQUE_TRANSLATED" }, { "enum_id": 37, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BULGARIAN_TRANSLATED" }, { "enum_id": 12, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CATALAN_TRANSLATED" }, { "enum_id": 49, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CHINESE_TRANSLATED" }, { "enum_id": 61, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MANDARIN_TRANSLATED" }, { "enum_id": 76, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CANTONESE_TRANSLATED" }, { "enum_id": 13, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CROATIAN_TRANSLATED" }, { "enum_id": 6, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CZECH_TRANSLATED" }, { "enum_id": 14, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DANISH_TRANSLATED" }, { "enum_id": 15, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DUTCH_TRANSLATED" }, { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ENGLISH_TRANSLATED" }, { "enum_id": 26, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ESTONIAN_TRANSLATED" }, { "enum_id": 16, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FINNISH_TRANSLATED" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FRENCH_TRANSLATED" }, { "enum_id": 18, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GALLEGAN_TRANSLATED" }, { "enum_id": 84, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GEORGIAN_TRANSLATED" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GERMAN_TRANSLATED" }, { "enum_id": 10, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GREEK_TRANSLATED" }, { "enum_id": 29, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HEBREW_TRANSLATED" }, { "enum_id": 58, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HINDI_TRANSLATED" }, { "enum_id": 30, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HUNGARIAN_TRANSLATED" }, { "enum_id": 46, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ORIGINAL_AUDIO" }, { "enum_id": 59, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SECOND_AUDIO" }, { "enum_id": 60, "string_id": "org.droidtv.ui.strings.R.string.MAIN_THIRD_AUDIO" }, { "enum_id": 47, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MULTIPLE_LANGUAGES" }, { "enum_id": 75, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ICELANDIC_TRANSLATED" }, { "enum_id": 50, "string_id": "org.droidtv.ui.strings.R.string.MAIN_INDONESIAN_TRANSLATED" }, { "enum_id": 43, "string_id": "org.droidtv.ui.strings.R.string.MAIN_IRISH_TRANSLATED" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ITALIAN_TRANSLATED" }, { "enum_id": 57, "string_id": "org.droidtv.ui.strings.R.string.MAIN_JAPANESE_TRANSLATED" }, { "enum_id": 41, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KAZAKH_TRANSLATED" }, { "enum_id": 55, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KOREAN_TRANSLATED" }, { "enum_id": 40, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LATVIAN_TRANSLATED" }, { "enum_id": 39, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LITHUANIAN_TRANSLATED" }, { "enum_id": 52, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MACEDONIAN_TRANSLATED" }, { "enum_id": 54, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAORI_TRANSLATED" }, { "enum_id": 19, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NORWEGIAN_TRANSLATED" }, { "enum_id": 44, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ORIGINAL_LANGUAGE" }, { "enum_id": 7, "string_id": "org.droidtv.ui.strings.R.string.MAIN_POLISH_TRANSLATED" }, { "enum_id": 20, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PORTUGUESE_TRANSLATED" }, { "enum_id": 25, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ROMANIAN_TRANSLATED" }, { "enum_id": 9, "string_id": "org.droidtv.ui.strings.R.string.MAIN_RUSSIAN_TRANSLATED" }, { "enum_id": 72, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SAMI_TRANSLATED" }, { "enum_id": 17, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GAELIC_TRANSLATED" }, { "enum_id": 34, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SIMPLIFIED_CHINESE_TRANSLATED" }, { "enum_id": 22, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SLOVAK_TRANSLATED" }, { "enum_id": 23, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SLOVENIAN_TRANSLATED" }, { "enum_id": 21, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SERBIAN_TRANSLATED" }, { "enum_id": 5, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPANISH_TRANSLATED" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SWEDISH_TRANSLATED" }, { "enum_id": 53, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TAMIL_TRANSLATED" }, { "enum_id": 45, "string_id": "org.droidtv.ui.strings.R.string.MAIN_THAI_TRANSLATED" }, { "enum_id": 8, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TURKISH_TRANSLATED" }, { "enum_id": 27, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UKRAINIAN_TRANSLATED" }, { "enum_id": 51, "string_id": "org.droidtv.ui.strings.R.string.MAIN_VIETNAMESE_TRANSLATED" }, { "enum_id": 24, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WELSH_TRANSLATED" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230842, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SECONDARY_TELETEXT", "context": "secondary_text", "data": { "enums": [ { "enum_id": 74, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ALBANIAN_TRANSLATED" }, { "enum_id": 28, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ARABIC_TRANSLATED" }, { "enum_id": 82, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ARMENIAN_TRANSLATED" }, { "enum_id": 83, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AZERBAIJANI_TRANSLATED" }, { "enum_id": 48, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BAHASA_MELAYU_TRANSLATED" }, { "enum_id": 11, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BASQUE_TRANSLATED" }, { "enum_id": 37, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BULGARIAN_TRANSLATED" }, { "enum_id": 12, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CATALAN_TRANSLATED" }, { "enum_id": 49, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CHINESE_TRANSLATED" }, { "enum_id": 61, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MANDARIN_TRANSLATED" }, { "enum_id": 76, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CANTONESE_TRANSLATED" }, { "enum_id": 13, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CROATIAN_TRANSLATED" }, { "enum_id": 6, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CZECH_TRANSLATED" }, { "enum_id": 14, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DANISH_TRANSLATED" }, { "enum_id": 15, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DUTCH_TRANSLATED" }, { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ENGLISH_TRANSLATED" }, { "enum_id": 26, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ESTONIAN_TRANSLATED" }, { "enum_id": 16, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FINNISH_TRANSLATED" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_FRENCH_TRANSLATED" }, { "enum_id": 18, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GALLEGAN_TRANSLATED" }, { "enum_id": 84, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GEORGIAN_TRANSLATED" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GERMAN_TRANSLATED" }, { "enum_id": 10, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GREEK_TRANSLATED" }, { "enum_id": 29, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HEBREW_TRANSLATED" }, { "enum_id": 58, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HINDI_TRANSLATED" }, { "enum_id": 30, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HUNGARIAN_TRANSLATED" }, { "enum_id": 46, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ORIGINAL_AUDIO" }, { "enum_id": 59, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SECOND_AUDIO" }, { "enum_id": 60, "string_id": "org.droidtv.ui.strings.R.string.MAIN_THIRD_AUDIO" }, { "enum_id": 47, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MULTIPLE_LANGUAGES" }, { "enum_id": 75, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ICELANDIC_TRANSLATED" }, { "enum_id": 50, "string_id": "org.droidtv.ui.strings.R.string.MAIN_INDONESIAN_TRANSLATED" }, { "enum_id": 43, "string_id": "org.droidtv.ui.strings.R.string.MAIN_IRISH_TRANSLATED" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ITALIAN_TRANSLATED" }, { "enum_id": 57, "string_id": "org.droidtv.ui.strings.R.string.MAIN_JAPANESE_TRANSLATED" }, { "enum_id": 41, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KAZAKH_TRANSLATED" }, { "enum_id": 55, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KOREAN_TRANSLATED" }, { "enum_id": 40, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LATVIAN_TRANSLATED" }, { "enum_id": 39, "string_id": "org.droidtv.ui.strings.R.string.MAIN_LITHUANIAN_TRANSLATED" }, { "enum_id": 52, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MACEDONIAN_TRANSLATED" }, { "enum_id": 54, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MAORI_TRANSLATED" }, { "enum_id": 19, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NORWEGIAN_TRANSLATED" }, { "enum_id": 44, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ORIGINAL_LANGUAGE" }, { "enum_id": 7, "string_id": "org.droidtv.ui.strings.R.string.MAIN_POLISH_TRANSLATED" }, { "enum_id": 20, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PORTUGUESE_TRANSLATED" }, { "enum_id": 25, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ROMANIAN_TRANSLATED" }, { "enum_id": 9, "string_id": "org.droidtv.ui.strings.R.string.MAIN_RUSSIAN_TRANSLATED" }, { "enum_id": 72, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SAMI_TRANSLATED" }, { "enum_id": 17, "string_id": "org.droidtv.ui.strings.R.string.MAIN_GAELIC_TRANSLATED" }, { "enum_id": 34, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SIMPLIFIED_CHINESE_TRANSLATED" }, { "enum_id": 22, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SLOVAK_TRANSLATED" }, { "enum_id": 23, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SLOVENIAN_TRANSLATED" }, { "enum_id": 21, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SERBIAN_TRANSLATED" }, { "enum_id": 5, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPANISH_TRANSLATED" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SWEDISH_TRANSLATED" }, { "enum_id": 53, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TAMIL_TRANSLATED" }, { "enum_id": 45, "string_id": "org.droidtv.ui.strings.R.string.MAIN_THAI_TRANSLATED" }, { "enum_id": 8, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TURKISH_TRANSLATED" }, { "enum_id": 27, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UKRAINIAN_TRANSLATED" }, { "enum_id": 51, "string_id": "org.droidtv.ui.strings.R.string.MAIN_VIETNAMESE_TRANSLATED" }, { "enum_id": 24, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WELSH_TRANSLATED" } ] }, "type": "LIST_NODE" } ] } }, { "node_id": 2131230843, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_POSTAL_CODE", "context": "postal_code", "data": {} }, { "node_id": 2131230844, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_CLOCK", "context": "clock", "data": { "nodes": [ { "node_id": 2131230845, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AUTO_CLOCK_MODE", "context": "auto_clock_mode", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AUTOMATIC" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_COUNTRY_DEPENDENT" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MANUAL" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230846, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TIME_ZONE", "context": "time_zone", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KAZAKH_WESTERN" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KAZAKH_EASTERN" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230847, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TIME_ZONE", "context": "time_zone", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PORTUGAL_MADEIRA" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AZORES" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230848, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TIME_ZONE", "context": "time_zone", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KALINGRAD" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MOSCOW" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SAMARA" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_YEKATERINBURG" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OMSK" }, { "enum_id": 5, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KRASNOYARSK" }, { "enum_id": 6, "string_id": "org.droidtv.ui.strings.R.string.MAIN_IRKUTSK" }, { "enum_id": 7, "string_id": "org.droidtv.ui.strings.R.string.MAIN_YAKUTSK" }, { "enum_id": 8, "string_id": "org.droidtv.ui.strings.R.string.MAIN_VLADIVOSTOK" }, { "enum_id": 9, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SREDNEKOLYMSK" }, { "enum_id": 10, "string_id": "org.droidtv.ui.strings.R.string.MAIN_KAMCHATKA" }, { "enum_id": 11, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NONE" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230849, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TIME_ZONE", "context": "time_zone", "data": { "enums": [ { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPAIN_BALEARES" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CANARY_ISLANDS" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230850, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TIME_ZONE", "context": "time_zone", "data": { "enums": [ { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_QUEENSLAND" }, { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NEW_SOUTH_WALES" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_VICTORIA" }, { "enum_id": 5, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TASMANIA" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SOUTH_AUSTRALIA" }, { "enum_id": 6, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NORTHERN_TERRITORY" }, { "enum_id": 4, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WESTERN_AUSTRALIA" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230851, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TIME_ZONE", "context": "time_zone", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_INDONESIA_WESTERN_TIME_ZONE" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_INDONESIA_CENTRAL_TIME_ZONE" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_INDONESIA_EASTERN_TIME_ZONE" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230852, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TIME_ZONE", "context": "time_zone", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NZ_EXCEPT_CHATHAM_TIME_ZONE" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_CHATHAM_ISLANDS" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230853, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DATE", "context": "date", "data": {}, "type": "DATE_PICKER" }, { "node_id": 2131230854, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TIME", "context": "time", "data": {}, "type": "TIME_PICKER" }, { "node_id": 2131230855, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SLEEPTIMER", "context": "sleeptimer", "data": { "slider_data": { "min": 0, "max": 180, "step_size": 5 } }, "type": "SLIDER_NODE" } ] } }, { "node_id": 2131230856, "string_id": "org.droidtv.ui.strings.R.string.MAIN_RC_KEYBOARD", "context": "rc_keyboard", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MISC_QWERTY" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MISC_AZERTY" } ] }, "type": "LIST_NODE" } ] } }, { "node_id": 2131230754, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_GOOGLE_SETTINGS", "context": "android_settings", "data": {} }, { "node_id": 2131230755, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_UNIVERSAL_ACCESS", "context": "accessibility", "data": { "nodes": [ { "node_id": 2131230756, "string_id": "org.droidtv.ui.strings.R.string.MAIN_UNIVERSAL_ACCESS", "context": "universal_access", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230757, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HEARING_IMPAIRED", "context": "hearing_impaired", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230758, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_AUDIO_DESCRIPTION", "context": "audio_description_menu", "data": { "nodes": [ { "node_id": 2131230759, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AUDIO_DESCRIPTION", "context": "audio_description", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230760, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPEAKERS_OR_HEADPHONES", "context": "speakers_headphones", "data": { "enums": [ { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPEAKERS" }, { "enum_id": 2, "string_id": "org.droidtv.ui.strings.R.string.MAIN_HEADPHONE" }, { "enum_id": 3, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPEAKERS_HEADPHONE" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230761, "string_id": "org.droidtv.ui.strings.R.string.MAIN_MIXED_VOLUME", "context": "mixed_volume", "data": { "slider_data": { "min": -32, "max": 32, "step_size": 1 } }, "type": "SLIDER_NODE" }, { "node_id": 2131230762, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SEAMLESS_MIXING", "context": "audio_effects", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230763, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SPEECH_PREFERENCE", "context": "speech", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PREFERENCE_DESCRIPTIVE" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PREFERENCE_SUBTITLES" } ] }, "type": "LIST_NODE" } ] } }, { "node_id": 2131230764, "string_id": "org.droidtv.ui.strings.R.string.MAIN_DIALOGUE_ENHANCEMENT", "context": "dialogue_enhancement", "data": { "slider_data": { "min": 0, "max": 9, "step_size": 1 } }, "type": "SLIDER_NODE" } ] } }, { "node_id": 2131230823, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_CHILD_LOCK", "context": "child_lock", "data": { "nodes": [ { "node_id": 2131230824, "string_id": "org.droidtv.ui.strings.R.string.MAIN_PARENTAL_RATING", "context": "parental_rating", "data": { "enums": [] }, "type": "LIST_NODE" }, { "node_id": 2131230825, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_PARENTAL_RATING_STREAMING_CHANNELS", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900001, "type": "PARENT_NODE", "string_id": "Argentina", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900038, "string_id": "ATP", "context": "parental_rating", "data": { "enums": [] }, "type": "LIST_NODE" }, { "node_id": 900039, "string_id": "SAM 13", "context": "parental_rating", "data": { "enums": [] }, "type": "LIST_NODE" }, { "node_id": 900040, "string_id": "SAM 16", "context": "parental_rating", "data": { "enums": [] }, "type": "LIST_NODE" }, { "node_id": 900041, "string_id": "SAM 18", "context": "parental_rating", "data": { "enums": [] }, "type": "LIST_NODE" } ] } }, { "node_id": 900002, "type": "PARENT_NODE", "string_id": "Australien", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900042, "type": "PARENT_NODE", "string_id": "P", "context": "parental_rating", "data": {} }, { "node_id": 900043, "type": "PARENT_NODE", "string_id": "C", "context": "parental_rating", "data": {} }, { "node_id": 900044, "type": "PARENT_NODE", "string_id": "G", "context": "parental_rating", "data": {} }, { "node_id": 900045, "type": "PARENT_NODE", "string_id": "PG", "context": "parental_rating", "data": {} }, { "node_id": 900046, "type": "PARENT_NODE", "string_id": "M", "context": "parental_rating", "data": {} }, { "node_id": 900047, "type": "PARENT_NODE", "string_id": "MA", "context": "parental_rating", "data": {} }, { "node_id": 900048, "type": "PARENT_NODE", "string_id": "AV", "context": "parental_rating", "data": {} }, { "node_id": 900049, "type": "PARENT_NODE", "string_id": "R", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900003, "type": "PARENT_NODE", "string_id": "Brasilien", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900050, "string_id": "GA", "context": "parental_rating", "data": { "enums": [] }, "type": "LIST_NODE" }, { "node_id": 900051, "string_id": "PG-10", "context": "parental_rating", "data": { "enums": [] }, "type": "LIST_NODE" }, { "node_id": 900052, "string_id": "PG-12", "context": "parental_rating", "data": { "enums": [] }, "type": "LIST_NODE" }, { "node_id": 900053, "string_id": "PG-14", "context": "parental_rating", "data": { "enums": [] }, "type": "LIST_NODE" }, { "node_id": 900054, "string_id": "PG-16", "context": "parental_rating", "data": { "enums": [] }, "type": "LIST_NODE" }, { "node_id": 900055, "string_id": "PG-18", "context": "parental_rating", "data": { "enums": [] }, "type": "LIST_NODE" } ] } }, { "node_id": 900004, "type": "PARENT_NODE", "string_id": "Kanada", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900056, "type": "PARENT_NODE", "string_id": "E", "context": "parental_rating", "data": {} }, { "node_id": 900057, "type": "PARENT_NODE", "string_id": "C", "context": "parental_rating", "data": {} }, { "node_id": 900058, "type": "PARENT_NODE", "string_id": "C8", "context": "parental_rating", "data": {} }, { "node_id": 900059, "type": "PARENT_NODE", "string_id": "G", "context": "parental_rating", "data": {} }, { "node_id": 900060, "type": "PARENT_NODE", "string_id": "PG", "context": "parental_rating", "data": {} }, { "node_id": 900061, "type": "PARENT_NODE", "string_id": "14+", "context": "parental_rating", "data": {} }, { "node_id": 900062, "type": "PARENT_NODE", "string_id": "18+", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900005, "type": "PARENT_NODE", "string_id": "Kina", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900063, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900064, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900065, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900066, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900067, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900068, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900069, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900070, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900071, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900072, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900073, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900074, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900075, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900076, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900077, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900006, "type": "PARENT_NODE", "string_id": "Armenien", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900078, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900079, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900080, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900081, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900082, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900083, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900084, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900085, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900086, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900087, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900088, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900089, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900090, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900091, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900092, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900007, "type": "PARENT_NODE", "string_id": "Bulgarien", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900093, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900094, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900095, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900096, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900097, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900098, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900099, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900100, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900101, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900102, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900103, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900104, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900105, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900106, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900107, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900008, "type": "PARENT_NODE", "string_id": "Schweiz", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900108, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900109, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900110, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900111, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900112, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900113, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900114, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900115, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900116, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900117, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900118, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900119, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900120, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900121, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900122, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900009, "type": "PARENT_NODE", "string_id": "Tyskland", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900123, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900124, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900125, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900126, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900127, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900128, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900129, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900130, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900131, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900132, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900133, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900134, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900135, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900136, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900137, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900010, "type": "PARENT_NODE", "string_id": "Danmark", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900138, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900139, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900140, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900141, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900142, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900143, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900144, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900145, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900146, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900147, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900148, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900149, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900150, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900151, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900152, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900011, "type": "PARENT_NODE", "string_id": "Finland", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900153, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900154, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900155, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900156, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900157, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900158, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900159, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900160, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900161, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900162, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900163, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900164, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900165, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900166, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900167, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900012, "type": "PARENT_NODE", "string_id": "Grekland", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900168, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900169, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900170, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900171, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900172, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900173, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900174, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900175, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900176, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900177, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900178, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900179, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900180, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900181, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900182, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900013, "type": "PARENT_NODE", "string_id": "Ungern", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900183, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900184, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900185, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900186, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900187, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900188, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900189, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900190, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900191, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900192, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900193, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900194, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900195, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900196, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900197, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900014, "type": "PARENT_NODE", "string_id": "Indonesien", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900198, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900199, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900200, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900201, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900202, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900203, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900204, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900205, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900206, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900207, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900208, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900209, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900210, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900211, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900212, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900015, "type": "PARENT_NODE", "string_id": "Irland", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900213, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900214, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900215, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900216, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900217, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900218, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900219, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900220, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900221, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900222, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900223, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900224, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900225, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900226, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900227, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900016, "type": "PARENT_NODE", "string_id": "Israel", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900228, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900229, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900230, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900231, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900232, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900233, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900234, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900235, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900236, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900237, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900238, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900239, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900240, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900241, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900242, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900017, "type": "PARENT_NODE", "string_id": "Island", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900243, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900244, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900245, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900246, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900247, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900248, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900249, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900250, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900251, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900252, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900253, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900254, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900255, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900256, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900257, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900018, "type": "PARENT_NODE", "string_id": "Malaysia", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900258, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900259, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900260, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900261, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900262, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900263, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900264, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900265, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900266, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900267, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900268, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900269, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900270, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900271, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900272, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900019, "type": "PARENT_NODE", "string_id": "Nederl\u00e4nderna", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900273, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900274, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900275, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900276, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900277, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900278, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900279, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900280, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900281, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900282, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900283, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900284, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900285, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900286, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900287, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900020, "type": "PARENT_NODE", "string_id": "Nya Zeeland", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900288, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900289, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900290, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900291, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900292, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900293, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900294, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900295, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900296, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900297, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900298, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900299, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900300, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900301, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900302, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900021, "type": "PARENT_NODE", "string_id": "Polen", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900303, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900304, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900305, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900306, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900307, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900308, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900309, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900310, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900311, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900312, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900313, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900314, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900315, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900316, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900317, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900022, "type": "PARENT_NODE", "string_id": "Portugal", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900318, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900319, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900320, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900321, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900322, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900323, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900324, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900325, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900326, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900327, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900328, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900329, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900330, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900331, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900332, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900023, "type": "PARENT_NODE", "string_id": "Rum\u00e4nien", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900333, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900334, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900335, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900336, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900337, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900338, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900339, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900340, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900341, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900342, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900343, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900344, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900345, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900346, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900347, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900024, "type": "PARENT_NODE", "string_id": "Ryssland", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900348, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900349, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900350, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900351, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900352, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900353, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900354, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900355, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900356, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900357, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900358, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900359, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900360, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900361, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900362, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900025, "type": "PARENT_NODE", "string_id": "Serbien", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900363, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900364, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900365, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900366, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900367, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900368, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900369, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900370, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900371, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900372, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900373, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900374, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900375, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900376, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900377, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900026, "type": "PARENT_NODE", "string_id": "Slovenien", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900378, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900379, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900380, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900381, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900382, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900383, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900384, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900385, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900386, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900387, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900388, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900389, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900390, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900391, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900392, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900027, "type": "PARENT_NODE", "string_id": "Thailand", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900393, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900394, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900395, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900396, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900397, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900398, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900399, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900400, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900401, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900402, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900403, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900404, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900405, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900406, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900407, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900028, "type": "PARENT_NODE", "string_id": "Turkiet", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900408, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900409, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900410, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900411, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900412, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900413, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900414, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900415, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900416, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900417, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900418, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900419, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900420, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900421, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900422, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900029, "type": "PARENT_NODE", "string_id": "Taiwan", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900423, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900424, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900425, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900426, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900427, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900428, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900429, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900430, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900431, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900432, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900433, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900434, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900435, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900436, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900437, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900030, "type": "PARENT_NODE", "string_id": "Ukraina", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900438, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900439, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900440, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900441, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900442, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900443, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900444, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900445, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900446, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900447, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900448, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900449, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900450, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900451, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900452, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900031, "type": "PARENT_NODE", "string_id": "Spanien", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900453, "type": "PARENT_NODE", "string_id": "All", "context": "parental_rating", "data": {} }, { "node_id": 900454, "type": "PARENT_NODE", "string_id": "Children", "context": "parental_rating", "data": {} }, { "node_id": 900455, "type": "PARENT_NODE", "string_id": "X-Rated", "context": "parental_rating", "data": {} }, { "node_id": 900456, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900457, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900458, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900459, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900460, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900461, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900462, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900463, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900464, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900465, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900466, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900467, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900468, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900469, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900470, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900032, "type": "PARENT_NODE", "string_id": "Frankrike", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900471, "type": "PARENT_NODE", "string_id": "Universal", "context": "parental_rating", "data": {} }, { "node_id": 900472, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900473, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900474, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900475, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900476, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900477, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900478, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900479, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900480, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900481, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900482, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900483, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900484, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900485, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900486, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900033, "type": "PARENT_NODE", "string_id": "Japan", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900487, "type": "PARENT_NODE", "string_id": "4", "context": "parental_rating", "data": {} }, { "node_id": 900488, "type": "PARENT_NODE", "string_id": "5", "context": "parental_rating", "data": {} }, { "node_id": 900489, "type": "PARENT_NODE", "string_id": "6", "context": "parental_rating", "data": {} }, { "node_id": 900490, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900491, "type": "PARENT_NODE", "string_id": "8", "context": "parental_rating", "data": {} }, { "node_id": 900492, "type": "PARENT_NODE", "string_id": "9", "context": "parental_rating", "data": {} }, { "node_id": 900493, "type": "PARENT_NODE", "string_id": "10", "context": "parental_rating", "data": {} }, { "node_id": 900494, "type": "PARENT_NODE", "string_id": "11", "context": "parental_rating", "data": {} }, { "node_id": 900495, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900496, "type": "PARENT_NODE", "string_id": "13", "context": "parental_rating", "data": {} }, { "node_id": 900497, "type": "PARENT_NODE", "string_id": "14", "context": "parental_rating", "data": {} }, { "node_id": 900498, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900499, "type": "PARENT_NODE", "string_id": "16", "context": "parental_rating", "data": {} }, { "node_id": 900500, "type": "PARENT_NODE", "string_id": "17", "context": "parental_rating", "data": {} }, { "node_id": 900501, "type": "PARENT_NODE", "string_id": "18", "context": "parental_rating", "data": {} }, { "node_id": 900502, "type": "PARENT_NODE", "string_id": "19", "context": "parental_rating", "data": {} }, { "node_id": 900503, "type": "PARENT_NODE", "string_id": "20", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900034, "type": "PARENT_NODE", "string_id": "Sydkorea", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900504, "type": "PARENT_NODE", "string_id": "ALL", "context": "parental_rating", "data": {} }, { "node_id": 900505, "type": "PARENT_NODE", "string_id": "7", "context": "parental_rating", "data": {} }, { "node_id": 900506, "type": "PARENT_NODE", "string_id": "12", "context": "parental_rating", "data": {} }, { "node_id": 900507, "type": "PARENT_NODE", "string_id": "15", "context": "parental_rating", "data": {} }, { "node_id": 900508, "type": "PARENT_NODE", "string_id": "19", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900035, "type": "PARENT_NODE", "string_id": "Singapore", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900509, "type": "PARENT_NODE", "string_id": "G", "context": "parental_rating", "data": {} }, { "node_id": 900510, "type": "PARENT_NODE", "string_id": "PG", "context": "parental_rating", "data": {} }, { "node_id": 900511, "type": "PARENT_NODE", "string_id": "PG13", "context": "parental_rating", "data": {} }, { "node_id": 900512, "type": "PARENT_NODE", "string_id": "NC16", "context": "parental_rating", "data": {} }, { "node_id": 900513, "type": "PARENT_NODE", "string_id": "M18", "context": "parental_rating", "data": {} }, { "node_id": 900514, "type": "PARENT_NODE", "string_id": "R21", "context": "parental_rating", "data": {} } ] } }, { "node_id": 900036, "type": "PARENT_NODE", "string_id": "USA", "context": "parental_rating", "data": { "nodes": [ { "node_id": 900515, "type": "PARENT_NODE", "string_id": "G", "context": "parental_rating", "data": {} }, { "node_id": 900516, "type": "PARENT_NODE", "string_id": "PG", "context": "parental_rating", "data": {} }, { "node_id": 900517, "type": "PARENT_NODE", "string_id": "PG-13", "context": "parental_rating", "data": {} }, { "node_id": 900518, "type": "PARENT_NODE", "string_id": "R", "context": "parental_rating", "data": {} }, { "node_id": 900519, "type": "PARENT_NODE", "string_id": "NC-17", "context": "parental_rating", "data": {} } ] } } ] } }, { "node_id": 2131230826, "string_id": "org.droidtv.ui.strings.R.string.MAIN_APP_LOCKING", "context": "app_locking", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230827, "type": "PARENT_NODE", "context": "change_code", "data": {} }, { "node_id": 2131230828, "type": "PARENT_NODE", "context": "CAM_change_code", "data": {} } ] } }, { "node_id": 2131230724, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_WIRELESS_AND_NETWORKS", "context": "wireless_networks", "data": { "nodes": [ { "node_id": 2131230725, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_WIRED_OR_WIFI", "context": "network", "data": { "nodes": [ { "node_id": 2131230726, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_CONNECT_TO_NETWORK", "context": "connect_to_network", "data": {} }, { "node_id": 2131230727, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_VIEW_NETWORK_SETTINGS", "context": "view_network_settings", "data": {} }, { "node_id": 2131230728, "string_id": "org.droidtv.ui.strings.R.string.MAIN_NETWORK_MODE", "context": "network_configuration", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MISC_DHCP" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_STATIC_IP" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230729, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_IP_CONFIGURATION", "context": "ip_configuration", "data": { "nodes": [ { "node_id": 2131230730, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_PANEL_IPV4_ADDRESS", "context": "ip_address", "data": {} }, { "node_id": 2131230731, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_PANEL_IPV4_NETMASK", "context": "netmask", "data": {} }, { "node_id": 2131230732, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_PANEL_IPV4_GATEWAY", "context": "gateway", "data": {} }, { "node_id": 2131230733, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_PANEL_IPV4_DNS_1", "context": "dns_1", "data": {} }, { "node_id": 2131230734, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_PANEL_IPV4_DNS_2", "context": "dns_2", "data": {} } ] } }, { "node_id": 2131230735, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SWITCH_ON_WITH_WIFI_WOWLAN", "context": "switch_on_with_network", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230736, "string_id": "org.droidtv.ui.strings.R.string.MAIN_SWITCH_ON_WITH_CHROMECAST", "context": "switch_on_with_network", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230737, "string_id": "org.droidtv.ui.strings.R.string.MAIN_RENDERER_ACCESS", "context": "digital_media_renderer", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230738, "string_id": "org.droidtv.ui.strings.R.string.MAIN_WIFI_ON_OFF", "context": "wifi_on_off", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230739, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_RECORDING_ON_THE_GO", "context": "myremote_recording", "data": {} }, { "node_id": 2131230740, "string_id": "org.droidtv.ui.strings.R.string.MAIN_TV_NAME", "context": "tv_network_name", "data": {}, "type": "TEXT_ENTRY" }, { "node_id": 2131230741, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_CLEAR_APPS_MEMORY", "context": "clear_internet_memory", "data": {} } ] } }, { "node_id": 2131230742, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_BLUETOOTH", "context": "bluetooth", "data": { "nodes": [ { "node_id": 2131230743, "string_id": "org.droidtv.ui.strings.R.string.MAIN_BT_ON_OFF", "context": "bluetooth_on_off", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230744, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_SEARCH_BT_DEVICE", "context": "search_for_bt_devices", "data": {} }, { "node_id": 2131230745, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_REMOVE_BT_DEVICE", "context": "remove_bt_device", "data": {} } ] } }, { "node_id": 2131230746, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_REMOTE_CONTROL", "context": "bt_remote_control", "data": { "nodes": [ { "node_id": 2131230747, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_PAIR_RC", "context": "pair_remote_control", "data": {} }, { "node_id": 2131230748, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_REMOTE_CONTROL_INFO", "context": "check_software_version", "data": {} }, { "node_id": 2131230749, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_CH_UPDATE_RC_SOFTWARE", "context": "update_rc_software", "data": {} } ] } }, { "node_id": 2131230750, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_CUBE_NAME", "context": "cube_name", "data": { "nodes": [ { "node_id": 2131230751, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_PAIR_CUBE_NAME", "context": "pair_voice_cube", "data": {} }, { "node_id": 2131230748, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_CURRENT_SOFTWARE_INFO", "context": "check_software_version", "data": {} }, { "node_id": 2131230752, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_UPDATE_CUBE_NAME_SOFTWARE", "context": "update_rc_software", "data": {} } ] } } ] } }, { "node_id": 2131230816, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_CHANNELS", "context": "channels", "data": { "nodes": [ { "node_id": 2131230817, "type": "PARENT_NODE", "context": "channel_installation", "data": {} }, { "node_id": 2131230818, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_SATELLITE_INSTALLATION", "context": "satellite_installation", "data": {} }, { "node_id": 2131230819, "type": "PARENT_NODE", "context": "channel_list_copy", "data": { "nodes": [ { "node_id": 2131230820, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_COPY_TO_USB", "context": "channels_copy_to_usb", "data": {} }, { "node_id": 2131230821, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_COPY_TO_TV", "context": "channels_copy_to_tv", "data": {} }, { "node_id": 2131230822, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_CURRENT_VERSION", "context": "channels_current_version", "data": {} } ] } } ] } }, { "node_id": 2131230977, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_UPDATE_SOFTWARE", "context": "update_software", "data": { "nodes": [ { "node_id": 2131230978, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_SEARCH_FOR_UPDATES", "context": "search_for_update", "data": { "nodes": [ { "node_id": 2131230979, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_INTERNET_RECOMMENDED", "context": "internet_updates", "data": {} }, { "node_id": 2131230980, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_USB", "context": "local_updates", "data": { "nodes": [ { "node_id": 2131230981, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_LOCAL_UPDATES", "context": "local_updates", "data": {} }, { "node_id": 2131230982, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_IDENTIFY_TV", "context": "local_updates", "data": {} } ] } }, { "node_id": 2131230983, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_LOOK_FOR_OAD_UPDATES", "context": "look_for_oad_updates", "data": {} } ] } }, { "node_id": 2131230984, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_CURRENT_SOFTWARE_INFO", "context": "current_software_info", "data": {} }, { "node_id": 2131230985, "string_id": "org.droidtv.ui.strings.R.string.MAIN_AUTOMATIC_SOFTWARE_UPDATE", "context": "automatic_update", "data": { "enums": [ { "enum_id": 0, "string_id": "org.droidtv.ui.strings.R.string.MAIN_OFF" }, { "enum_id": 1, "string_id": "org.droidtv.ui.strings.R.string.MAIN_ON" } ] }, "type": "LIST_NODE" }, { "node_id": 2131230986, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_ANNOUNCEMENT", "context": "announcement", "data": {} }, { "node_id": 2131230987, "type": "PARENT_NODE", "string_id": "org.droidtv.ui.strings.R.string.MAIN_VIEW_SOFTWARE_UPDATE_HISTORY", "context": "view_software_update_history", "data": {} } ] } } ] } } } MENUITEMS_SETTINGS_UPDATE: MenuItemsSettingsUpdate = { "values": [ { "value": { "Nodeid": 2131230778, "data": { "value": True } } } ] } MENUITEMS_SETTINGS_CURRENT: MenuItemsSettingsCurrent = { "values": [ { "value": { "Nodeid": 2131230778, "Controllable": False, "Available": True, "string_id": "Inschakelen", "data": { "value": True } } } ], "version": 0 } MENUITEMS_SETTINGS_CURRENT_POST: MenuItemsSettingsCurrentPost = { "nodes": [ { "nodeid": 420 } ] } RECORDINGS_LIST: RecordingsListed = { "version": "253.91", "recordings": [ { "RecordingId": 36, "RecordingType": "RECORDING_ONGOING", "IsIpEpgRec": False, "ccid": 2091, "StartTime": 1676833531, "Duration": 569, "MarginStart": 0, "MarginEnd": 0, "EventId": 47369, "EITVersion": 0, "RetentionInfo": 0, "EventInfo": "Eisige Welten II - Planet der Extreme\nDeutschland 2023\nIm Fokus dieser Folge stehen die besonderen Anpassungsstrategien der Bewohner an die Veränderungen in den klimatisch herausfordernden Lebensräumen.\n\nDie Reise beginnt auf dem gefrorenen Kontinent der Antarktis im äußersten Süden, dem lebensfeindlichsten Ort der Erde. Nachdem sie im Winter auf dem Eis aufgewachsen sind, werden die Kaiserpinguinküken im Frühjahr von ihren Eltern verlassen.\nHD-Produktion", "EventExtendedInfo": "", "EventGenre": "8", "RecName": "Terra X", "SeriesID": "None", "SeasonNo": 0, "EpisodeNo": 0, "EpisodeCount": 72300, "ProgramNumber": 11110, "EventRating": 0, "hasDot": True, "isFTARecording": False, "LastPinChangedTime": 0, "Version": 344, "HasCicamPin": False, "HasLicenseFile": False, "Size": 0, "ResumeInfo": 0, "IsPartial": False, "AutoMarginStart": 0, "AutoMarginEnd": 0, "ServerRecordingId": -1, "ActualStartTime": 1676833531, "ProgramDuration": 0, "IsRadio": False, "EITSource": "EIT_SOURCE_PF", "RecError": "REC_ERROR_NONE" }, { "RecordingId": 35, "RecordingType": "RECORDING_NEW", "IsIpEpgRec": False, "ccid": 2091, "StartTime": 1676832212, "Duration": 22, "MarginStart": 0, "MarginEnd": 0, "EventId": 47369, "EITVersion": 0, "RetentionInfo": -1, "EventInfo": "Eisige Welten II - Planet der Extreme\nDeutschland 2023\nIm Fokus dieser Folge stehen die besonderen Anpassungsstrategien der Bewohner an die Veränderungen in den klimatisch herausfordernden Lebensräumen.\n\nDie Reise beginnt auf dem gefrorenen Kontinent der Antarktis im äußersten Süden, dem lebensfeindlichsten Ort der Erde. Nachdem sie im Winter auf dem Eis aufgewachsen sind, werden die Kaiserpinguinküken im Frühjahr von ihren Eltern verlassen.\nHD-Produktion", "EventExtendedInfo": "", "EventGenre": "8", "RecName": "Terra X", "SeriesID": "None", "SeasonNo": 0, "EpisodeNo": 0, "EpisodeCount": 70980, "ProgramNumber": 11110, "EventRating": 0, "hasDot": True, "isFTARecording": False, "LastPinChangedTime": 0, "Version": 339, "HasCicamPin": False, "HasLicenseFile": False, "Size": 0, "ResumeInfo": 0, "IsPartial": False, "AutoMarginStart": 0, "AutoMarginEnd": 0, "ServerRecordingId": -1, "ActualStartTime": 1676832212, "ProgramDuration": 0, "IsRadio": False, "EITSource": "EIT_SOURCE_PF", "RecError": "REC_ERROR_NONE" }, { "RecordingId": 34, "RecordingType": "RECORDING_PARTIALLY_VIEWED", "IsIpEpgRec": False, "ccid": 2091, "StartTime": 1676677580, "Duration": 484, "MarginStart": 0, "MarginEnd": 0, "EventId": -1, "EITVersion": 0, "RetentionInfo": -1, "EventInfo": "\n\nAlpine Ski-WM: Parallel-Event, Übertragung aus Méribel/Frankreich\n\n14:10: Biathlon-WM (AD): 20 km Einzel Männer, Übertragung aus Oberhof\nHD-Produktion", "EventExtendedInfo": "", "EventGenre": "4", "RecName": "ZDF HD 2023-02-18 00:46", "SeriesID": "None", "SeasonNo": 0, "EpisodeNo": 0, "EpisodeCount": 2760, "ProgramNumber": 11110, "EventRating": 0, "hasDot": True, "isFTARecording": False, "LastPinChangedTime": 0, "Version": 328, "HasCicamPin": False, "HasLicenseFile": False, "Size": 0, "ResumeInfo": 56, "IsPartial": False, "AutoMarginStart": 0, "AutoMarginEnd": 0, "ServerRecordingId": -1, "ActualStartTime": 1676677581, "ProgramDuration": 0, "IsRadio": False, "EITSource": "EIT_SOURCE_PF", "RecError": "REC_ERROR_NONE" } ] }ha-philipsjs-3.3.3/haphilipsjs/dummy/000077500000000000000000000000001471516442100175715ustar00rootroot00000000000000ha-philipsjs-3.3.3/haphilipsjs/dummy/__init__.py000066400000000000000000000000221471516442100216740ustar00rootroot00000000000000"""Dummy TV's.""" ha-philipsjs-3.3.3/haphilipsjs/dummy/v1.py000066400000000000000000000030641471516442100204740ustar00rootroot00000000000000from aiohttp import web from aiohttp.web_request import Request from aiohttp.web_response import json_response from haphilipsjs.data.v1 import ( AMBILIGHT, CHANNELLISTS, CHANNELS_CURRENT, CHANNELS, SOURCES_CURRENT, SOURCES, SYSTEM_55PFL6007T, VOLUME, ) async def put_ambilight_cached(req): print("cached") return web.json_response({}) async def put_ambilight_mode(req): print("mode") return web.json_response({}) def get_data(data): async def get(req: Request): return web.json_response(data) return get app = web.Application() app.add_routes( [ web.get("/1/system", get_data(SYSTEM_55PFL6007T)), web.get("/1/sources", get_data(SOURCES)), web.get("/1/sources/current", get_data(SOURCES_CURRENT)), web.get("/1/channels", get_data(CHANNELS)), web.get("/1/channels/current", get_data(CHANNELS_CURRENT)), web.get("/1/audio/volume", get_data(VOLUME)), web.get("/1/channellists", get_data(CHANNELLISTS)), web.get("/1/ambilight/mode", get_data(AMBILIGHT["mode"])), web.post("/1/ambilight/mode", put_ambilight_mode), web.get("/1/ambilight/topology", get_data(AMBILIGHT["topology"])), web.get("/1/ambilight/measured", get_data(AMBILIGHT["measured"])), web.get("/1/ambilight/processed", get_data(AMBILIGHT["processed"])), web.get("/1/ambilight/cached", get_data(AMBILIGHT["cached"])), web.post("/1/ambilight/cached", put_ambilight_cached), ] ) if __name__ == "__main__": web.run_app(app, port=1925) ha-philipsjs-3.3.3/haphilipsjs/typing.py000066400000000000000000000222551471516442100203300ustar00rootroot00000000000000from typing import Dict, List, Literal, NamedTuple, Optional, Type, TypedDict, Union class ActivitiesChannelType(TypedDict, total=False): ccid: int name: str preset: str onid: str tsid: str sid: str serviceType: str type: str logoVersion: str class ActivitiesChannelListType(TypedDict, total=False): id: str version: Union[int, str] class ActivitesTVType(TypedDict, total=False): channel: ActivitiesChannelType channelList: ActivitiesChannelListType class ChannelsCurrentType(TypedDict): id: str class ComponentType(TypedDict): packageName: str className: str class ApplicationIntentType(TypedDict, total=False): extras: Dict component: ComponentType action: str class ApplicationType(TypedDict, total=False): label: str intent: ApplicationIntentType order: int id: str type: str class ApplicationsType(TypedDict): version: int applications: List[ApplicationType] class FavoriteChannelType(TypedDict, total=False): ccid: int preset: str class FavoriteListType(TypedDict, total=False): id: str version: Union[int, str] listType: str medium: str name: str channels: List[FavoriteChannelType] class ChannelType(TypedDict, total=False): ccid: Union[int, str] preset: str name: str onid: int tsid: int sid: int serviceType: str type: str logoVersion: Union[int, str] class ChannelListType(TypedDict, total=False): id: str version: Union[int, str] listType: str medium: str operator: str installCountry: str Channel: List[ChannelType] ChannelsType = Dict[str, ChannelType] class ChannelDbTvListBase(TypedDict): id: str version: Union[int, str] listType: str class ChannelDbTvList(ChannelDbTvListBase, total=False): medium: str active: bool virtual: bool modifiable: bool class ChannelDbTvListFavorite(ChannelDbTvListBase, total=False): medium: str active: bool virtual: bool modifiable: bool parentId: str name: str class ChannelDbTv(TypedDict, total=False): channelLists: List[ChannelDbTvList] favoriteLists: List[ChannelDbTvListFavorite] class JsonFeaturesType(TypedDict, total=False): editfavorites: List[str] recordings: List[str] ambilight: List[str] menuitems: List[str] textentry: List[str] applications: List[str] pointer: List[str] inputkey: List[str] activities: List[str] channels: List[str] mappings: List[str] class SystemFeaturesType(TypedDict, total=False): tvtype: str content: List[str] tvsearch: str pairing_type: str secured_transport: str companion_screen: Union[str, bool] # stored here on saphi os_type: str class SystemFeaturingType(TypedDict): jsonfeatures: JsonFeaturesType systemfeatures: SystemFeaturesType class SystemType(TypedDict, total=False): menulanguage: str name: str country: str serialnumber: str serialnumber_encrypted: str softwareversion: str softwareversion_encrypted: str model: str model_encrypted: str deviceid_encrypted: str nettvversion: str epgsource: str api_version: Dict featuring: SystemFeaturingType notifyChange: str # here on android systems os_type: str class SourceType(TypedDict, total=False): name: str SourcesType = Dict[str, SourceType] class SourceCurrentType(TypedDict): id: str class ContextType(TypedDict, total=False): data: str level1: str level2: str level3: str # in some older context these exists activity: str menu: str Recording: str AmbilightPixelType = Dict[Literal["r", "g", "b"], int] AmbilightSideType = Dict[str, AmbilightPixelType] AmbilightLayerType = Dict[Literal["left", "top", "right", "bottom"], AmbilightSideType] AmbilightLayersType = Dict[str, AmbilightLayerType] class AmbilightSupportedStyleType(TypedDict, total=False): styleName: str algorithms: List[str] maxTuning: int maxSpeed: int # Extension to get the non expert modes too menuSettings: List[str] class AmbilightSupportedStylesType(TypedDict): supportedStyles: List[AmbilightSupportedStyleType] class AmbilightColorType(TypedDict): hue: int saturation: int brightness: int class AmbilightAudioSettingsType(TypedDict): color: AmbilightColorType colorDelta: AmbilightColorType tuning: int algorithm: str class AmbilightColorSettingsType(TypedDict): color: AmbilightColorType colorDelta: AmbilightColorType speed: int algorithm: str class AmbilightCurrentConfiguration(TypedDict, total=False): styleName: str isExpert: bool menuSetting: str stringValue: str audioSettings: AmbilightAudioSettingsType colorSettings: AmbilightColorSettingsType class RecordingsListedType(TypedDict, total=False): RecordingId: int ServerRecordingId: int RecordingType: str #RECORDING_ONGOING, RECORDING_SCHEDULED, RECORDING_NEW, RECORDING_PARTIALLY_VIEWED, RECORDING_VIEWED RecName: str EventId: int EventInfo: str EventExtendedInfo: str EventGenre: str EventRating: int SeriesID: str StartTime: int ActualStartTime: int Duration: int MarginStart: int MarginEnd: int AutoMarginStart: int AutoMarginEnd: int RetentionInfo: int SeasonNo: int EpisodeNo: int EpisodeCount: int ProgramNumber: int ProgramDuration: int ResumeInfo: int LastPinChangedTime: int HasCicamPin: bool hasDot: bool HasLicenseFile: bool IsRadio: bool IsPartial: bool IsIpEpgRec: bool isFTARecording: bool EITVersion: int EITSource: str #EIT_SOURCE_PF, ... RecError: str #REC_ERROR_NONE, ... Version: int Size: int ccid: int class RecordingsListed(TypedDict): version: str recordings: List[RecordingsListedType] class MenuItemsSettingsNodeDataSliderData(TypedDict): min: int max: int step_size: int class MenuItemsSettingsNodeDataSlider(TypedDict): slider_id: str slider_data: MenuItemsSettingsNodeDataSliderData class MenuItemsSettingsNodeDataEnumRequired(TypedDict): enum_id: int string_id: str class MenuItemsSettingsNodeDataEnum(MenuItemsSettingsNodeDataEnumRequired, total=False): icon: Optional[str] class MenuItemsSettingsNodeData(TypedDict, total=False): nodes: List["MenuItemsSettingsNode"] # PARENT_NODE enums: List[MenuItemsSettingsNodeDataEnum] # LIST_NODE slider_data: MenuItemsSettingsNodeDataSliderData # SLIDER_NODE sliders: List[MenuItemsSettingsNodeDataSlider] # MULTIPLE_SLIDERS colors: List[int] # WALL_COLOR_NODE class MenuItemsSettingsNodeRequired(TypedDict): node_id: int type: str data: MenuItemsSettingsNodeData class MenuItemsSettingsNode(MenuItemsSettingsNodeRequired, total=False): string_id: str icon: Optional[str] context: str class MenuItemsSettingsEntry(NamedTuple): node: MenuItemsSettingsNode parent: Optional[int] class MenuItemsSettingsStructure(TypedDict, total=False): node: MenuItemsSettingsNode class MenuItemsSettingsValueInt(TypedDict): value: int class MenuItemsSettingsValueBool(TypedDict): value: bool class MenuItemsSettingsValueEnumEntry(TypedDict): enum_id: int controllable: bool available: bool string_id: str class MenuItemsSettingsValueEnumRequired(TypedDict): select_item: Optional[int] class MenuItemsSettingsValueEnum(MenuItemsSettingsValueEnumRequired, total=False): enum_values: List[MenuItemsSettingsValueEnumEntry] class MenuItemsSettingsValueNode(TypedDict): activenode_id: int class MenuItemsSettingsValueSlider(TypedDict): slider_id: int value: int class MenuItemsSettingsValueSliders(TypedDict): values: List[MenuItemsSettingsValueSlider] MenuItemsSettingsValueData = Union[ MenuItemsSettingsValueInt, MenuItemsSettingsValueBool, MenuItemsSettingsValueEnum, MenuItemsSettingsValueNode, MenuItemsSettingsValueSliders ] class MenuItemsSettingsCurrentValueValue(TypedDict): Nodeid: int Controllable: bool Available: bool string_id: str data: MenuItemsSettingsValueData class MenuItemsSettingsCurrentValue(TypedDict): value: MenuItemsSettingsCurrentValueValue class MenuItemsSettingsCurrent(TypedDict): values: List[MenuItemsSettingsCurrentValue] version: int class MenuItemsSettingsCurrentPostNode(TypedDict): nodeid: int class MenuItemsSettingsCurrentPost(TypedDict): nodes: List[MenuItemsSettingsCurrentPostNode] class MenuItemsSettingsUpdateValueValue(TypedDict): Nodeid: int data: MenuItemsSettingsValueData class MenuItemsSettingsUpdateValue(TypedDict): value: MenuItemsSettingsUpdateValueValue class MenuItemsSettingsUpdate(TypedDict): values: List[MenuItemsSettingsUpdateValue] class StringsTranslation(TypedDict): string_id: str string_translation: Optional[str] class Strings(TypedDict): translations: List[StringsTranslation] class StringId(TypedDict): string_id: str class StringLocale(TypedDict): language: str country: str variant: str class StringsRequest(TypedDict): strings: List[StringId] locale: StringLocale ha-philipsjs-3.3.3/setup.cfg000066400000000000000000000001351471516442100157400ustar00rootroot00000000000000[metadata] description-file = README.md [tool:pytest] testpaths = tests asyncio_mode = auto ha-philipsjs-3.3.3/setup.py000066400000000000000000000030161471516442100156320ustar00rootroot00000000000000import os from setuptools import setup, find_packages def readme(): with open('README.md') as f: return f.read() PACKAGE_NAME = 'ha-philipsjs' HERE = os.path.abspath(os.path.dirname(__file__)) VERSION = '3.2.3' PACKAGES = find_packages(exclude=['tests', 'tests.*', 'dist', 'ccu', 'build']) REQUIRES = [ "cryptography", "httpx>=0.22.0", ] setup( name=PACKAGE_NAME, version=VERSION, license='MIT License', url='https://github.com/danielperna84/ha-philipsjs', download_url='https://github.com/danielperna84/ha-philipsjs/tarball/'+VERSION, author='Daniel Perna', author_email='danielperna84@gmail.com', description='jointSPACE API for Home-Assistant', long_description=readme(), long_description_content_type='text/markdown', packages=PACKAGES, include_package_data=True, zip_safe=False, platforms='any', install_requires=REQUIRES, keywords=['jointSPACE'], classifiers=[ 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3.8' ], extras_require={ 'tests': [ 'pytest>3.6.4', 'pytest-cov<2.6', 'pytest-asyncio>=0.20.2', 'coveralls', 'pytest-mock', 'respx>=0.17.0', ] }, python_requires='>=3.8' ) ha-philipsjs-3.3.3/tests/000077500000000000000000000000001471516442100152625ustar00rootroot00000000000000ha-philipsjs-3.3.3/tests/test_auth.py000066400000000000000000000006101471516442100176310ustar00rootroot00000000000000 from haphilipsjs import AUTH_SHARED_KEY, hmac_signature import pytest @pytest.mark.parametrize( ("timestamp", "auth_key", "pin", "auth_signature"), [ (1128, "um27pghe7wyo0ysu", "0667", "rdPzC+bdWQHQMcwe3X+LddeG/tA=") ] ) async def test_hmac(timestamp, auth_key, pin, auth_signature): assert hmac_signature(AUTH_SHARED_KEY, str(timestamp), pin) == auth_signatureha-philipsjs-3.3.3/tests/test_v1.py000066400000000000000000000236361471516442100172330ustar00rootroot00000000000000import httpx import haphilipsjs import pytest import respx import json from typing import NamedTuple, cast, Dict from haphilipsjs.data.v1 import ( AMBILIGHT, CHANNELLISTS, CHANNELS_CURRENT, CHANNELS, CONTEXT, POWERSTATE, SOURCES_CURRENT, SOURCES, SYSTEM_DECRYPTED, SYSTEM_ENCRYPTED, VOLUME, ) class Param(NamedTuple): type: str base: str @pytest.fixture(params=["55PFL6007T", "47PFH6309", "65PUS6121"], name="param") async def param_fixture(request): return Param(request.param, "http://127.0.0.1:1925/1") @pytest.fixture async def client_mock(param: Param): with respx.mock: client = haphilipsjs.PhilipsTV("127.0.0.1", api_version=1) respx.get(f"{param.base}/system").respond( json=cast(Dict, SYSTEM_ENCRYPTED[param.type]) ) respx.get(f"{param.base}/sources").respond(json=SOURCES) respx.get(f"{param.base}/sources/current").respond( json=cast(Dict, SOURCES_CURRENT) ) respx.get(f"{param.base}/channels").respond(json=CHANNELS) respx.get(f"{param.base}/channels/current").respond( json=cast(Dict, CHANNELS_CURRENT) ) respx.get(f"{param.base}/audio/volume").respond(json=VOLUME) respx.get(f"{param.base}/channellists").respond(json=CHANNELLISTS) respx.get(f"{param.base}/ambilight/mode").respond(json=AMBILIGHT["mode"]) respx.get(f"{param.base}/ambilight/topology").respond( json=AMBILIGHT["topology"] ) respx.get(f"{param.base}/ambilight/measured").respond( json=AMBILIGHT["measured"] ) respx.get(f"{param.base}/ambilight/processed").respond( json=AMBILIGHT["processed"] ) respx.get(f"{param.base}/ambilight/cached").respond(json=AMBILIGHT["cached"]) if param.type in POWERSTATE: respx.get(f"{param.base}/powerstate").respond(json=POWERSTATE[param.type]) else: respx.get(f"{param.base}/powerstate").respond(404) if param.type in CONTEXT: respx.get(f"{param.base}/context").respond(json=CONTEXT[param.type]) else: respx.get(f"{param.base}/context").respond(404) yield client await client.aclose() async def test_basic_data(client_mock: haphilipsjs.PhilipsTV, param: Param): """Test for basic data""" await client_mock.update() assert client_mock.on == True assert client_mock.system == SYSTEM_DECRYPTED[param.type] assert client_mock.sources == SOURCES assert client_mock.channels == CHANNELS assert client_mock.channels_current == [ { **channel, "ccid": key } for key, channel in CHANNELS.items() ] assert client_mock.ambilight_current_configuration is None assert client_mock.ambilight_styles == {} assert client_mock.powerstate == POWERSTATE.get(param.type, {}).get("powerstate") assert client_mock.context == CONTEXT.get(param.type) async def test_current_source_none(client_mock, param: Param): await client_mock.update() assert client_mock.source_id == "hdmi1" respx.get(f"{param.base}/sources/current").respond(json={}) await client_mock.update() assert client_mock.source_id == None async def test_current_channel_none(client_mock, param: Param): await client_mock.update() assert client_mock.channel_id == "fingerprint-1" respx.get(f"{param.base}/channels/current").respond(json={}) await client_mock.update() assert client_mock.channel_id == None async def test_current_channel_with_channellist_prefix(client_mock, param: Param): respx.get(f"{param.base}/channels/current").respond(json={"id": "0_fingerprint-1"}) await client_mock.update() assert client_mock.channel_id == "fingerprint-1" async def test_get_source_name(client_mock): """Verify that we can translate source id to name""" await client_mock.update() assert await client_mock.getSourceName("ypbpr") == "Y Pb Pr" assert await client_mock.getSourceName("invalid_name") == None async def test_get_channel_name(client_mock): """Verify that we can translate channel id to name""" await client_mock.update() assert await client_mock.getChannelName("fingerprint-3") == "Irdeto scrambled" assert await client_mock.getChannelName("invalid_name") == None async def test_timeout(client_mock, param: Param): """Test that connect timeouts trigger tv to be considered off""" await client_mock.update() assert client_mock.on == True respx.get(f"{param.base}/sources/current").mock(side_effect=httpx.ConnectTimeout) await client_mock.update() assert client_mock.on == False respx.get(f"{param.base}/sources/current").respond(json=cast(Dict, SOURCES_CURRENT)) await client_mock.update() assert client_mock.on == True async def test_volume(client_mock, param: Param): respx.get(f"{param.base}/audio/volume").respond( json={"muted": True, "current": 30, "min": 0, "max": 60} ) await client_mock.update() assert client_mock.volume == 0.5 assert client_mock.muted == True respx.get(f"{param.base}/audio/volume").respond( json={"muted": False, "current": 60, "min": 0, "max": 60} ) await client_mock.update() assert client_mock.volume == 1.0 assert client_mock.muted == False respx.get(f"{param.base}/audio/volume").respond( json={"muted": False, "current": 0, "min": 0, "max": 60} ) await client_mock.update() assert client_mock.volume == 0.0 assert client_mock.muted == False async def test_set_volume(client_mock, param: Param): await client_mock.update() respx.post(f"{param.base}/audio/volume").respond(json={}) await client_mock.setVolume(0.5) assert respx.calls[-1].request.url == f"{param.base}/audio/volume" assert json.loads(respx.calls[-1].request.content) == { "muted": False, "current": 30, } await client_mock.setVolume(1.0, True) assert json.loads(respx.calls[-1].request.content) == { "muted": True, "current": 60, } await client_mock.setVolume(0.0, True) assert json.loads(respx.calls[-1].request.content) == { "muted": True, "current": 0, } await client_mock.setVolume(None, True) assert json.loads(respx.calls[-1].request.content) == { "muted": True, } async def test_set_source(client_mock, param: Param): respx.post(f"{param.base}/sources/current").respond(json={}) await client_mock.setSource("hdmi2") assert respx.calls[-1].request.url == f"{param.base}/sources/current" assert json.loads(respx.calls[-1].request.content) == { "id": "hdmi2", } async def test_set_channel(client_mock, param: Param): respx.post(f"{param.base}/channels/current").respond(json={}) await client_mock.setChannel("fingerprint-2") assert respx.calls[-1].request.url == f"{param.base}/channels/current" assert json.loads(respx.calls[-1].request.content) == { "id": "fingerprint-2", } async def test_send_key(client_mock, param: Param): respx.post(f"{param.base}/input/key").respond(json={}) await client_mock.sendKey("Standby") assert respx.calls[-1].request.url == f"{param.base}/input/key" assert json.loads(respx.calls[-1].request.content) == { "key": "Standby", } async def test_send_key_off(client_mock, param: Param): respx.post(f"{param.base}/input/key").mock(side_effect=httpx.ConnectTimeout) with pytest.raises(haphilipsjs.ConnectionFailure): await client_mock.sendKey("Standby") async def test_ambilight_mode(client_mock, param: Param): await client_mock.getSystem() respx.post(f"{param.base}/ambilight/mode").respond(json={}) await client_mock.setAmbilightMode("internal") assert respx.calls[-1].request.url == f"{param.base}/ambilight/mode" assert json.loads(respx.calls[-1].request.content) == { "current": "internal", } assert await client_mock.getAmbilightMode() assert client_mock.ambilight_mode == "internal" assert await client_mock.setAmbilightMode("manual") assert client_mock.ambilight_mode == "manual" async def test_ambilight_power(client_mock: haphilipsjs.PhilipsTV, param: Param): respx.post(f"{param.base}/ambilight/cached").respond(json={}) respx.post(f"{param.base}/ambilight/mode").respond(json={}) assert client_mock.ambilight_power == None await client_mock.update() await client_mock.getAmbilightPower() await client_mock.getAmbilightCached() await client_mock.getAmbilightMode() assert client_mock.ambilight_power == "On" assert await client_mock.setAmbilightPower("Off") assert respx.calls[-2].request.url == f"{param.base}/ambilight/cached" assert json.loads(respx.calls[-2].request.content) == {"r": 0, "g": 0, "b": 0} assert respx.calls[-1].request.url == f"{param.base}/ambilight/cached" assert await client_mock.setAmbilightPower("On") assert respx.calls[-1].request.url == f"{param.base}/ambilight/mode" assert json.loads(respx.calls[-1].request.content) == {"current": "internal"} async def test_ambilight_topology(client_mock): assert await client_mock.getAmbilightTopology() == AMBILIGHT["topology"] async def test_ambilight_measured(client_mock): assert await client_mock.getAmbilightMeasured() == AMBILIGHT["measured"] async def test_ambilight_processed(client_mock): assert await client_mock.getAmbilightProcessed() == AMBILIGHT["processed"] async def test_ambilight_cached(client_mock, param: Param): assert await client_mock.getAmbilightCached() == AMBILIGHT["cached"] respx.post(f"{param.base}/ambilight/cached").respond(json={}) data = {"r": 100, "g": 210, "b": 30} await client_mock.setAmbilightCached(data) assert respx.calls[-2].request.url == f"{param.base}/ambilight/cached" assert respx.calls[-1].request.url == f"{param.base}/ambilight/cached" assert json.loads(respx.calls[-2].request.content) == data ha-philipsjs-3.3.3/tests/test_v6.py000066400000000000000000000530641471516442100172360ustar00rootroot00000000000000import haphilipsjs import pytest import httpx import respx import json from unittest.mock import patch from typing import Dict, NamedTuple, cast from haphilipsjs.data.v6 import ( AMBILIGHT, ACTIVITIES_TV, AMBILIGHT_CURRENT_CONFIGURATION, AMBILIGHT_SUPPORTED_STYLES, AMBILIGHT_SUPPORTED_STYLES_EXTENDED, APPLICATIONS, CHANNELDB_TV_ANDROID, CHANNELDB_TV_SAPHI, CHANNELDB_TV_CHANNELLISTS_ALL, CHANNELDB_TV_FAVORITELISTS_ALLTER, CHANNELDB_TV_FAVORITELISTS_1, ACTIVITIES_CURRENT, CONTEXT, MENUITEMS_SETTINGS_STRUCTURE, MENUITEMS_SETTINGS_CURRENT, POWERSTATE, SYSTEM_ANDROID_DECRYPTED, SYSTEM_ANDROID_ENCRYPTED, SYSTEM_SAPHI_DECRYPTED, SYSTEM_SAPHI_ENCRYPTED, VOLUME, SCREENSTATE, HUELAMPPOWER, RECORDINGS_LIST, PAIR_REQUEST_DEVICE, PAIR_REQUEST_SAPHI, PAIR_REQUEST_ANDROID, PAIR_RESPONSE, ) from haphilipsjs.typing import StringsRequest, Strings MOCK_ANDROID_SOURCES = { "content://android.media.tv/channel": {"name": "Watch TV"}, "content://android.media.tv/passthrough/com.mediatek.tvinput%2F.hdmi.HDMIInputService%2FHW5": { "name": "HDMI 1" }, "content://android.media.tv/passthrough/com.mediatek.tvinput%2F.hdmi.HDMIInputService%2FHW6": { "name": "HDMI 2" }, "content://android.media.tv/passthrough/com.mediatek.tvinput%2F.hdmi.HDMIInputService%2FHW7": { "name": "HDMI 3" }, "content://android.media.tv/passthrough/com.mediatek.tvinput%2F.hdmi.HDMIInputService%2FHW8": { "name": "HDMI 4" }, } MOCK_SAPHI_SOURCES = {} class Param(NamedTuple): type: str base: str @pytest.fixture(params=["android", "saphi"], name="param") async def param_fixture(request): if request.param == "android": return Param(request.param, "https://127.0.0.1:1926/6") elif request.param == "saphi": return Param(request.param, "http://127.0.0.1:1925/6") else: raise Exception @pytest.fixture async def client_mock(param: Param): with respx.mock: if param.type == "android": respx.get(f"{param.base}/system").respond( json=cast(Dict, SYSTEM_ANDROID_ENCRYPTED) ) respx.get("http://127.0.0.1:1925/6/system").respond( json={} ) respx.get(f"{param.base}/channeldb/tv").respond( json=cast(Dict, CHANNELDB_TV_ANDROID) ) elif param.type == "saphi": respx.get(f"{param.base}/system").respond( json=cast(Dict, SYSTEM_SAPHI_ENCRYPTED) ) respx.get(f"{param.base}/channeldb/tv").respond( json=cast(Dict, CHANNELDB_TV_SAPHI) ) else: raise Exception respx.get(f"{param.base}/channeldb/tv/channelLists/all").respond( json=cast(Dict, CHANNELDB_TV_CHANNELLISTS_ALL) ) respx.get(f"{param.base}/channeldb/tv/favoriteLists/allter").respond( json=cast(Dict, CHANNELDB_TV_FAVORITELISTS_ALLTER) ) respx.get(f"{param.base}/channeldb/tv/favoriteLists/1").respond( json=cast(Dict, CHANNELDB_TV_FAVORITELISTS_1) ) respx.get(f"{param.base}/activities/current").respond( json=cast(Dict, ACTIVITIES_CURRENT) ) respx.get(f"{param.base}/activities/tv").respond(json=cast(Dict, ACTIVITIES_TV[param.type])) respx.get(f"{param.base}/applications").respond(json=cast(Dict, APPLICATIONS)) respx.get(f"{param.base}/powerstate").respond(json=POWERSTATE) respx.get(f"{param.base}/screenstate").respond(json=SCREENSTATE) respx.get(f"{param.base}/context").respond(json=cast(Dict, CONTEXT)) respx.get(f"{param.base}/audio/volume").respond(json=VOLUME) respx.get(f"{param.base}/ambilight/mode").respond(json=AMBILIGHT["mode"]) respx.get(f"{param.base}/ambilight/topology").respond( json=AMBILIGHT["topology"] ) respx.get(f"{param.base}/ambilight/measured").respond( json=AMBILIGHT["measured"] ) respx.get(f"{param.base}/ambilight/processed").respond( json=AMBILIGHT["processed"] ) respx.get(f"{param.base}/ambilight/cached").respond(json=AMBILIGHT["cached"]) respx.get(f"{param.base}/ambilight/power").respond(json={"power": "On"}) respx.get(f"{param.base}/ambilight/supportedstyles").respond( json=cast(Dict, AMBILIGHT_SUPPORTED_STYLES) ) respx.get(f"{param.base}/ambilight/currentconfiguration").respond( json=cast(Dict, AMBILIGHT_CURRENT_CONFIGURATION) ) respx.get(f"{param.base}/HueLamp/power").respond(json=HUELAMPPOWER) respx.get(f"{param.base}/menuitems/settings/structure").respond( json=cast(Dict, MENUITEMS_SETTINGS_STRUCTURE) ) respx.post(f"{param.base}/menuitems/settings/current").respond( json=cast(Dict, MENUITEMS_SETTINGS_CURRENT) ) if param.type == "android": client = haphilipsjs.PhilipsTV( "127.0.0.1", api_version=6, secured_transport=True ) elif param.type == "saphi": client = haphilipsjs.PhilipsTV( "127.0.0.1", api_version=6, secured_transport=False ) else: raise Exception respx.get(f"{param.base}/recordings/list").respond( json=RECORDINGS_LIST ) yield client await client.aclose() async def test_basic_data(client_mock, param: Param): """Test for basic data""" await client_mock.update() assert client_mock.on == True if param.type == "android": assert client_mock.system == SYSTEM_ANDROID_DECRYPTED assert client_mock.sources == MOCK_ANDROID_SOURCES assert client_mock.applications == { app["id"]: app for app in APPLICATIONS["applications"] if "id" in app } assert ( client_mock.application_id == "org.droidtv.nettv.market.MarketMainActivity-org.droidtv.nettv.market" ) assert client_mock.quirk_ambilight_mode_ignored == True assert client_mock.os_type == "MSAF_2019_P" assert client_mock.channel_list_id == "1" assert client_mock.channels_current == [ {"ccid": 1649, "preset": "1"} ] elif param.type == "saphi": assert client_mock.system == SYSTEM_SAPHI_DECRYPTED assert client_mock.sources == MOCK_SAPHI_SOURCES assert client_mock.applications == {} assert client_mock.application_id == None assert client_mock.quirk_ambilight_mode_ignored == True assert client_mock.os_type == "Linux" assert client_mock.channel_list_id == "all" assert client_mock.channels_current == list(client_mock.channels.values()) assert client_mock.channels == { "1648": {"ccid": 1648, "preset": "1", "name": "Irdeto scrambled"}, "1649": {"ccid": 1649, "preset": "2"}, } assert client_mock.powerstate == POWERSTATE["powerstate"] assert client_mock.screenstate == SCREENSTATE["screenstate"] assert client_mock.huelamp_power == HUELAMPPOWER["power"] async def test_current_channel_none(client_mock, param): await client_mock.update() assert client_mock.channel_id == "1648" respx.get(f"{param.base}/activities/tv").respond(json={}) await client_mock.update() assert client_mock.channel_id == None async def test_get_channel_name(client_mock): """Verify that we can translate channel id to name""" await client_mock.update() assert await client_mock.getChannelName("1648") == "Irdeto scrambled" assert await client_mock.getChannelName("balha") == None async def test_set_source(client_mock, param): """Verify that we can translate channel id to name""" await client_mock.update() if param.type == "android": route = respx.post(f"{param.base}/activities/launch").respond(json={}) assert await client_mock.setSource( "content://android.media.tv/passthrough/com.mediatek.tvinput%2F.hdmi.HDMIInputService%2FHW5" ) assert json.loads(route.calls[0].request.content) == { "intent": { "extras": { "uri": "content://android.media.tv/passthrough/com.mediatek.tvinput%2F.hdmi.HDMIInputService%2FHW5" }, "action": "org.droidtv.playtv.SELECTURI", "component": { "packageName": "org.droidtv.playtv", "className": "org.droidtv.playtv.PlayTvActivity", }, } } elif param.type == "saphi": assert await client_mock.setSource("a") == False async def test_timeout(client_mock, param): """Test that connect timeouts trigger tv to be considered off""" await client_mock.update() assert client_mock.on == True respx.get(f"{param.base}/audio/volume").mock(side_effect=httpx.ConnectTimeout) await client_mock.update() assert client_mock.on == False respx.get(f"{param.base}/audio/volume").respond(json=VOLUME) await client_mock.update() assert client_mock.on == True async def test_volume(client_mock, param: Param): respx.get(f"{param.base}/audio/volume").respond( json={"muted": True, "current": 30, "min": 0, "max": 60} ) await client_mock.update() assert client_mock.volume == 0.5 assert client_mock.muted == True respx.get(f"{param.base}/audio/volume").respond( json={"muted": False, "current": 60, "min": 0, "max": 60} ) await client_mock.update() assert client_mock.volume == 1.0 assert client_mock.muted == False respx.get(f"{param.base}/audio/volume").respond( json={"muted": False, "current": 0, "min": 0, "max": 60} ) await client_mock.update() assert client_mock.volume == 0.0 assert client_mock.muted == False async def test_set_volume(client_mock, param: Param): await client_mock.update() respx.post(f"{param.base}/audio/volume").respond(json={}) await client_mock.setVolume(0.5) assert respx.calls[-1].request.url == f"{param.base}/audio/volume" assert json.loads(respx.calls[-1].request.content) == { "muted": False, "current": 30, } await client_mock.setVolume(1.0, True) assert json.loads(respx.calls[-1].request.content) == { "muted": True, "current": 60, } await client_mock.setVolume(0.0, True) assert json.loads(respx.calls[-1].request.content) == { "muted": True, "current": 0, } await client_mock.setVolume(None, True) assert json.loads(respx.calls[-1].request.content) == { "muted": True, } async def test_set_channel(client_mock, param: Param): respx.post(f"{param.base}/activities/tv").respond(json={}) await client_mock.setChannel(1649) assert respx.calls[-1].request.url == f"{param.base}/activities/tv" assert json.loads(respx.calls[-1].request.content) == { "channelList": {"id": "all"}, "channel": {"ccid": 1649}, } async def test_send_key(client_mock, param: Param): respx.post(f"{param.base}/input/key").respond(json={}) await client_mock.update() await client_mock.sendKey("Standby") assert respx.calls[-1].request.url == f"{param.base}/input/key" assert json.loads(respx.calls[-1].request.content) == { "key": "Standby", } assert client_mock.audio_volume["muted"] == False await client_mock.sendKey("Mute") assert client_mock.audio_volume["muted"] == True assert client_mock.audio_volume["current"] == 18 await client_mock.sendKey("VolumeUp") assert client_mock.audio_volume["current"] == 19 await client_mock.sendKey("VolumeDown") assert client_mock.audio_volume["current"] == 18 async def test_send_key_off(client_mock, param: Param): respx.post(f"{param.base}/input/key").mock(side_effect=httpx.ConnectTimeout) with pytest.raises(haphilipsjs.ConnectionFailure): await client_mock.sendKey("Standby") async def test_send_key_retry(client_mock, param: Param): route = respx.post(f"{param.base}/input/key").mock(side_effect=httpx.RemoteProtocolError) with pytest.raises(haphilipsjs.ProtocolFailure): await client_mock.sendKey("Standby") assert route.call_count == 2 async def test_ambilight_mode(client_mock, param): await client_mock.getSystem() respx.post(f"{param.base}/ambilight/mode").respond(json={}) await client_mock.setAmbilightMode("internal") if client_mock.quirk_ambilight_mode_ignored: assert respx.calls[-2].request.url == f"{param.base}/ambilight/mode" assert json.loads(respx.calls[-2].request.content) == { "current": "internal", } assert respx.calls[-1].request.url == f"{param.base}/ambilight/mode" assert json.loads(respx.calls[-1].request.content) == { "current": "internal_invalid", } else: assert respx.calls[-1].request.url == f"{param.base}/ambilight/mode" assert json.loads(respx.calls[-1].request.content) == { "current": "internal", } assert await client_mock.getAmbilightMode() assert client_mock.ambilight_mode == "internal" assert await client_mock.setAmbilightMode("manual") assert client_mock.ambilight_mode == "manual" if client_mock.quirk_ambilight_mode_ignored: assert await client_mock.getAmbilightMode() assert client_mock.ambilight_mode == "manual" assert await client_mock.setAmbilightMode("interal") assert await client_mock.getAmbilightMode() async def test_ambilight_power(client_mock, param): route = respx.post(f"{param.base}/ambilight/power").respond(json={}) assert client_mock.ambilight_power == None await client_mock.update() await client_mock.getAmbilightPower() assert client_mock.ambilight_power == "On" await client_mock.setAmbilightPower("On") assert json.loads(route.calls[0].request.content) == {"power": "On"} await client_mock.setAmbilightPower("Off") assert json.loads(route.calls[1].request.content) == {"power": "Off"} async def test_ambilight_topology(client_mock): assert await client_mock.getAmbilightTopology() == AMBILIGHT["topology"] async def test_ambilight_measured(client_mock): assert await client_mock.getAmbilightMeasured() == AMBILIGHT["measured"] async def test_ambilight_processed(client_mock): assert await client_mock.getAmbilightProcessed() == AMBILIGHT["processed"] async def test_ambilight_cached(client_mock, param: Param): assert await client_mock.getAmbilightCached() == AMBILIGHT["cached"] respx.post(f"{param.base}/ambilight/cached").respond(json={}) data = {"r": 100, "g": 210, "b": 30} await client_mock.setAmbilightCached(data) assert respx.calls[-2].request.url == f"{param.base}/ambilight/cached" assert respx.calls[-2].request.url == f"{param.base}/ambilight/cached" assert json.loads(respx.calls[-2].request.content) == data async def test_ambilight_modes(client_mock, param): await client_mock.getSystem() if param.type == "android": assert client_mock.ambilight_modes == ["internal", "manual", "expert", "lounge"] elif param.type == "saphi": assert client_mock.ambilight_modes == ["internal", "manual", "expert"] async def test_ambilight_current_configuration(client_mock, param): respx.post(f"{param.base}/ambilight/currentconfiguration").respond(json={}) await client_mock.getSystem() await client_mock.getAmbilightCurrentConfiguration() data = { "isExpert": False, "menuSetting": "STANDARD", "stringValue": "Standard", "styleName": "FOLLOW_VIDEO", } assert client_mock.ambilight_current_configuration == data await client_mock.setAmbilightCurrentConfiguration(data) assert json.loads(respx.calls[-1].request.content) == data async def test_ambilight_supported_stypes(client_mock, param): await client_mock.getSystem() await client_mock.getAmbilightSupportedStyles() assert client_mock.ambilight_styles == AMBILIGHT_SUPPORTED_STYLES_EXTENDED[param.type] async def test_menu_items_current(client_mock: haphilipsjs.PhilipsTV, param): await client_mock.getSystem() MOCK_VALUE_1 = { "Nodeid": 2131230778, "Controllable": False, "Available": True, "string_id": "Inschakelen", "data": { "value": True } } MOCK_VALUE_2 = { "Nodeid": 2131230779, "Controllable": False, "Available": True, "string_id": "Inschakelen 2", "data": { "value": True } } route = respx.post(f"{param.base}/menuitems/settings/current") route.side_effect = [ httpx.Response( status_code=200, json={ "values": [{"value": MOCK_VALUE_1}], "version": 0 } ), httpx.Response( status_code=200, json={ "values": [{"value": MOCK_VALUE_2}], "version": 0 } ), ] with patch.object(haphilipsjs, "MAXIMUM_ITEMS_IN_REQUEST", new=1): data = await client_mock.getMenuItemsSettingsCurrentValue([2131230778, 2131230779]) if param.type == "android": assert data == { 2131230778: MOCK_VALUE_1, 2131230779: MOCK_VALUE_2, } elif param.type == "saphi": assert data == { 2131230778: None, 2131230779: None } async def test_get_strings_cached(client_mock: haphilipsjs.PhilipsTV, param: Param): await client_mock.getSystem() assert client_mock.strings == {} with respx.mock as mock: def mock_strings(request: StringsRequest, response: Strings): return mock.post(f"{param.base}/strings", json=request).respond(json=cast(Dict, response)) mock_strings({ "strings": [{"string_id": "1"}, {"string_id": "2"}], "locale": { "language": "", "country": "", "variant": "", } }, { "translations": [ { "string_id": "1", "string_translation": "ONE"}, { "string_id": "2", "string_translation": "TWO"} ] } ) res = await client_mock.getStringsCached(["1","2"]) assert res == {"1": "ONE", "2": "TWO"} assert client_mock.strings == {"1": "ONE", "2": "TWO"} mock_strings({ "strings": [{"string_id": "3"}, {"string_id": "4"}], "locale": { "language": "", "country": "", "variant": "", }, }, { "translations": [ { "string_id": "3", "string_translation": "THREE"}, { "string_id": "4", "string_translation": "FOUR"} ] } ) res = await client_mock.getStringsCached(["1","3","4"]) assert res == {"1": "ONE", "2": "TWO", "3": "THREE", "4": "FOUR"} assert client_mock.strings == {"1": "ONE", "2": "TWO", "3": "THREE", "4": "FOUR"} async def test_buggy_json(): assert haphilipsjs.decode_xtv_json("") == {} assert haphilipsjs.decode_xtv_json("}") == {} assert haphilipsjs.decode_xtv_json("Context Service not started") == {} assert haphilipsjs.decode_xtv_json('{,"a":{}}') == {"a": {}} assert haphilipsjs.decode_xtv_json('{"a":{},}') == {"a": {}} assert haphilipsjs.decode_xtv_json('{"a":{},,,"b":{}}') == {"a": {}, "b": {}} with pytest.raises(haphilipsjs.NoneJsonData): haphilipsjs.decode_xtv_json("Plain text data") async def test_get_recordings(client_mock): """Verify that we can read back selected recording values""" await client_mock.update() recording_ongoing = False recording_new = 0 assert client_mock.recordings_list["version"] == "253.91" for rec in client_mock.recordings_list["recordings"]: if rec["RecordingId"] == 36: rec_time_planned = rec["StartTime"] rec_margin_start = rec["MarginStart"] rec_time = rec_time_planned - rec_margin_start assert rec_time == 1676833531 assert rec["RecordingType"] == "RECORDING_ONGOING" assert rec["RecError"] == "REC_ERROR_NONE" if rec["RecordingType"] == "RECORDING_ONGOING": recording_ongoing = True if rec["RecordingType"] == "RECORDING_NEW": recording_new += 1 assert recording_ongoing == True assert recording_new == 1 async def test_pair_request(client_mock: haphilipsjs.PhilipsTV, param: Param): await client_mock.update() if param.type == "android": post_json = PAIR_REQUEST_ANDROID elif param.type == "saphi": post_json = PAIR_REQUEST_SAPHI else: pytest.skip("Unknown pair data") device = post_json["device"] with respx.mock as mock: route = mock.post(f"{param.base}/pair/request") route.respond( json=cast(Dict, PAIR_RESPONSE) ) data = await client_mock.pairRequest( app_id=device["app_id"], app_name=device["app_name"], device_name=device["device_name"], device_os=device["device_os"], type=device["type"], device_id = device["id"] ) assert json.loads(route.calls.last.request.content) == post_json assert data == { "device": device, "auth_key": PAIR_RESPONSE["auth_key"], "timestamp": PAIR_RESPONSE["timestamp"], }