├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── issue.md └── workflows │ ├── hassfest.yaml │ └── validate.yaml ├── .gitignore ├── LICENSE ├── README.md ├── custom_components └── hyperhdr │ ├── __init__.py │ ├── camera.py │ ├── config_flow.py │ ├── const.py │ ├── light.py │ ├── manifest.json │ ├── sensor.py │ ├── switch.py │ └── translations │ ├── ar.json │ ├── ca.json │ ├── cs.json │ ├── de.json │ ├── el.json │ ├── en.json │ ├── es-419.json │ ├── es.json │ ├── et.json │ ├── fr.json │ ├── he.json │ ├── hu.json │ ├── id.json │ ├── it.json │ ├── ko.json │ ├── lb.json │ ├── nl.json │ ├── no.json │ ├── pl.json │ ├── pt.json │ ├── ru.json │ ├── sl.json │ ├── tr.json │ ├── uk.json │ └── zh-Hant.json ├── hacs.json ├── hyperhdr-logo.png └── info.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Issue 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | 16 | 17 | ## Version of the custom_component 18 | 21 | 22 | ## Configuration 23 | 24 | ```yaml 25 | 26 | Add your logs here. 27 | 28 | ``` 29 | 30 | ## Describe the bug 31 | A clear and concise description of what the bug is. 32 | 33 | 34 | ## Debug log 35 | 36 | 37 | 38 | ```text 39 | 40 | Add your logs here. 41 | 42 | ``` -------------------------------------------------------------------------------- /.github/workflows/hassfest.yaml: -------------------------------------------------------------------------------- 1 | name: Validate with hassfest 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: "0 0 * * *" 8 | 9 | jobs: 10 | validate: 11 | runs-on: "ubuntu-latest" 12 | steps: 13 | - uses: "actions/checkout@v2" 14 | - uses: home-assistant/actions/hassfest@master -------------------------------------------------------------------------------- /.github/workflows/validate.yaml: -------------------------------------------------------------------------------- 1 | name: Validate 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | schedule: 9 | - cron: "0 0 * * *" 10 | 11 | jobs: 12 | validate: 13 | runs-on: "ubuntu-latest" 14 | steps: 15 | - uses: "actions/checkout@v2" 16 | - name: HACS validation 17 | uses: "hacs/action@main" 18 | with: 19 | category: "integration" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | pythonenv* 3 | venv 4 | .venv 5 | .coverage 6 | .idea 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Joakim Sørensen @ludeeus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 13 | 14 | # HyperHDR custom component for Home Assistant 15 | 16 | HyperHDR is an open source bias lighting implementation which runs on many platforms. 17 | 18 | **This component will set up the following platforms.** 19 | 20 | Platform | Description 21 | -- | -- 22 | `light` | Show HyperHDR as a controllable device. 23 | `switch` | Show switches to control HyperHDR components. 24 | 25 | ![hyperhdr-logo](https://github.com/mjoshd/hyperhdr-ha/blob/master/hyperhdr-logo.png) 26 | 27 | ## Installation 28 | 29 | ### Using HACS 30 | 31 | 1. Add to your [HACS](https://hacs.xyz/) custom repositories. 32 | 1. Choose `Integration` from the category selection. 33 | 1. Click install. 34 | 1. Return to the Integrations page within HACS then click the `+ Explore & download repositories` button. 35 | 1. Search for `HyperHDR`, select it, then click `Download this repository with HACS`. 36 | 1. Restart Home Assistant to load the integration. 37 | 1. Visit the Wiki for information regarding: 38 | - [Initial Setup](https://github.com/mjoshd/hyperhdr-ha/wiki#initial-setup) 39 | - [Post-setup Advice](https://github.com/mjoshd/hyperhdr-ha/wiki#post-setup-advice) 40 | - [Debug Logging](https://github.com/mjoshd/hyperhdr-ha/wiki#debug-logging) 41 | 42 | ### Manually (not recommended) 43 | 44 | - Download the [latest release](https://github.com/mjoshd/hyperhdr-ha/releases) as a **zip file** then extract it and move the `hyperhdr` folder into the `custom_components` folder in your Home Assistant installation. 45 | - Restart Home Assistant to load the integration. 46 | 47 | ## Configuration 48 | 49 | 1. In Home Assistant navigate to `Configuration` -> `Devices & Services` -> `Integrations`. 50 | 1. Click the `+ Add Integration` button. 51 | 1. Search for `HyperHDR`. 52 | 1. If you cannot find `HyperHDR` in the list then be sure to clear your browser cache and/or perform a hard-refresh of the page. 53 | 1. Enter the IP address of your HyperHDR instance. 54 | 1. Click the `Submit` button. 55 | 56 | 76 | -------------------------------------------------------------------------------- /custom_components/hyperhdr/__init__.py: -------------------------------------------------------------------------------- 1 | """The HyperHDR component.""" 2 | 3 | from __future__ import annotations 4 | 5 | import asyncio 6 | from collections.abc import Callable 7 | from contextlib import suppress 8 | import logging 9 | from typing import Any, cast 10 | 11 | from awesomeversion import AwesomeVersion 12 | from hyperhdr import client, const as hyperhdr_const 13 | 14 | from homeassistant.config_entries import ConfigEntry 15 | from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TOKEN, Platform 16 | from homeassistant.core import HomeAssistant, callback 17 | from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady 18 | from homeassistant.helpers import device_registry as dr 19 | from homeassistant.helpers.dispatcher import ( 20 | async_dispatcher_connect, 21 | async_dispatcher_send, 22 | ) 23 | 24 | from .const import ( 25 | CONF_INSTANCE_CLIENTS, 26 | CONF_ON_UNLOAD, 27 | CONF_ROOT_CLIENT, 28 | DEFAULT_NAME, 29 | DOMAIN, 30 | HYPERHDR_RELEASES_URL, 31 | HYPERHDR_VERSION_WARN_CUTOFF, 32 | SIGNAL_INSTANCE_ADD, 33 | SIGNAL_INSTANCE_REMOVE, 34 | ) 35 | 36 | ### HyperHDR v0.0.7 37 | PLATFORMS = [Platform.LIGHT, Platform.SWITCH] 38 | 39 | ### Enable BROKEN camera stream. Camera is removed for good reason! Do not open camera related issues!! 40 | # PLATFORMS = [Platform.CAMERA, Platform.LIGHT, Platform.SWITCH] 41 | 42 | ### Original - From Hyperion 43 | # PLATFORMS = [Platform.CAMERA, Platform.LIGHT, Platform.SENSOR, Platform.SWITCH] 44 | 45 | _LOGGER = logging.getLogger(__name__) 46 | 47 | # Unique ID 48 | # ========= 49 | # A config entry represents a connection to a single HyperHDR server. The config entry 50 | # unique_id is the server id returned from the HyperHDR instance (a unique ID per 51 | # server). 52 | # 53 | # Each server connection may create multiple entities. The unique_id for each entity is 54 | # __, where will be the unique_id on the 55 | # relevant config entry (as above), will be the server instance # and 56 | # will be a unique identifying type name for each entity associated with this 57 | # server/instance (e.g. "hyperhdr_light"). 58 | # 59 | # The get_hyperhdr_unique_id method will create a per-entity unique id when given the 60 | # server id, an instance number and a name. 61 | 62 | # hass.data format 63 | # ================ 64 | # 65 | # hass.data[DOMAIN] = { 66 | # : { 67 | # "ROOT_CLIENT": , 68 | # "ON_UNLOAD": [, ...], 69 | # } 70 | # } 71 | 72 | 73 | def get_hyperhdr_unique_id(server_id: str, instance: int, name: str) -> str: 74 | """Get a unique_id for a HyperHDR instance.""" 75 | return f"{server_id}_{instance}_{name}" 76 | 77 | 78 | def get_hyperhdr_device_id(server_id: str, instance: int) -> str: 79 | """Get an id for a HyperHDR device/instance.""" 80 | return f"{server_id}_{instance}" 81 | 82 | 83 | def split_hyperhdr_unique_id(unique_id: str) -> tuple[str, int, str] | None: 84 | """Split a unique_id into a (server_id, instance, type) tuple.""" 85 | data = tuple(unique_id.split("_", 2)) 86 | if len(data) != 3: 87 | return None 88 | try: 89 | return (data[0], int(data[1]), data[2]) 90 | except ValueError: 91 | return None 92 | 93 | 94 | def create_hyperhdr_client( 95 | *args: Any, 96 | **kwargs: Any, 97 | ) -> client.HyperHDRClient: 98 | """Create a HyperHDR Client.""" 99 | return client.HyperHDRClient(*args, **kwargs) 100 | 101 | 102 | async def async_create_connect_hyperhdr_client( 103 | *args: Any, 104 | **kwargs: Any, 105 | ) -> client.HyperHDRClient | None: 106 | """Create and connect a HyperHDR Client.""" 107 | hyperhdr_client = create_hyperhdr_client(*args, **kwargs) 108 | 109 | if not await hyperhdr_client.async_client_connect(): 110 | return None 111 | return hyperhdr_client 112 | 113 | 114 | @callback 115 | def listen_for_instance_updates( 116 | hass: HomeAssistant, 117 | config_entry: ConfigEntry, 118 | add_func: Callable, 119 | remove_func: Callable, 120 | ) -> None: 121 | """Listen for instance additions/removals.""" 122 | 123 | hass.data[DOMAIN][config_entry.entry_id][CONF_ON_UNLOAD].extend( 124 | [ 125 | async_dispatcher_connect( 126 | hass, 127 | SIGNAL_INSTANCE_ADD.format(config_entry.entry_id), 128 | add_func, 129 | ), 130 | async_dispatcher_connect( 131 | hass, 132 | SIGNAL_INSTANCE_REMOVE.format(config_entry.entry_id), 133 | remove_func, 134 | ), 135 | ] 136 | ) 137 | 138 | 139 | async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: 140 | """Set up HyperHDR from a config entry.""" 141 | host = entry.data[CONF_HOST] 142 | port = entry.data[CONF_PORT] 143 | token = entry.data.get(CONF_TOKEN) 144 | 145 | hyperhdr_client = await async_create_connect_hyperhdr_client( 146 | host, port, token=token, raw_connection=True 147 | ) 148 | 149 | # Client won't connect? => Not ready. 150 | if not hyperhdr_client: 151 | raise ConfigEntryNotReady 152 | version = await hyperhdr_client.async_sysinfo_version() 153 | if version is not None: 154 | with suppress(ValueError): 155 | if AwesomeVersion(version) < AwesomeVersion(HYPERHDR_VERSION_WARN_CUTOFF): 156 | _LOGGER.warning( 157 | ( 158 | "Using a HyperHDR server version < %s is not recommended --" 159 | " some features may be unavailable or may not function" 160 | " correctly. Please consider upgrading: %s" 161 | ), 162 | HYPERHDR_VERSION_WARN_CUTOFF, 163 | HYPERHDR_RELEASES_URL, 164 | ) 165 | 166 | # Client needs authentication, but no token provided? => Reauth. 167 | auth_resp = await hyperhdr_client.async_is_auth_required() 168 | if ( 169 | auth_resp is not None 170 | and client.ResponseOK(auth_resp) 171 | and auth_resp.get(hyperhdr_const.KEY_INFO, {}).get( 172 | hyperhdr_const.KEY_REQUIRED, False 173 | ) 174 | and token is None 175 | ): 176 | await hyperhdr_client.async_client_disconnect() 177 | raise ConfigEntryAuthFailed 178 | 179 | # Client login doesn't work? => Reauth. 180 | if not await hyperhdr_client.async_client_login(): 181 | await hyperhdr_client.async_client_disconnect() 182 | raise ConfigEntryAuthFailed 183 | 184 | # Cannot switch instance or cannot load state? => Not ready. 185 | if ( 186 | not await hyperhdr_client.async_client_switch_instance() 187 | or not client.ServerInfoResponseOK(await hyperhdr_client.async_get_serverinfo()) 188 | ): 189 | await hyperhdr_client.async_client_disconnect() 190 | raise ConfigEntryNotReady 191 | 192 | # We need 1 root client (to manage instances being removed/added) and then 1 client 193 | # per HyperHDR server instance which is shared for all entities associated with 194 | # that instance. 195 | hass.data.setdefault(DOMAIN, {}) 196 | hass.data[DOMAIN][entry.entry_id] = { 197 | CONF_ROOT_CLIENT: hyperhdr_client, 198 | CONF_INSTANCE_CLIENTS: {}, 199 | CONF_ON_UNLOAD: [], 200 | } 201 | 202 | async def async_instances_to_clients(response: dict[str, Any]) -> None: 203 | """Convert instances to HyperHDR clients.""" 204 | if not response or hyperhdr_const.KEY_DATA not in response: 205 | return 206 | await async_instances_to_clients_raw(response[hyperhdr_const.KEY_DATA]) 207 | 208 | async def async_instances_to_clients_raw(instances: list[dict[str, Any]]) -> None: 209 | """Convert instances to HyperHDR clients.""" 210 | device_registry = dr.async_get(hass) 211 | running_instances: set[int] = set() 212 | stopped_instances: set[int] = set() 213 | existing_instances = hass.data[DOMAIN][entry.entry_id][CONF_INSTANCE_CLIENTS] 214 | server_id = cast(str, entry.unique_id) 215 | 216 | # In practice, an instance can be in 3 states as seen by this function: 217 | # 218 | # * Exists, and is running: Should be present in HASS/registry. 219 | # * Exists, but is not running: Cannot add it yet, but entity may have be 220 | # registered from a previous time it was running. 221 | # * No longer exists at all: Should not be present in HASS/registry. 222 | 223 | # Add instances that are missing. 224 | for instance in instances: 225 | instance_num = instance.get(hyperhdr_const.KEY_INSTANCE) 226 | if instance_num is None: 227 | continue 228 | if not instance.get(hyperhdr_const.KEY_RUNNING, False): 229 | stopped_instances.add(instance_num) 230 | continue 231 | running_instances.add(instance_num) 232 | if instance_num in existing_instances: 233 | continue 234 | hyperhdr_client = await async_create_connect_hyperhdr_client( 235 | host, port, instance=instance_num, token=token 236 | ) 237 | if not hyperhdr_client: 238 | continue 239 | existing_instances[instance_num] = hyperhdr_client 240 | instance_name = instance.get(hyperhdr_const.KEY_FRIENDLY_NAME, DEFAULT_NAME) 241 | async_dispatcher_send( 242 | hass, 243 | SIGNAL_INSTANCE_ADD.format(entry.entry_id), 244 | instance_num, 245 | instance_name, 246 | ) 247 | 248 | # Remove entities that are not running instances on HyperHDR. 249 | for instance_num in set(existing_instances) - running_instances: 250 | del existing_instances[instance_num] 251 | async_dispatcher_send( 252 | hass, SIGNAL_INSTANCE_REMOVE.format(entry.entry_id), instance_num 253 | ) 254 | 255 | # Ensure every device associated with this config entry is still in the list of 256 | # motionEye cameras, otherwise remove the device (and thus entities). 257 | known_devices = { 258 | get_hyperhdr_device_id(server_id, instance_num) 259 | for instance_num in running_instances | stopped_instances 260 | } 261 | for device_entry in dr.async_entries_for_config_entry( 262 | device_registry, entry.entry_id 263 | ): 264 | for kind, key in device_entry.identifiers: 265 | if kind == DOMAIN and key in known_devices: 266 | break 267 | else: 268 | device_registry.async_remove_device(device_entry.id) 269 | 270 | hyperhdr_client.set_callbacks( 271 | { 272 | f"{hyperhdr_const.KEY_INSTANCE}-{hyperhdr_const.KEY_UPDATE}": async_instances_to_clients, 273 | } 274 | ) 275 | 276 | await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) 277 | assert hyperhdr_client 278 | if hyperhdr_client.instances is not None: 279 | await async_instances_to_clients_raw(hyperhdr_client.instances) 280 | hass.data[DOMAIN][entry.entry_id][CONF_ON_UNLOAD].append( 281 | entry.add_update_listener(_async_entry_updated) 282 | ) 283 | 284 | return True 285 | 286 | 287 | async def _async_entry_updated(hass: HomeAssistant, config_entry: ConfigEntry) -> None: 288 | """Handle entry updates.""" 289 | await hass.config_entries.async_reload(config_entry.entry_id) 290 | 291 | 292 | async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: 293 | """Unload a config entry.""" 294 | unload_ok = await hass.config_entries.async_unload_platforms( 295 | config_entry, PLATFORMS 296 | ) 297 | if unload_ok and config_entry.entry_id in hass.data[DOMAIN]: 298 | config_data = hass.data[DOMAIN].pop(config_entry.entry_id) 299 | for func in config_data[CONF_ON_UNLOAD]: 300 | func() 301 | 302 | # Disconnect the shared instance clients. 303 | await asyncio.gather( 304 | *( 305 | config_data[CONF_INSTANCE_CLIENTS][ 306 | instance_num 307 | ].async_client_disconnect() 308 | for instance_num in config_data[CONF_INSTANCE_CLIENTS] 309 | ) 310 | ) 311 | 312 | # Disconnect the root client. 313 | root_client = config_data[CONF_ROOT_CLIENT] 314 | await root_client.async_client_disconnect() 315 | return unload_ok 316 | -------------------------------------------------------------------------------- /custom_components/hyperhdr/camera.py: -------------------------------------------------------------------------------- 1 | """Switch platform for HyperHDR.""" 2 | 3 | from __future__ import annotations 4 | 5 | import asyncio 6 | import base64 7 | import binascii 8 | from collections.abc import AsyncGenerator 9 | from contextlib import asynccontextmanager 10 | import functools 11 | from typing import Any 12 | 13 | from aiohttp import web 14 | from hyperhdr import client 15 | from hyperhdr.const import ( 16 | KEY_IMAGE, 17 | KEY_IMAGE_STREAM, 18 | KEY_LEDCOLORS, 19 | KEY_RESULT, 20 | KEY_UPDATE, 21 | ) 22 | 23 | from homeassistant.components.camera import ( 24 | DEFAULT_CONTENT_TYPE, 25 | Camera, 26 | async_get_still_stream, 27 | ) 28 | from homeassistant.config_entries import ConfigEntry 29 | from homeassistant.core import HomeAssistant, callback 30 | from homeassistant.helpers.device_registry import DeviceInfo 31 | from homeassistant.helpers.dispatcher import ( 32 | async_dispatcher_connect, 33 | async_dispatcher_send, 34 | ) 35 | from homeassistant.helpers.entity_platform import AddEntitiesCallback 36 | 37 | from . import ( 38 | get_hyperhdr_device_id, 39 | get_hyperhdr_unique_id, 40 | listen_for_instance_updates, 41 | ) 42 | from .const import ( 43 | CONF_INSTANCE_CLIENTS, 44 | DOMAIN, 45 | HYPERHDR_MANUFACTURER_NAME, 46 | HYPERHDR_MODEL_NAME, 47 | SIGNAL_ENTITY_REMOVE, 48 | TYPE_HYPERHDR_CAMERA, 49 | ) 50 | 51 | IMAGE_STREAM_JPG_SENTINEL = "data:image/jpg;base64," 52 | 53 | 54 | async def async_setup_entry( 55 | hass: HomeAssistant, 56 | config_entry: ConfigEntry, 57 | async_add_entities: AddEntitiesCallback, 58 | ) -> None: 59 | """Set up a HyperHDR platform from config entry.""" 60 | entry_data = hass.data[DOMAIN][config_entry.entry_id] 61 | server_id = config_entry.unique_id 62 | 63 | def camera_unique_id(instance_num: int) -> str: 64 | """Return the camera unique_id.""" 65 | assert server_id 66 | return get_hyperhdr_unique_id(server_id, instance_num, TYPE_HYPERHDR_CAMERA) 67 | 68 | @callback 69 | def instance_add(instance_num: int, instance_name: str) -> None: 70 | """Add entities for a new HyperHDR instance.""" 71 | assert server_id 72 | async_add_entities( 73 | [ 74 | HyperHDRCamera( 75 | server_id, 76 | instance_num, 77 | instance_name, 78 | entry_data[CONF_INSTANCE_CLIENTS][instance_num], 79 | ) 80 | ] 81 | ) 82 | 83 | @callback 84 | def instance_remove(instance_num: int) -> None: 85 | """Remove entities for an old HyperHDR instance.""" 86 | assert server_id 87 | async_dispatcher_send( 88 | hass, 89 | SIGNAL_ENTITY_REMOVE.format( 90 | camera_unique_id(instance_num), 91 | ), 92 | ) 93 | 94 | listen_for_instance_updates(hass, config_entry, instance_add, instance_remove) 95 | 96 | 97 | # A note on HyperHDR streaming semantics: 98 | # 99 | # Different HyperHDR priorities behave different with regards to streaming. Colors will 100 | # not stream (as there is nothing to stream). External grabbers (e.g. USB Capture) will 101 | # stream what is being captured. Some effects (based on GIFs) will stream, others will 102 | # not. In cases when streaming is not supported from a selected priority, there is no 103 | # notification beyond the failure of new frames to arrive. 104 | 105 | 106 | class HyperHDRCamera(Camera): 107 | """ComponentBinarySwitch switch class.""" 108 | 109 | # The camera component does not work and is being disabled by default. 110 | _attr_entity_registry_enabled_default = False 111 | _attr_has_entity_name = True 112 | _attr_name = None 113 | 114 | def __init__( 115 | self, 116 | server_id: str, 117 | instance_num: int, 118 | instance_name: str, 119 | hyperhdr_client: client.HyperHDRClient, 120 | ) -> None: 121 | """Initialize the switch.""" 122 | super().__init__() 123 | 124 | self._attr_unique_id = get_hyperhdr_unique_id( 125 | server_id, instance_num, TYPE_HYPERHDR_CAMERA 126 | ) 127 | self._device_id = get_hyperhdr_device_id(server_id, instance_num) 128 | self._instance_name = instance_name 129 | self._client = hyperhdr_client 130 | 131 | self._image_cond = asyncio.Condition() 132 | self._image: bytes | None = None 133 | 134 | # The number of open streams, when zero the stream is stopped. 135 | self._image_stream_clients = 0 136 | 137 | self._client_callbacks = { 138 | f"{KEY_LEDCOLORS}-{KEY_IMAGE_STREAM}-{KEY_UPDATE}": self._update_imagestream 139 | } 140 | self._attr_device_info = DeviceInfo( 141 | identifiers={(DOMAIN, self._device_id)}, 142 | manufacturer=HYPERHDR_MANUFACTURER_NAME, 143 | model=HYPERHDR_MODEL_NAME, 144 | name=instance_name, 145 | configuration_url=hyperhdr_client.remote_url, 146 | ) 147 | 148 | @property 149 | def is_on(self) -> bool: 150 | """Return true if the camera is on.""" 151 | return self.available 152 | 153 | @property 154 | def available(self) -> bool: 155 | """Return server availability.""" 156 | return bool(self._client.has_loaded_state) 157 | 158 | async def _update_imagestream(self, img: dict[str, Any] | None = None) -> None: 159 | """Update HyperHDR components.""" 160 | if not img: 161 | return 162 | img_data = img.get(KEY_RESULT, {}).get(KEY_IMAGE) 163 | if not img_data or not img_data.startswith(IMAGE_STREAM_JPG_SENTINEL): 164 | return 165 | async with self._image_cond: 166 | try: 167 | self._image = base64.b64decode( 168 | img_data.removeprefix(IMAGE_STREAM_JPG_SENTINEL) 169 | ) 170 | except binascii.Error: 171 | return 172 | self._image_cond.notify_all() 173 | 174 | async def _async_wait_for_camera_image(self) -> bytes | None: 175 | """Return a single camera image in a stream.""" 176 | async with self._image_cond: 177 | await self._image_cond.wait() 178 | return self._image if self.available else None 179 | 180 | async def _start_image_streaming_for_client(self) -> bool: 181 | """Start streaming for a client.""" 182 | if ( 183 | not self._image_stream_clients 184 | and not await self._client.async_send_image_stream_start() 185 | ): 186 | return False 187 | 188 | self._image_stream_clients += 1 189 | self._attr_is_streaming = True 190 | self.async_write_ha_state() 191 | return True 192 | 193 | async def _stop_image_streaming_for_client(self) -> None: 194 | """Stop streaming for a client.""" 195 | self._image_stream_clients -= 1 196 | 197 | if not self._image_stream_clients: 198 | await self._client.async_send_image_stream_stop() 199 | self._attr_is_streaming = False 200 | self.async_write_ha_state() 201 | 202 | @asynccontextmanager 203 | async def _image_streaming(self) -> AsyncGenerator: 204 | """Async context manager to start/stop image streaming.""" 205 | try: 206 | yield await self._start_image_streaming_for_client() 207 | finally: 208 | await self._stop_image_streaming_for_client() 209 | 210 | async def async_camera_image( 211 | self, width: int | None = None, height: int | None = None 212 | ) -> bytes | None: 213 | """Return single camera image bytes.""" 214 | async with self._image_streaming() as is_streaming: 215 | if is_streaming: 216 | return await self._async_wait_for_camera_image() 217 | return None 218 | 219 | async def handle_async_mjpeg_stream( 220 | self, request: web.Request 221 | ) -> web.StreamResponse | None: 222 | """Serve an HTTP MJPEG stream from the camera.""" 223 | async with self._image_streaming() as is_streaming: 224 | if is_streaming: 225 | return await async_get_still_stream( 226 | request, 227 | self._async_wait_for_camera_image, 228 | DEFAULT_CONTENT_TYPE, 229 | 0.0, 230 | ) 231 | return None 232 | 233 | async def async_added_to_hass(self) -> None: 234 | """Register callbacks when entity added to hass.""" 235 | self.async_on_remove( 236 | async_dispatcher_connect( 237 | self.hass, 238 | SIGNAL_ENTITY_REMOVE.format(self._attr_unique_id), 239 | functools.partial(self.async_remove, force_remove=True), 240 | ) 241 | ) 242 | 243 | self._client.add_callbacks(self._client_callbacks) 244 | 245 | async def async_will_remove_from_hass(self) -> None: 246 | """Cleanup prior to hass removal.""" 247 | self._client.remove_callbacks(self._client_callbacks) 248 | 249 | 250 | CAMERA_TYPES = { 251 | TYPE_HYPERHDR_CAMERA: HyperHDRCamera, 252 | } 253 | -------------------------------------------------------------------------------- /custom_components/hyperhdr/config_flow.py: -------------------------------------------------------------------------------- 1 | """HyperHDR config flow.""" 2 | 3 | from __future__ import annotations 4 | 5 | import asyncio 6 | from collections.abc import Mapping 7 | from contextlib import suppress 8 | import logging 9 | from typing import Any 10 | from urllib.parse import urlparse 11 | 12 | from hyperhdr import client, const 13 | import voluptuous as vol 14 | 15 | from homeassistant.components import ssdp 16 | from homeassistant.config_entries import ( 17 | SOURCE_REAUTH, 18 | ConfigEntry, 19 | ConfigFlow, 20 | ConfigFlowResult, 21 | OptionsFlow, 22 | ) 23 | from homeassistant.const import ( 24 | CONF_BASE, 25 | CONF_HOST, 26 | CONF_ID, 27 | CONF_PORT, 28 | CONF_SOURCE, 29 | CONF_TOKEN, 30 | ) 31 | from homeassistant.core import callback 32 | import homeassistant.helpers.config_validation as cv 33 | 34 | from . import create_hyperhdr_client 35 | from .const import ( 36 | CONF_AUTH_ID, 37 | CONF_CREATE_TOKEN, 38 | CONF_EFFECT_HIDE_LIST, 39 | CONF_EFFECT_SHOW_LIST, 40 | CONF_PRIORITY, 41 | DEFAULT_ORIGIN, 42 | DEFAULT_PRIORITY, 43 | DOMAIN, 44 | ) 45 | 46 | _LOGGER = logging.getLogger(__name__) 47 | _LOGGER.setLevel(logging.DEBUG) 48 | 49 | # +------------------+ +------------------+ +--------------------+ +--------------------+ 50 | # |Step: SSDP | |Step: user | |Step: import | |Step: reauth | 51 | # | | | | | | | | 52 | # |Input: | |Input: | |Input: | |Input: | 53 | # +------------------+ +------------------+ +--------------------+ +--------------------+ 54 | # v v v v 55 | # +-------------------+-----------------------+--------------------+ 56 | # Auth not | Auth | 57 | # required? | required? | 58 | # | v 59 | # | +------------+ 60 | # | |Step: auth | 61 | # | | | 62 | # | |Input: token| 63 | # | +------------+ 64 | # | Static | 65 | # v token | 66 | # <------------------+ 67 | # | | 68 | # | | New token 69 | # | v 70 | # | +------------------+ 71 | # | |Step: create_token| 72 | # | +------------------+ 73 | # | | 74 | # | v 75 | # | +---------------------------+ +--------------------------------+ 76 | # | |Step: create_token_external|-->|Step: create_token_external_fail| 77 | # | +---------------------------+ +--------------------------------+ 78 | # | | 79 | # | v 80 | # | +-----------------------------------+ 81 | # | |Step: create_token_external_success| 82 | # | +-----------------------------------+ 83 | # | | 84 | # v<------------------+ 85 | # | 86 | # v 87 | # +-------------+ Confirm not required? 88 | # |Step: Confirm|---------------------->+ 89 | # +-------------+ | 90 | # | | 91 | # v SSDP: Explicit confirm | 92 | # +------------------------------>+ 93 | # | 94 | # v 95 | # +----------------+ 96 | # | Create/Update! | 97 | # +----------------+ 98 | 99 | # A note on choice of discovery mechanisms: HyperHDR supports both Zeroconf and SSDP out 100 | # of the box. This config flow needs two port numbers from the HyperHDR instance, the 101 | # JSON port (for the API) and the UI port (for the user to approve dynamically created 102 | # auth tokens). With Zeroconf the port numbers for both are in different Zeroconf 103 | # entries, and as Home Assistant only passes a single entry into the config flow, we can 104 | # only conveniently 'see' one port or the other (which means we need to guess one port 105 | # number). With SSDP, we get the combined block including both port numbers, so SSDP is 106 | # the favored discovery implementation. 107 | 108 | 109 | class HyperHDRConfigFlow(ConfigFlow, domain=DOMAIN): 110 | """Handle a HyperHDR config flow.""" 111 | 112 | VERSION = 1 113 | 114 | def __init__(self) -> None: 115 | """Instantiate config flow.""" 116 | self._data: dict[str, Any] = {} 117 | self._request_token_task: asyncio.Task | None = None 118 | self._auth_id: str | None = None 119 | self._require_confirm: bool = False 120 | self._port_ui: int = const.DEFAULT_PORT_UI 121 | 122 | def _create_client(self, raw_connection: bool = False) -> client.HyperHDRClient: 123 | """Create and connect a client instance.""" 124 | return create_hyperhdr_client( 125 | self._data[CONF_HOST], 126 | self._data[CONF_PORT], 127 | token=self._data.get(CONF_TOKEN), 128 | raw_connection=raw_connection, 129 | ) 130 | 131 | async def _advance_to_auth_step_if_necessary( 132 | self, hyperhdr_client: client.HyperHDRClient 133 | ) -> ConfigFlowResult: 134 | """Determine if auth is required.""" 135 | auth_resp = await hyperhdr_client.async_is_auth_required() 136 | 137 | # Could not determine if auth is required. 138 | if not auth_resp or not client.ResponseOK(auth_resp): 139 | return self.async_abort(reason="auth_required_error") 140 | auth_required = auth_resp.get(const.KEY_INFO, {}).get(const.KEY_REQUIRED, False) 141 | if auth_required: 142 | return await self.async_step_auth() 143 | return await self.async_step_confirm() 144 | 145 | async def async_step_reauth( 146 | self, entry_data: Mapping[str, Any] 147 | ) -> ConfigFlowResult: 148 | """Handle a reauthentication flow.""" 149 | self._data = dict(entry_data) 150 | async with self._create_client(raw_connection=True) as hyperhdr_client: 151 | if not hyperhdr_client: 152 | return self.async_abort(reason="cannot_connect") 153 | return await self._advance_to_auth_step_if_necessary(hyperhdr_client) 154 | 155 | async def async_step_ssdp( 156 | self, discovery_info: ssdp.SsdpServiceInfo 157 | ) -> ConfigFlowResult: 158 | """Handle a flow initiated by SSDP.""" 159 | # Sample data provided by SSDP: { 160 | # 'ssdp_location': 'http://192.168.0.1:8090/description.xml', 161 | # 'ssdp_st': 'upnp:rootdevice', 162 | # 'deviceType': 'urn:schemas-upnp-org:device:Basic:1', 163 | # 'friendlyName': 'HyperHDR (192.168.0.1)', 164 | # 'manufacturer': 'HyperHDR Open Source Ambient Lighting', 165 | # 'manufacturerURL': 'https://www.hyperhdr-project.org', 166 | # 'modelDescription': 'HyperHDR Open Source Ambient Light', 167 | # 'modelName': 'HyperHDR', 168 | # 'modelNumber': '2.0.0-alpha.8', 169 | # 'modelURL': 'https://www.hyperhdr-project.org', 170 | # 'serialNumber': 'f9aab089-f85a-55cf-b7c1-222a72faebe9', 171 | # 'UDN': 'uuid:f9aab089-f85a-55cf-b7c1-222a72faebe9', 172 | # 'ports': { 173 | # 'jsonServer': '19444', 174 | # 'sslServer': '8092', 175 | # 'protoBuffer': '19445', 176 | # 'flatBuffer': '19400' 177 | # }, 178 | # 'presentationURL': 'index.html', 179 | # 'iconList': { 180 | # 'icon': { 181 | # 'mimetype': 'image/png', 182 | # 'height': '100', 183 | # 'width': '100', 184 | # 'depth': '32', 185 | # 'url': 'img/hyperhdr/ssdp_icon.png' 186 | # } 187 | # }, 188 | # 'ssdp_usn': 'uuid:f9aab089-f85a-55cf-b7c1-222a72faebe9', 189 | # 'ssdp_ext': '', 190 | # 'ssdp_server': 'Raspbian GNU/Linux 10 (buster)/10 UPnP/1.0 HyperHDR/2.0.0-alpha.8'} 191 | 192 | # SSDP requires user confirmation. 193 | self._require_confirm = True 194 | self._data[CONF_HOST] = urlparse(discovery_info.ssdp_location).hostname 195 | try: 196 | self._port_ui = ( 197 | urlparse(discovery_info.ssdp_location).port or const.DEFAULT_PORT_UI 198 | ) 199 | except ValueError: 200 | self._port_ui = const.DEFAULT_PORT_UI 201 | 202 | try: 203 | self._data[CONF_PORT] = int( 204 | discovery_info.upnp.get("ports", {}).get( 205 | "jsonServer", const.DEFAULT_PORT_JSON 206 | ) 207 | ) 208 | except ValueError: 209 | self._data[CONF_PORT] = const.DEFAULT_PORT_JSON 210 | 211 | if not (hyperhdr_id := discovery_info.upnp.get(ssdp.ATTR_UPNP_SERIAL)): 212 | return self.async_abort(reason="no_id") 213 | 214 | # For discovery mechanisms, we set the unique_id as early as possible to 215 | # avoid discovery popping up a duplicate on the screen. The unique_id is set 216 | # authoritatively later in the flow by asking the server to confirm its id 217 | # (which should theoretically be the same as specified here) 218 | await self.async_set_unique_id(hyperhdr_id) 219 | self._abort_if_unique_id_configured() 220 | 221 | async with self._create_client(raw_connection=True) as hyperhdr_client: 222 | if not hyperhdr_client: 223 | return self.async_abort(reason="cannot_connect") 224 | return await self._advance_to_auth_step_if_necessary(hyperhdr_client) 225 | 226 | async def async_step_user( 227 | self, 228 | user_input: dict[str, Any] | None = None, 229 | ) -> ConfigFlowResult: 230 | """Handle a flow initiated by the user.""" 231 | errors = {} 232 | if user_input: 233 | self._data.update(user_input) 234 | 235 | async with self._create_client(raw_connection=True) as hyperhdr_client: 236 | if hyperhdr_client: 237 | return await self._advance_to_auth_step_if_necessary( 238 | hyperhdr_client 239 | ) 240 | errors[CONF_BASE] = "cannot_connect" 241 | 242 | return self.async_show_form( 243 | step_id="user", 244 | data_schema=vol.Schema( 245 | { 246 | vol.Required(CONF_HOST): str, 247 | vol.Optional(CONF_PORT, default=const.DEFAULT_PORT_JSON): int, 248 | } 249 | ), 250 | errors=errors, 251 | ) 252 | 253 | async def _cancel_request_token_task(self) -> None: 254 | """Cancel the request token task if it exists.""" 255 | if self._request_token_task is not None: 256 | if not self._request_token_task.done(): 257 | self._request_token_task.cancel() 258 | 259 | with suppress(asyncio.CancelledError): 260 | await self._request_token_task 261 | self._request_token_task = None 262 | 263 | async def _request_token_task_func(self, auth_id: str) -> None: 264 | """Send an async_request_token request.""" 265 | auth_resp: dict[str, Any] | None = None 266 | async with self._create_client(raw_connection=True) as hyperhdr_client: 267 | if hyperhdr_client: 268 | # The HyperHDR-py client has a default timeout of 3 minutes on this request. 269 | auth_resp = await hyperhdr_client.async_request_token( 270 | comment=DEFAULT_ORIGIN, id=auth_id 271 | ) 272 | await self.hass.config_entries.flow.async_configure( 273 | flow_id=self.flow_id, user_input=auth_resp 274 | ) 275 | 276 | def _get_hyperhdr_url(self) -> str: 277 | """Return the URL of the HyperHDR UI.""" 278 | # If this flow was kicked off by SSDP, this will be the correct frontend URL. If 279 | # this is a manual flow instantiation, then it will be a best guess (as this 280 | # flow does not have that information available to it). This is only used for 281 | # approving new dynamically created tokens, so the complexity of asking the user 282 | # manually for this information is likely not worth it (when it would only be 283 | # used to open a URL, that the user already knows the address of). 284 | return f"http://{self._data[CONF_HOST]}:{self._port_ui}" 285 | 286 | async def _can_login(self) -> bool | None: 287 | """Verify login details.""" 288 | async with self._create_client(raw_connection=True) as hyperhdr_client: 289 | if not hyperhdr_client: 290 | return None 291 | return bool( 292 | client.LoginResponseOK( 293 | await hyperhdr_client.async_login(token=self._data[CONF_TOKEN]) 294 | ) 295 | ) 296 | 297 | async def async_step_auth( 298 | self, 299 | user_input: dict[str, Any] | None = None, 300 | ) -> ConfigFlowResult: 301 | """Handle the auth step of a flow.""" 302 | errors = {} 303 | if user_input: 304 | if user_input.get(CONF_CREATE_TOKEN): 305 | return await self.async_step_create_token() 306 | 307 | # Using a static token. 308 | self._data[CONF_TOKEN] = user_input.get(CONF_TOKEN) 309 | login_ok = await self._can_login() 310 | if login_ok is None: 311 | return self.async_abort(reason="cannot_connect") 312 | if login_ok: 313 | return await self.async_step_confirm() 314 | errors[CONF_BASE] = "invalid_access_token" 315 | 316 | return self.async_show_form( 317 | step_id="auth", 318 | data_schema=vol.Schema( 319 | { 320 | vol.Required(CONF_CREATE_TOKEN): bool, 321 | vol.Optional(CONF_TOKEN): str, 322 | } 323 | ), 324 | errors=errors, 325 | ) 326 | 327 | async def async_step_create_token( 328 | self, user_input: dict[str, Any] | None = None 329 | ) -> ConfigFlowResult: 330 | """Send a request for a new token.""" 331 | if user_input is None: 332 | self._auth_id = client.generate_random_auth_id() 333 | return self.async_show_form( 334 | step_id="create_token", 335 | description_placeholders={ 336 | CONF_AUTH_ID: self._auth_id, 337 | }, 338 | ) 339 | 340 | # Cancel the request token task if it's already running, then re-create it. 341 | await self._cancel_request_token_task() 342 | # Start a task in the background requesting a new token. The next step will 343 | # wait on the response (which includes the user needing to visit the HyperHDR 344 | # UI to approve the request for a new token). 345 | assert self._auth_id is not None 346 | self._request_token_task = self.hass.async_create_task( 347 | self._request_token_task_func(self._auth_id), eager_start=False 348 | ) 349 | return self.async_external_step( 350 | step_id="create_token_external", url=self._get_hyperhdr_url() 351 | ) 352 | 353 | async def async_step_create_token_external( 354 | self, auth_resp: dict[str, Any] | None = None 355 | ) -> ConfigFlowResult: 356 | """Handle completion of the request for a new token.""" 357 | if auth_resp is not None and client.ResponseOK(auth_resp): 358 | token = auth_resp.get(const.KEY_INFO, {}).get(const.KEY_TOKEN) 359 | if token: 360 | self._data[CONF_TOKEN] = token 361 | return self.async_external_step_done( 362 | next_step_id="create_token_success" 363 | ) 364 | return self.async_external_step_done(next_step_id="create_token_fail") 365 | 366 | async def async_step_create_token_success( 367 | self, _: dict[str, Any] | None = None 368 | ) -> ConfigFlowResult: 369 | """Create an entry after successful token creation.""" 370 | # Clean-up the request task. 371 | await self._cancel_request_token_task() 372 | 373 | # Test the token. 374 | login_ok = await self._can_login() 375 | 376 | if login_ok is None: 377 | return self.async_abort(reason="cannot_connect") 378 | if not login_ok: 379 | return self.async_abort(reason="auth_new_token_not_work_error") 380 | return await self.async_step_confirm() 381 | 382 | async def async_step_create_token_fail( 383 | self, _: dict[str, Any] | None = None 384 | ) -> ConfigFlowResult: 385 | """Show an error on the auth form.""" 386 | # Clean-up the request task. 387 | await self._cancel_request_token_task() 388 | return self.async_abort(reason="auth_new_token_not_granted_error") 389 | 390 | async def async_step_confirm( 391 | self, user_input: dict[str, Any] | None = None 392 | ) -> ConfigFlowResult: 393 | """Get final confirmation before entry creation.""" 394 | if user_input is None and self._require_confirm: 395 | return self.async_show_form( 396 | step_id="confirm", 397 | description_placeholders={ 398 | CONF_HOST: self._data[CONF_HOST], 399 | CONF_PORT: self._data[CONF_PORT], 400 | CONF_ID: self.unique_id, 401 | }, 402 | ) 403 | 404 | async with self._create_client() as hyperhdr_client: 405 | if not hyperhdr_client: 406 | return self.async_abort(reason="cannot_connect") 407 | hyperhdr_id = await hyperhdr_client.async_sysinfo_id() 408 | 409 | if not hyperhdr_id: 410 | return self.async_abort(reason="no_id") 411 | 412 | entry = await self.async_set_unique_id(hyperhdr_id, raise_on_progress=False) 413 | 414 | if self.context.get(CONF_SOURCE) == SOURCE_REAUTH and entry is not None: 415 | return self.async_update_reload_and_abort(entry, data=self._data) 416 | 417 | self._abort_if_unique_id_configured() 418 | 419 | return self.async_create_entry( 420 | title=f"{self._data[CONF_HOST]}:{self._data[CONF_PORT]}", data=self._data 421 | ) 422 | 423 | @staticmethod 424 | @callback 425 | def async_get_options_flow(config_entry: ConfigEntry) -> HyperHDROptionsFlow: 426 | """Get the HyperHDR Options flow.""" 427 | return HyperHDROptionsFlow(config_entry) 428 | 429 | 430 | class HyperHDROptionsFlow(OptionsFlow): 431 | """HyperHDR options flow.""" 432 | 433 | def __init__(self, config_entry: ConfigEntry) -> None: 434 | """Initialize a HyperHDR options flow.""" 435 | self._config_entry = config_entry 436 | 437 | def _create_client(self) -> client.HyperHDRClient: 438 | """Create and connect a client instance.""" 439 | return create_hyperhdr_client( 440 | self._config_entry.data[CONF_HOST], 441 | self._config_entry.data[CONF_PORT], 442 | token=self._config_entry.data.get(CONF_TOKEN), 443 | ) 444 | 445 | async def async_step_init( 446 | self, user_input: dict[str, Any] | None = None 447 | ) -> ConfigFlowResult: 448 | """Manage the options.""" 449 | 450 | effects = {} 451 | async with self._create_client() as hyperhdr_client: 452 | if not hyperhdr_client: 453 | return self.async_abort(reason="cannot_connect") 454 | for effect in hyperhdr_client.effects or []: 455 | if const.KEY_NAME in effect: 456 | effects[effect[const.KEY_NAME]] = effect[const.KEY_NAME] 457 | 458 | # If a new effect is added to HyperHDR, we always want it to show by default. So 459 | # rather than store a 'show list' in the config entry, we store a 'hide list'. 460 | # However, it's more intuitive to ask the user to select which effects to show, 461 | # so we inverse the meaning prior to storage. 462 | 463 | if user_input is not None: 464 | effect_show_list = user_input.pop(CONF_EFFECT_SHOW_LIST) 465 | user_input[CONF_EFFECT_HIDE_LIST] = sorted( 466 | set(effects) - set(effect_show_list) 467 | ) 468 | return self.async_create_entry(title="", data=user_input) 469 | 470 | default_effect_show_list = list( 471 | set(effects) 472 | - set(self._config_entry.options.get(CONF_EFFECT_HIDE_LIST, [])) 473 | ) 474 | 475 | return self.async_show_form( 476 | step_id="init", 477 | data_schema=vol.Schema( 478 | { 479 | vol.Optional( 480 | CONF_PRIORITY, 481 | default=self._config_entry.options.get( 482 | CONF_PRIORITY, DEFAULT_PRIORITY 483 | ), 484 | ): vol.All(vol.Coerce(int), vol.Range(min=0, max=255)), 485 | vol.Optional( 486 | CONF_EFFECT_SHOW_LIST, 487 | default=default_effect_show_list, 488 | ): cv.multi_select(effects), 489 | } 490 | ), 491 | ) 492 | -------------------------------------------------------------------------------- /custom_components/hyperhdr/const.py: -------------------------------------------------------------------------------- 1 | """Constants for HyperHDR integration.""" 2 | 3 | CONF_AUTH_ID = "auth_id" 4 | CONF_CREATE_TOKEN = "create_token" 5 | CONF_INSTANCE = "instance" 6 | CONF_INSTANCE_CLIENTS = "INSTANCE_CLIENTS" 7 | CONF_ON_UNLOAD = "ON_UNLOAD" 8 | CONF_PRIORITY = "priority" 9 | CONF_ROOT_CLIENT = "ROOT_CLIENT" 10 | CONF_EFFECT_HIDE_LIST = "effect_hide_list" 11 | CONF_EFFECT_SHOW_LIST = "effect_show_list" 12 | 13 | DEFAULT_NAME = "HyperHDR" 14 | DEFAULT_ORIGIN = "Home Assistant" 15 | DEFAULT_PRIORITY = 128 16 | 17 | DOMAIN = "hyperhdr" 18 | 19 | HYPERHDR_MANUFACTURER_NAME = "HyperHDR" 20 | HYPERHDR_MODEL_NAME = f"{HYPERHDR_MANUFACTURER_NAME}" 21 | HYPERHDR_RELEASES_URL = "https://github.com/awawa-dev/HyperHDR/releases" 22 | HYPERHDR_VERSION_WARN_CUTOFF = "20.0.0.0" 23 | 24 | NAME_SUFFIX_HYPERHDR_LIGHT = "" 25 | NAME_SUFFIX_HYPERHDR_PRIORITY_LIGHT = "Priority" 26 | 27 | SIGNAL_INSTANCE_ADD = f"{DOMAIN}_instance_add_signal.{{}}" 28 | SIGNAL_INSTANCE_REMOVE = f"{DOMAIN}_instance_remove_signal.{{}}" 29 | SIGNAL_ENTITY_REMOVE = f"{DOMAIN}_entity_remove_signal.{{}}" 30 | 31 | TYPE_HYPERHDR_CAMERA = "hyperhdr_camera" 32 | TYPE_HYPERHDR_LIGHT = "hyperhdr_light" 33 | TYPE_HYPERHDR_PRIORITY_LIGHT = "hyperhdr_priority_light" 34 | TYPE_HYPERHDR_COMPONENT_SWITCH_BASE = "hyperhdr_component_switch" 35 | 36 | TYPE_HYPERHDR_SENSOR_BASE = "hyperhdr_sensor" 37 | TYPE_HYPERHDR_SENSOR_VISIBLE_PRIORITY = "visible_priority" 38 | -------------------------------------------------------------------------------- /custom_components/hyperhdr/light.py: -------------------------------------------------------------------------------- 1 | """Support for HyperHDR remotes.""" 2 | from __future__ import annotations 3 | 4 | from collections.abc import Callable, Mapping, Sequence 5 | import functools 6 | import logging 7 | from types import MappingProxyType 8 | from typing import Any 9 | 10 | from hyperhdr import client, const 11 | 12 | from homeassistant.components.light import ( 13 | ATTR_BRIGHTNESS, 14 | ATTR_EFFECT, 15 | ATTR_HS_COLOR, 16 | ColorMode, 17 | LightEntity, 18 | LightEntityFeature, 19 | ) 20 | from homeassistant.config_entries import ConfigEntry 21 | from homeassistant.core import HomeAssistant, callback 22 | from homeassistant.helpers.dispatcher import ( 23 | async_dispatcher_connect, 24 | async_dispatcher_send, 25 | ) 26 | from homeassistant.helpers.entity import DeviceInfo 27 | from homeassistant.helpers.entity_platform import AddEntitiesCallback 28 | import homeassistant.util.color as color_util 29 | 30 | from . import ( 31 | get_hyperhdr_device_id, 32 | get_hyperhdr_unique_id, 33 | listen_for_instance_updates, 34 | ) 35 | from .const import ( 36 | CONF_EFFECT_HIDE_LIST, 37 | CONF_INSTANCE_CLIENTS, 38 | CONF_PRIORITY, 39 | DEFAULT_ORIGIN, 40 | DEFAULT_PRIORITY, 41 | DOMAIN, 42 | HYPERHDR_MANUFACTURER_NAME, 43 | HYPERHDR_MODEL_NAME, 44 | NAME_SUFFIX_HYPERHDR_LIGHT, 45 | NAME_SUFFIX_HYPERHDR_PRIORITY_LIGHT, 46 | SIGNAL_ENTITY_REMOVE, 47 | TYPE_HYPERHDR_LIGHT, 48 | TYPE_HYPERHDR_PRIORITY_LIGHT, 49 | ) 50 | 51 | _LOGGER = logging.getLogger(__name__) 52 | 53 | COLOR_BLACK = color_util.COLORS["black"] 54 | 55 | CONF_DEFAULT_COLOR = "default_color" 56 | CONF_HDMI_PRIORITY = "hdmi_priority" 57 | CONF_EFFECT_LIST = "effect_list" 58 | 59 | # As we want to preserve brightness control for effects (e.g. to reduce the 60 | # brightness for VIDEOGRABBER), we need to persist the effect that is in flight, so 61 | # subsequent calls to turn_on will know the keep the effect enabled. 62 | # Unfortunately the Home Assistant UI does not easily expose a way to remove a 63 | # selected effect (there is no 'No Effect' option by default). Instead, we 64 | # create a new fake effect ("Solid") that is always selected by default for 65 | # showing a solid color. This is the same method used by WLED. 66 | KEY_EFFECT_SOLID = "Solid" 67 | 68 | DEFAULT_COLOR = [255, 255, 255] 69 | DEFAULT_BRIGHTNESS = 255 70 | DEFAULT_EFFECT = KEY_EFFECT_SOLID 71 | DEFAULT_NAME = "HyperHDR" 72 | DEFAULT_PORT = const.DEFAULT_PORT_JSON 73 | DEFAULT_HDMI_PRIORITY = 880 74 | DEFAULT_EFFECT_LIST: list[str] = [] 75 | 76 | ICON_LIGHTBULB = "mdi:lightbulb" 77 | ICON_EFFECT = "mdi:lava-lamp" 78 | ICON_EXTERNAL_SOURCE = "mdi:television-ambient-light" 79 | 80 | 81 | async def async_setup_entry( 82 | hass: HomeAssistant, 83 | config_entry: ConfigEntry, 84 | async_add_entities: AddEntitiesCallback, 85 | ) -> None: 86 | """Set up a HyperHDR platform from config entry.""" 87 | 88 | entry_data = hass.data[DOMAIN][config_entry.entry_id] 89 | server_id = config_entry.unique_id 90 | 91 | @callback 92 | def instance_add(instance_num: int, instance_name: str) -> None: 93 | """Add entities for a new HyperHDR instance.""" 94 | assert server_id 95 | args = ( 96 | server_id, 97 | instance_num, 98 | instance_name, 99 | config_entry.options, 100 | entry_data[CONF_INSTANCE_CLIENTS][instance_num], 101 | ) 102 | async_add_entities( 103 | [ 104 | HyperHDRLight(*args), 105 | HyperHDRPriorityLight(*args), 106 | ] 107 | ) 108 | 109 | @callback 110 | def instance_remove(instance_num: int) -> None: 111 | """Remove entities for an old HyperHDR instance.""" 112 | assert server_id 113 | for light_type in LIGHT_TYPES: 114 | async_dispatcher_send( 115 | hass, 116 | SIGNAL_ENTITY_REMOVE.format( 117 | get_hyperhdr_unique_id(server_id, instance_num, light_type) 118 | ), 119 | ) 120 | 121 | listen_for_instance_updates(hass, config_entry, instance_add, instance_remove) 122 | 123 | 124 | class HyperHDRBaseLight(LightEntity): 125 | """A HyperHDR light base class.""" 126 | 127 | _attr_color_mode = ColorMode.HS 128 | _attr_supported_color_modes = {ColorMode.HS} 129 | _attr_supported_features = LightEntityFeature.EFFECT 130 | 131 | def __init__( 132 | self, 133 | server_id: str, 134 | instance_num: int, 135 | instance_name: str, 136 | options: MappingProxyType[str, Any], 137 | hyperhdr_client: client.HyperHDRClient, 138 | ) -> None: 139 | """Initialize the light.""" 140 | self._unique_id = self._compute_unique_id(server_id, instance_num) 141 | self._name = self._compute_name(instance_name) 142 | self._device_id = get_hyperhdr_device_id(server_id, instance_num) 143 | self._instance_name = instance_name 144 | self._options = options 145 | self._client = hyperhdr_client 146 | 147 | # Active state representing the HyperHDR instance. 148 | self._brightness: int = 255 149 | self._rgb_color: Sequence[int] = DEFAULT_COLOR 150 | self._effect: str = KEY_EFFECT_SOLID 151 | 152 | self._static_effect_list: list[str] = [KEY_EFFECT_SOLID] 153 | if self._support_external_effects: 154 | self._static_effect_list += [ 155 | const.KEY_COMPONENTID_TO_NAME[component] 156 | for component in const.KEY_COMPONENTID_EXTERNAL_SOURCES 157 | ] 158 | self._effect_list: list[str] = self._static_effect_list[:] 159 | 160 | self._client_callbacks: Mapping[str, Callable[[dict[str, Any]], None]] = { 161 | f"{const.KEY_ADJUSTMENT}-{const.KEY_UPDATE}": self._update_adjustment, 162 | f"{const.KEY_COMPONENTS}-{const.KEY_UPDATE}": self._update_components, 163 | f"{const.KEY_EFFECTS}-{const.KEY_UPDATE}": self._update_effect_list, 164 | f"{const.KEY_PRIORITIES}-{const.KEY_UPDATE}": self._update_priorities, 165 | f"{const.KEY_CLIENT}-{const.KEY_UPDATE}": self._update_client, 166 | } 167 | 168 | def _compute_unique_id(self, server_id: str, instance_num: int) -> str: 169 | """Compute a unique id for this instance.""" 170 | raise NotImplementedError 171 | 172 | def _compute_name(self, instance_name: str) -> str: 173 | """Compute the name of the light.""" 174 | raise NotImplementedError 175 | 176 | @property 177 | def entity_registry_enabled_default(self) -> bool: 178 | """Whether or not the entity is enabled by default.""" 179 | return True 180 | 181 | @property 182 | def should_poll(self) -> bool: 183 | """Return whether or not this entity should be polled.""" 184 | return False 185 | 186 | @property 187 | def name(self) -> str: 188 | """Return the name of the light.""" 189 | return self._name 190 | 191 | @property 192 | def brightness(self) -> int: 193 | """Return the brightness of this light between 0..255.""" 194 | return self._brightness 195 | 196 | @property 197 | def hs_color(self) -> tuple[float, float]: 198 | """Return last color value set.""" 199 | return color_util.color_RGB_to_hs(*self._rgb_color) 200 | 201 | @property 202 | def icon(self) -> str: 203 | """Return state specific icon.""" 204 | if self.is_on: 205 | if ( 206 | self.effect in const.KEY_COMPONENTID_FROM_NAME 207 | and const.KEY_COMPONENTID_FROM_NAME[self.effect] 208 | in const.KEY_COMPONENTID_EXTERNAL_SOURCES 209 | ): 210 | return ICON_EXTERNAL_SOURCE 211 | if self.effect != KEY_EFFECT_SOLID: 212 | return ICON_EFFECT 213 | return ICON_LIGHTBULB 214 | 215 | @property 216 | def effect(self) -> str: 217 | """Return the current effect.""" 218 | return self._effect 219 | 220 | @property 221 | def effect_list(self) -> list[str]: 222 | """Return the list of supported effects.""" 223 | return self._effect_list 224 | 225 | @property 226 | def available(self) -> bool: 227 | """Return server availability.""" 228 | return bool(self._client.has_loaded_state) 229 | 230 | @property 231 | def unique_id(self) -> str: 232 | """Return a unique id for this instance.""" 233 | return self._unique_id 234 | 235 | @property 236 | def device_info(self) -> DeviceInfo: 237 | """Return device information.""" 238 | return DeviceInfo( 239 | identifiers={(DOMAIN, self._device_id)}, 240 | manufacturer=HYPERHDR_MANUFACTURER_NAME, 241 | model=HYPERHDR_MODEL_NAME, 242 | name=self._instance_name, 243 | configuration_url=self._client.remote_url, 244 | ) 245 | 246 | def _get_option(self, key: str) -> Any: 247 | """Get a value from the provided options.""" 248 | defaults = { 249 | CONF_PRIORITY: DEFAULT_PRIORITY, 250 | CONF_EFFECT_HIDE_LIST: [], 251 | } 252 | return self._options.get(key, defaults[key]) 253 | 254 | async def async_turn_on(self, **kwargs: Any) -> None: 255 | """Turn on the light.""" 256 | # == Get key parameters == 257 | if ATTR_EFFECT not in kwargs and ATTR_HS_COLOR in kwargs: 258 | effect = KEY_EFFECT_SOLID 259 | else: 260 | effect = kwargs.get(ATTR_EFFECT, self._effect) 261 | rgb_color: Sequence[int] 262 | if ATTR_HS_COLOR in kwargs: 263 | rgb_color = color_util.color_hs_to_RGB(*kwargs[ATTR_HS_COLOR]) 264 | else: 265 | rgb_color = self._rgb_color 266 | 267 | # == Set brightness == 268 | if ATTR_BRIGHTNESS in kwargs: 269 | brightness = kwargs[ATTR_BRIGHTNESS] 270 | for item in self._client.adjustment or []: 271 | if ( 272 | const.KEY_ID in item 273 | and not await self._client.async_send_set_adjustment( 274 | **{ 275 | const.KEY_ADJUSTMENT: { 276 | const.KEY_BRIGHTNESS: int( 277 | round((float(brightness) * 100) / 255) 278 | ), 279 | const.KEY_ID: item[const.KEY_ID], 280 | } 281 | } 282 | ) 283 | ): 284 | return 285 | 286 | # == Set an external source 287 | if ( 288 | effect 289 | and self._support_external_effects 290 | and ( 291 | effect in const.KEY_COMPONENTID_EXTERNAL_SOURCES 292 | or effect in const.KEY_COMPONENTID_FROM_NAME 293 | ) 294 | ): 295 | if effect in const.KEY_COMPONENTID_FROM_NAME: 296 | component = const.KEY_COMPONENTID_FROM_NAME[effect] 297 | else: 298 | _LOGGER.warning( 299 | "Use of HyperHDR effect '%s' is deprecated and will be removed " 300 | "in a future release. Please use '%s' instead", 301 | effect, 302 | const.KEY_COMPONENTID_TO_NAME[effect], 303 | ) 304 | component = effect 305 | 306 | # Clear any color/effect. 307 | if not await self._client.async_send_clear( 308 | **{const.KEY_PRIORITY: self._get_option(CONF_PRIORITY)} 309 | ): 310 | return 311 | 312 | # Turn off all external sources, except the intended. 313 | for key in const.KEY_COMPONENTID_EXTERNAL_SOURCES: 314 | if not await self._client.async_send_set_component( 315 | **{ 316 | const.KEY_COMPONENTSTATE: { 317 | const.KEY_COMPONENT: key, 318 | const.KEY_STATE: component == key, 319 | } 320 | } 321 | ): 322 | return 323 | 324 | # == Set an effect 325 | elif effect and effect != KEY_EFFECT_SOLID: 326 | # This call should not be necessary, but without it there is no priorities-update issued: 327 | # https://github.com/hyperhdr-project/hyperhdr.ng/issues/992 328 | if not await self._client.async_send_clear( 329 | **{const.KEY_PRIORITY: self._get_option(CONF_PRIORITY)} 330 | ): 331 | return 332 | 333 | if not await self._client.async_send_set_effect( 334 | **{ 335 | const.KEY_PRIORITY: self._get_option(CONF_PRIORITY), 336 | const.KEY_EFFECT: {const.KEY_NAME: effect}, 337 | const.KEY_ORIGIN: DEFAULT_ORIGIN, 338 | } 339 | ): 340 | return 341 | # == Set a color 342 | else: 343 | if not await self._client.async_send_set_color( 344 | **{ 345 | const.KEY_PRIORITY: self._get_option(CONF_PRIORITY), 346 | const.KEY_COLOR: rgb_color, 347 | const.KEY_ORIGIN: DEFAULT_ORIGIN, 348 | } 349 | ): 350 | return 351 | 352 | def _set_internal_state( 353 | self, 354 | brightness: int | None = None, 355 | rgb_color: Sequence[int] | None = None, 356 | effect: str | None = None, 357 | ) -> None: 358 | """Set the internal state.""" 359 | if brightness is not None: 360 | self._brightness = brightness 361 | if rgb_color is not None: 362 | self._rgb_color = rgb_color 363 | if effect is not None: 364 | self._effect = effect 365 | 366 | @callback 367 | def _update_components(self, _: dict[str, Any] | None = None) -> None: 368 | """Update HyperHDR components.""" 369 | self.async_write_ha_state() 370 | 371 | @callback 372 | def _update_adjustment(self, _: dict[str, Any] | None = None) -> None: 373 | """Update HyperHDR adjustments.""" 374 | if self._client.adjustment: 375 | brightness_pct = self._client.adjustment[0].get( 376 | const.KEY_BRIGHTNESS, DEFAULT_BRIGHTNESS 377 | ) 378 | if brightness_pct < 0 or brightness_pct > 100: 379 | return 380 | self._set_internal_state( 381 | brightness=int(round((brightness_pct * 255) / float(100))) 382 | ) 383 | self.async_write_ha_state() 384 | 385 | @callback 386 | def _update_priorities(self, _: dict[str, Any] | None = None) -> None: 387 | """Update HyperHDR priorities.""" 388 | priority = self._get_priority_entry_that_dictates_state() 389 | if priority and self._allow_priority_update(priority): 390 | componentid = priority.get(const.KEY_COMPONENTID) 391 | if ( 392 | self._support_external_effects 393 | and componentid in const.KEY_COMPONENTID_EXTERNAL_SOURCES 394 | and componentid in const.KEY_COMPONENTID_TO_NAME 395 | ): 396 | self._set_internal_state( 397 | rgb_color=DEFAULT_COLOR, 398 | effect=const.KEY_COMPONENTID_TO_NAME[componentid], 399 | ) 400 | elif componentid == const.KEY_COMPONENTID_EFFECT: 401 | # Owner is the effect name. 402 | # See: https://docs.hyperhdr-project.org/en/json/ServerInfo.html#priorities 403 | self._set_internal_state( 404 | rgb_color=DEFAULT_COLOR, effect=priority[const.KEY_OWNER] 405 | ) 406 | elif componentid == const.KEY_COMPONENTID_COLOR: 407 | self._set_internal_state( 408 | rgb_color=priority[const.KEY_VALUE][const.KEY_RGB], 409 | effect=KEY_EFFECT_SOLID, 410 | ) 411 | self.async_write_ha_state() 412 | 413 | @callback 414 | def _update_effect_list(self, _: dict[str, Any] | None = None) -> None: 415 | """Update HyperHDR effects.""" 416 | if not self._client.effects: 417 | return 418 | effect_list: list[str] = [] 419 | hide_effects = self._get_option(CONF_EFFECT_HIDE_LIST) 420 | 421 | for effect in self._client.effects or []: 422 | if const.KEY_NAME in effect: 423 | effect_name = effect[const.KEY_NAME] 424 | if effect_name not in hide_effects: 425 | effect_list.append(effect_name) 426 | 427 | self._effect_list = [ 428 | effect for effect in self._static_effect_list if effect not in hide_effects 429 | ] + effect_list 430 | self.async_write_ha_state() 431 | 432 | @callback 433 | def _update_full_state(self) -> None: 434 | """Update full HyperHDR state.""" 435 | self._update_adjustment() 436 | self._update_priorities() 437 | self._update_effect_list() 438 | 439 | _LOGGER.debug( 440 | "HyperHDR full state update: On=%s,Brightness=%i,Effect=%s " 441 | "(%i effects total),Color=%s", 442 | self.is_on, 443 | self._brightness, 444 | self._effect, 445 | len(self._effect_list), 446 | self._rgb_color, 447 | ) 448 | 449 | @callback 450 | def _update_client(self, _: dict[str, Any] | None = None) -> None: 451 | """Update client connection state.""" 452 | self.async_write_ha_state() 453 | 454 | async def async_added_to_hass(self) -> None: 455 | """Register callbacks when entity added to hass.""" 456 | self.async_on_remove( 457 | async_dispatcher_connect( 458 | self.hass, 459 | SIGNAL_ENTITY_REMOVE.format(self.unique_id), 460 | functools.partial(self.async_remove, force_remove=True), 461 | ) 462 | ) 463 | 464 | self._client.add_callbacks(self._client_callbacks) 465 | 466 | # Load initial state. 467 | self._update_full_state() 468 | 469 | async def async_will_remove_from_hass(self) -> None: 470 | """Cleanup prior to hass removal.""" 471 | self._client.remove_callbacks(self._client_callbacks) 472 | 473 | @property 474 | def _support_external_effects(self) -> bool: 475 | """Whether or not to support setting external effects from the light entity.""" 476 | return True 477 | 478 | def _get_priority_entry_that_dictates_state(self) -> dict[str, Any] | None: 479 | """Get the relevant HyperHDR priority entry to consider.""" 480 | # Return the visible priority (whether or not it is the HA priority). 481 | 482 | # Explicit type specifier to ensure this works when the underlying (typed) 483 | # library is installed along with the tests. Casts would trigger a 484 | # redundant-cast warning in this case. 485 | priority: dict[str, Any] | None = self._client.visible_priority 486 | return priority 487 | 488 | def _allow_priority_update(self, priority: dict[str, Any] | None = None) -> bool: 489 | """Determine whether to allow a priority to update internal state.""" 490 | return True 491 | 492 | 493 | class HyperHDRLight(HyperHDRBaseLight): 494 | """A HyperHDR light that acts in absolute (vs priority) manner. 495 | 496 | Light state is the absolute HyperHDR component state (e.g. LED device on/off) rather 497 | than color based at a particular priority, and the 'winning' priority determines 498 | shown state rather than exclusively the HA priority. 499 | """ 500 | 501 | def _compute_unique_id(self, server_id: str, instance_num: int) -> str: 502 | """Compute a unique id for this instance.""" 503 | return get_hyperhdr_unique_id(server_id, instance_num, TYPE_HYPERHDR_LIGHT) 504 | 505 | def _compute_name(self, instance_name: str) -> str: 506 | """Compute the name of the light.""" 507 | return f"{instance_name} {NAME_SUFFIX_HYPERHDR_LIGHT}".strip() 508 | 509 | @property 510 | def is_on(self) -> bool: 511 | """Return true if light is on.""" 512 | return ( 513 | bool(self._client.is_on()) 514 | and self._get_priority_entry_that_dictates_state() is not None 515 | ) 516 | 517 | async def async_turn_on(self, **kwargs: Any) -> None: 518 | """Turn on the light.""" 519 | # == Turn device on == 520 | # Turn on both ALL (HyperHDR itself) and LEDDEVICE. It would be 521 | # preferable to enable LEDDEVICE after the settings (e.g. brightness, 522 | # color, effect), but this is not possible due to: 523 | # https://github.com/hyperion-project/hyperion.ng/issues/967 524 | if not bool(self._client.is_on()): 525 | for component in ( 526 | const.KEY_COMPONENTID_ALL, 527 | const.KEY_COMPONENTID_LEDDEVICE, 528 | ): 529 | if not await self._client.async_send_set_component( 530 | **{ 531 | const.KEY_COMPONENTSTATE: { 532 | const.KEY_COMPONENT: component, 533 | const.KEY_STATE: True, 534 | } 535 | } 536 | ): 537 | return 538 | 539 | # Turn on the relevant HyperHDR priority as usual. 540 | await super().async_turn_on(**kwargs) 541 | 542 | async def async_turn_off(self, **kwargs: Any) -> None: 543 | """Turn off the light.""" 544 | if not await self._client.async_send_set_component( 545 | **{ 546 | const.KEY_COMPONENTSTATE: { 547 | const.KEY_COMPONENT: const.KEY_COMPONENTID_LEDDEVICE, 548 | const.KEY_STATE: False, 549 | } 550 | } 551 | ): 552 | return 553 | 554 | 555 | class HyperHDRPriorityLight(HyperHDRBaseLight): 556 | """A HyperHDR light that only acts on a single HyperHDR priority.""" 557 | 558 | def _compute_unique_id(self, server_id: str, instance_num: int) -> str: 559 | """Compute a unique id for this instance.""" 560 | return get_hyperhdr_unique_id( 561 | server_id, instance_num, TYPE_HYPERHDR_PRIORITY_LIGHT 562 | ) 563 | 564 | def _compute_name(self, instance_name: str) -> str: 565 | """Compute the name of the light.""" 566 | return f"{instance_name} {NAME_SUFFIX_HYPERHDR_PRIORITY_LIGHT}".strip() 567 | 568 | @property 569 | def entity_registry_enabled_default(self) -> bool: 570 | """Whether or not the entity is enabled by default.""" 571 | return False 572 | 573 | @property 574 | def is_on(self) -> bool: 575 | """Return true if light is on.""" 576 | priority = self._get_priority_entry_that_dictates_state() 577 | return ( 578 | priority is not None 579 | and not HyperHDRPriorityLight._is_priority_entry_black(priority) 580 | ) 581 | 582 | async def async_turn_off(self, **kwargs: Any) -> None: 583 | """Turn off the light.""" 584 | if not await self._client.async_send_clear( 585 | **{const.KEY_PRIORITY: self._get_option(CONF_PRIORITY)} 586 | ): 587 | return 588 | await self._client.async_send_set_color( 589 | **{ 590 | const.KEY_PRIORITY: self._get_option(CONF_PRIORITY), 591 | const.KEY_COLOR: COLOR_BLACK, 592 | const.KEY_ORIGIN: DEFAULT_ORIGIN, 593 | } 594 | ) 595 | 596 | @property 597 | def _support_external_effects(self) -> bool: 598 | """Whether or not to support setting external effects from the light entity.""" 599 | return False 600 | 601 | def _get_priority_entry_that_dictates_state(self) -> dict[str, Any] | None: 602 | """Get the relevant HyperHDR priority entry to consider.""" 603 | # Return the active priority (if any) at the configured HA priority. 604 | for candidate in self._client.priorities or []: 605 | if const.KEY_PRIORITY not in candidate: 606 | continue 607 | if candidate[const.KEY_PRIORITY] == self._get_option( 608 | CONF_PRIORITY 609 | ) and candidate.get(const.KEY_ACTIVE, False): 610 | # Explicit type specifier to ensure this works when the underlying 611 | # (typed) library is installed along with the tests. Casts would trigger 612 | # a redundant-cast warning in this case. 613 | output: dict[str, Any] = candidate 614 | return output 615 | return None 616 | 617 | @classmethod 618 | def _is_priority_entry_black(cls, priority: dict[str, Any] | None) -> bool: 619 | """Determine if a given priority entry is the color black.""" 620 | if ( 621 | priority 622 | and priority.get(const.KEY_COMPONENTID) == const.KEY_COMPONENTID_COLOR 623 | ): 624 | rgb_color = priority.get(const.KEY_VALUE, {}).get(const.KEY_RGB) 625 | if rgb_color is not None and tuple(rgb_color) == COLOR_BLACK: 626 | return True 627 | return False 628 | 629 | def _allow_priority_update(self, priority: dict[str, Any] | None = None) -> bool: 630 | """Determine whether to allow a HyperHDR priority to update entity attributes.""" 631 | # Black is treated as 'off' (and Home Assistant does not support selecting black 632 | # from the color selector). Do not set our internal attributes if the priority is 633 | # 'off' (i.e. if black is active). Do this to ensure it seamlessly turns back on 634 | # at the correct prior color on the next 'on' call. 635 | return not HyperHDRPriorityLight._is_priority_entry_black(priority) 636 | 637 | 638 | LIGHT_TYPES = { 639 | TYPE_HYPERHDR_LIGHT: HyperHDRLight, 640 | TYPE_HYPERHDR_PRIORITY_LIGHT: HyperHDRPriorityLight, 641 | } 642 | -------------------------------------------------------------------------------- /custom_components/hyperhdr/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain": "hyperhdr", 3 | "name": "HyperHDR", 4 | "codeowners": ["@mjoshd"], 5 | "config_flow": true, 6 | "documentation": "https://github.com/awawa-dev/HyperHDR#readme", 7 | "iot_class": "local_push", 8 | "loggers": ["hyperhdr"], 9 | "requirements": ["hyperhdr-py==0.0.3"], 10 | "ssdp": [ 11 | { 12 | "manufacturer": "HyperHDR Open Source Ambient Lighting", 13 | "st": "urn:github.com/awawa-dev/HyperHDR:device:basic:1" 14 | } 15 | ], 16 | "issue_tracker": "https://github.com/mjoshd/hyperhdr-ha/issues", 17 | "version": "0.0.7" 18 | } 19 | -------------------------------------------------------------------------------- /custom_components/hyperhdr/sensor.py: -------------------------------------------------------------------------------- 1 | """Sensor platform for HyperHDR.""" 2 | 3 | from __future__ import annotations 4 | 5 | import functools 6 | from typing import Any 7 | 8 | from hyperhdr import client 9 | from hyperhdr.const import ( 10 | KEY_COMPONENTID, 11 | KEY_ORIGIN, 12 | KEY_OWNER, 13 | KEY_PRIORITIES, 14 | KEY_PRIORITY, 15 | KEY_RGB, 16 | KEY_UPDATE, 17 | KEY_VALUE, 18 | KEY_VISIBLE, 19 | ) 20 | 21 | from homeassistant.components.sensor import SensorEntity, SensorEntityDescription 22 | from homeassistant.config_entries import ConfigEntry 23 | from homeassistant.core import HomeAssistant, callback 24 | from homeassistant.helpers.device_registry import DeviceInfo 25 | from homeassistant.helpers.dispatcher import ( 26 | async_dispatcher_connect, 27 | async_dispatcher_send, 28 | ) 29 | from homeassistant.helpers.entity_platform import AddEntitiesCallback 30 | 31 | from . import ( 32 | get_hyperhdr_device_id, 33 | get_hyperhdr_unique_id, 34 | listen_for_instance_updates, 35 | ) 36 | from .const import ( 37 | CONF_INSTANCE_CLIENTS, 38 | DOMAIN, 39 | HYPERHDR_MANUFACTURER_NAME, 40 | HYPERHDR_MODEL_NAME, 41 | SIGNAL_ENTITY_REMOVE, 42 | TYPE_HYPERHDR_SENSOR_BASE, 43 | TYPE_HYPERHDR_SENSOR_VISIBLE_PRIORITY, 44 | ) 45 | 46 | SENSORS = [TYPE_HYPERHDR_SENSOR_VISIBLE_PRIORITY] 47 | PRIORITY_SENSOR_DESCRIPTION = SensorEntityDescription( 48 | key="visible_priority", 49 | translation_key="visible_priority", 50 | icon="mdi:lava-lamp", 51 | ) 52 | 53 | 54 | def _sensor_unique_id(server_id: str, instance_num: int, suffix: str) -> str: 55 | """Calculate a sensor's unique_id.""" 56 | return get_hyperhdr_unique_id( 57 | server_id, 58 | instance_num, 59 | f"{TYPE_HYPERHDR_SENSOR_BASE}_{suffix}", 60 | ) 61 | 62 | 63 | async def async_setup_entry( 64 | hass: HomeAssistant, 65 | config_entry: ConfigEntry, 66 | async_add_entities: AddEntitiesCallback, 67 | ) -> None: 68 | """Set up a HyperHDR platform from config entry.""" 69 | entry_data = hass.data[DOMAIN][config_entry.entry_id] 70 | server_id = config_entry.unique_id 71 | 72 | @callback 73 | def instance_add(instance_num: int, instance_name: str) -> None: 74 | """Add entities for a new HyperHDR instance.""" 75 | assert server_id 76 | sensors = [ 77 | HyperHDRVisiblePrioritySensor( 78 | server_id, 79 | instance_num, 80 | instance_name, 81 | entry_data[CONF_INSTANCE_CLIENTS][instance_num], 82 | PRIORITY_SENSOR_DESCRIPTION, 83 | ) 84 | ] 85 | 86 | async_add_entities(sensors) 87 | 88 | @callback 89 | def instance_remove(instance_num: int) -> None: 90 | """Remove entities for an old HyperHDR instance.""" 91 | assert server_id 92 | 93 | for sensor in SENSORS: 94 | async_dispatcher_send( 95 | hass, 96 | SIGNAL_ENTITY_REMOVE.format( 97 | _sensor_unique_id(server_id, instance_num, sensor), 98 | ), 99 | ) 100 | 101 | listen_for_instance_updates(hass, config_entry, instance_add, instance_remove) 102 | 103 | 104 | class HyperHDRSensor(SensorEntity): 105 | """Sensor class.""" 106 | 107 | _attr_has_entity_name = True 108 | _attr_should_poll = False 109 | 110 | def __init__( 111 | self, 112 | server_id: str, 113 | instance_num: int, 114 | instance_name: str, 115 | hyperhdr_client: client.HyperHDRClient, 116 | entity_description: SensorEntityDescription, 117 | ) -> None: 118 | """Initialize the sensor.""" 119 | self.entity_description = entity_description 120 | self._client = hyperhdr_client 121 | self._attr_native_value = None 122 | self._client_callbacks: dict[str, Any] = {} 123 | 124 | device_id = get_hyperhdr_device_id(server_id, instance_num) 125 | 126 | self._attr_device_info = DeviceInfo( 127 | identifiers={(DOMAIN, device_id)}, 128 | manufacturer=HYPERHDR_MANUFACTURER_NAME, 129 | model=HYPERHDR_MODEL_NAME, 130 | name=instance_name, 131 | configuration_url=self._client.remote_url, 132 | ) 133 | 134 | @property 135 | def available(self) -> bool: 136 | """Return server availability.""" 137 | return bool(self._client.has_loaded_state) 138 | 139 | async def async_added_to_hass(self) -> None: 140 | """Register callbacks when entity added to hass.""" 141 | self.async_on_remove( 142 | async_dispatcher_connect( 143 | self.hass, 144 | SIGNAL_ENTITY_REMOVE.format(self._attr_unique_id), 145 | functools.partial(self.async_remove, force_remove=True), 146 | ) 147 | ) 148 | 149 | self._client.add_callbacks(self._client_callbacks) 150 | 151 | async def async_will_remove_from_hass(self) -> None: 152 | """Cleanup prior to hass removal.""" 153 | self._client.remove_callbacks(self._client_callbacks) 154 | 155 | 156 | class HyperHDRVisiblePrioritySensor(HyperHDRSensor): 157 | """Class that displays the visible priority of a HyperHDR instance.""" 158 | 159 | def __init__( 160 | self, 161 | server_id: str, 162 | instance_num: int, 163 | instance_name: str, 164 | hyperhdr_client: client.HyperHDRClient, 165 | entity_description: SensorEntityDescription, 166 | ) -> None: 167 | """Initialize the sensor.""" 168 | 169 | super().__init__( 170 | server_id, instance_num, instance_name, hyperhdr_client, entity_description 171 | ) 172 | 173 | self._attr_unique_id = _sensor_unique_id( 174 | server_id, instance_num, TYPE_HYPERHDR_SENSOR_VISIBLE_PRIORITY 175 | ) 176 | 177 | self._client_callbacks = { 178 | f"{KEY_PRIORITIES}-{KEY_UPDATE}": self._update_priorities 179 | } 180 | 181 | @callback 182 | def _update_priorities(self, _: dict[str, Any] | None = None) -> None: 183 | """Update HyperHDR priorities.""" 184 | state_value = None 185 | attrs = {} 186 | 187 | for priority in self._client.priorities or []: 188 | if not (KEY_VISIBLE in priority and priority[KEY_VISIBLE] is True): 189 | continue 190 | 191 | if priority[KEY_COMPONENTID] == "COLOR": 192 | state_value = priority[KEY_VALUE][KEY_RGB] 193 | else: 194 | state_value = priority.get(KEY_OWNER) 195 | 196 | attrs = { 197 | "component_id": priority[KEY_COMPONENTID], 198 | "origin": priority[KEY_ORIGIN], 199 | "priority": priority[KEY_PRIORITY], 200 | "owner": priority.get(KEY_OWNER), 201 | } 202 | 203 | if priority[KEY_COMPONENTID] == "COLOR": 204 | attrs["color"] = priority[KEY_VALUE] 205 | else: 206 | attrs["color"] = None 207 | 208 | self._attr_native_value = state_value 209 | self._attr_extra_state_attributes = attrs 210 | 211 | self.async_write_ha_state() 212 | -------------------------------------------------------------------------------- /custom_components/hyperhdr/switch.py: -------------------------------------------------------------------------------- 1 | """Switch platform for HyperHDR.""" 2 | 3 | from __future__ import annotations 4 | 5 | import functools 6 | from typing import Any 7 | 8 | from hyperhdr import client 9 | from hyperhdr.const import ( 10 | KEY_COMPONENT, 11 | KEY_COMPONENTID_ALL, 12 | KEY_COMPONENTID_BLACKBORDER, 13 | KEY_COMPONENTID_BOBLIGHTSERVER, 14 | KEY_COMPONENTID_FORWARDER, 15 | KEY_COMPONENTID_SYSTEMGRABBER, 16 | KEY_COMPONENTID_LEDDEVICE, 17 | KEY_COMPONENTID_SMOOTHING, 18 | KEY_COMPONENTID_HDR, 19 | KEY_COMPONENTID_TO_NAME, 20 | KEY_COMPONENTID_VIDEOGRABBER, 21 | KEY_COMPONENTS, 22 | KEY_COMPONENTSTATE, 23 | KEY_ENABLED, 24 | KEY_NAME, 25 | KEY_STATE, 26 | KEY_UPDATE, 27 | ) 28 | 29 | from homeassistant.components.switch import SwitchEntity 30 | from homeassistant.config_entries import ConfigEntry 31 | from homeassistant.const import EntityCategory 32 | from homeassistant.core import HomeAssistant, callback 33 | from homeassistant.helpers.device_registry import DeviceInfo 34 | from homeassistant.helpers.dispatcher import ( 35 | async_dispatcher_connect, 36 | async_dispatcher_send, 37 | ) 38 | from homeassistant.helpers.entity import DeviceInfo, EntityCategory 39 | from homeassistant.helpers.entity_platform import AddEntitiesCallback 40 | from homeassistant.util import slugify 41 | 42 | from . import ( 43 | get_hyperhdr_device_id, 44 | get_hyperhdr_unique_id, 45 | listen_for_instance_updates, 46 | ) 47 | from .const import ( 48 | CONF_INSTANCE_CLIENTS, 49 | DOMAIN, 50 | HYPERHDR_MANUFACTURER_NAME, 51 | HYPERHDR_MODEL_NAME, 52 | SIGNAL_ENTITY_REMOVE, 53 | TYPE_HYPERHDR_COMPONENT_SWITCH_BASE, 54 | ) 55 | 56 | COMPONENT_SWITCHES = [ 57 | KEY_COMPONENTID_ALL, 58 | KEY_COMPONENTID_SMOOTHING, 59 | KEY_COMPONENTID_BLACKBORDER, 60 | KEY_COMPONENTID_FORWARDER, 61 | KEY_COMPONENTID_BOBLIGHTSERVER, 62 | KEY_COMPONENTID_SYSTEMGRABBER, 63 | KEY_COMPONENTID_LEDDEVICE, 64 | KEY_COMPONENTID_VIDEOGRABBER, 65 | KEY_COMPONENTID_HDR, 66 | ] 67 | 68 | 69 | def _component_to_unique_id(server_id: str, component: str, instance_num: int) -> str: 70 | """Convert a component to a unique_id.""" 71 | return get_hyperhdr_unique_id( 72 | server_id, 73 | instance_num, 74 | slugify( 75 | f"{TYPE_HYPERHDR_COMPONENT_SWITCH_BASE} {KEY_COMPONENTID_TO_NAME[component]}" 76 | ), 77 | ) 78 | 79 | 80 | def _component_to_translation_key(component: str) -> str: 81 | return { 82 | KEY_COMPONENTID_ALL: "all", 83 | KEY_COMPONENTID_SMOOTHING: "smoothing", 84 | KEY_COMPONENTID_BLACKBORDER: "blackbar_detection", 85 | KEY_COMPONENTID_FORWARDER: "forwarder", 86 | KEY_COMPONENTID_BOBLIGHTSERVER: "boblight_server", 87 | KEY_COMPONENTID_SYSTEMGRABBER: "platform_capture", 88 | KEY_COMPONENTID_LEDDEVICE: "led_device", 89 | KEY_COMPONENTID_VIDEOGRABBER: "usb_capture", 90 | KEY_COMPONENTID_HDR: "hdr_tone_mapping", 91 | }[component] 92 | 93 | 94 | async def async_setup_entry( 95 | hass: HomeAssistant, 96 | config_entry: ConfigEntry, 97 | async_add_entities: AddEntitiesCallback, 98 | ) -> None: 99 | """Set up a HyperHDR platform from config entry.""" 100 | entry_data = hass.data[DOMAIN][config_entry.entry_id] 101 | server_id = config_entry.unique_id 102 | 103 | @callback 104 | def instance_add(instance_num: int, instance_name: str) -> None: 105 | """Add entities for a new HyperHDR instance.""" 106 | assert server_id 107 | async_add_entities( 108 | HyperHDRComponentSwitch( 109 | server_id, 110 | instance_num, 111 | instance_name, 112 | component, 113 | entry_data[CONF_INSTANCE_CLIENTS][instance_num], 114 | ) 115 | for component in COMPONENT_SWITCHES 116 | ) 117 | 118 | @callback 119 | def instance_remove(instance_num: int) -> None: 120 | """Remove entities for an old HyperHDR instance.""" 121 | assert server_id 122 | for component in COMPONENT_SWITCHES: 123 | async_dispatcher_send( 124 | hass, 125 | SIGNAL_ENTITY_REMOVE.format( 126 | _component_to_unique_id(server_id, component, instance_num), 127 | ), 128 | ) 129 | 130 | listen_for_instance_updates(hass, config_entry, instance_add, instance_remove) 131 | 132 | 133 | class HyperHDRComponentSwitch(SwitchEntity): 134 | """ComponentBinarySwitch switch class.""" 135 | 136 | _attr_entity_category = EntityCategory.CONFIG 137 | _attr_should_poll = False 138 | _attr_has_entity_name = True 139 | # These component controls are for advanced users and are disabled by default. 140 | _attr_entity_registry_enabled_default = False 141 | 142 | def __init__( 143 | self, 144 | server_id: str, 145 | instance_num: int, 146 | instance_name: str, 147 | component_name: str, 148 | hyperhdr_client: client.HyperHDRClient, 149 | ) -> None: 150 | """Initialize the switch.""" 151 | self._attr_unique_id = _component_to_unique_id( 152 | server_id, component_name, instance_num 153 | ) 154 | self._device_id = get_hyperhdr_device_id(server_id, instance_num) 155 | self._attr_translation_key = _component_to_translation_key(component_name) 156 | self._instance_name = instance_name 157 | self._component_name = component_name 158 | self._client = hyperhdr_client 159 | self._client_callbacks = { 160 | f"{KEY_COMPONENTS}-{KEY_UPDATE}": self._update_components 161 | } 162 | self._attr_device_info = DeviceInfo( 163 | identifiers={(DOMAIN, self._device_id)}, 164 | manufacturer=HYPERHDR_MANUFACTURER_NAME, 165 | model=HYPERHDR_MODEL_NAME, 166 | name=self._instance_name, 167 | configuration_url=self._client.remote_url, 168 | ) 169 | 170 | @property 171 | def is_on(self) -> bool: 172 | """Return true if the switch is on.""" 173 | for component in self._client.components or []: 174 | if component[KEY_NAME] == self._component_name: 175 | return bool(component.setdefault(KEY_ENABLED, False)) 176 | return False 177 | 178 | @property 179 | def available(self) -> bool: 180 | """Return server availability.""" 181 | return bool(self._client.has_loaded_state) 182 | 183 | async def _async_send_set_component(self, value: bool) -> None: 184 | """Send a component control request.""" 185 | await self._client.async_send_set_component( 186 | **{ 187 | KEY_COMPONENTSTATE: { 188 | KEY_COMPONENT: self._component_name, 189 | KEY_STATE: value, 190 | } 191 | } 192 | ) 193 | 194 | async def async_turn_on(self, **kwargs: Any) -> None: 195 | """Turn on the switch.""" 196 | await self._async_send_set_component(True) 197 | 198 | async def async_turn_off(self, **kwargs: Any) -> None: 199 | """Turn off the switch.""" 200 | await self._async_send_set_component(False) 201 | 202 | @callback 203 | def _update_components(self, _: dict[str, Any] | None = None) -> None: 204 | """Update HyperHDR components.""" 205 | self.async_write_ha_state() 206 | 207 | async def async_added_to_hass(self) -> None: 208 | """Register callbacks when entity added to hass.""" 209 | self.async_on_remove( 210 | async_dispatcher_connect( 211 | self.hass, 212 | SIGNAL_ENTITY_REMOVE.format(self._attr_unique_id), 213 | functools.partial(self.async_remove, force_remove=True), 214 | ) 215 | ) 216 | 217 | self._client.add_callbacks(self._client_callbacks) 218 | 219 | async def async_will_remove_from_hass(self) -> None: 220 | """Cleanup prior to hass removal.""" 221 | self._client.remove_callbacks(self._client_callbacks) 222 | -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "step": { 4 | "confirm": { 5 | "description": "\u0647\u0644 \u062a\u0631\u064a\u062f \u0625\u0636\u0627\u0641\u0629 HyperHDR Ambilight \u0625\u0644\u0649 Home Assistant\u061f \n\n ** \u0627\u0644\u0645\u0636\u064a\u0641: ** {host}\n ** \u0627\u0644\u0645\u0646\u0641\u0630: ** {port}\n ** \u0627\u0644\u0645\u0639\u0631\u0641 **: {id}", 6 | "title": "\u062a\u0623\u0643\u064a\u062f \u0625\u0636\u0627\u0641\u0629 \u062e\u062f\u0645\u0629 HyperHDR Ambilight" 7 | } 8 | } 9 | }, 10 | "entity": { 11 | "switch": { 12 | "all": { 13 | "name": "Component All" 14 | }, 15 | "smoothing": { 16 | "name": "Component Smoothing" 17 | }, 18 | "blackbar_detection": { 19 | "name": "Component Blackbar Detection" 20 | }, 21 | "forwarder": { 22 | "name": "Component Forwarder" 23 | }, 24 | "boblight_server": { 25 | "name": "Component Boblight Server" 26 | }, 27 | "platform_capture": { 28 | "name": "Component Platform Capture" 29 | }, 30 | "led_device": { 31 | "name": "Component LED Device" 32 | }, 33 | "usb_capture": { 34 | "name": "Component USB Capture" 35 | }, 36 | "hdr_tone_mapping": { 37 | "name": "Component HDR Tone Mapping" 38 | } 39 | }, 40 | "sensor": { 41 | "visible_priority": { 42 | "name": "Visible Priority" 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/ca.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "El servei ja est\u00e0 configurat", 5 | "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", 6 | "auth_new_token_not_granted_error": "El nou token creat no est\u00e0 aprovat a HyperHDR UI", 7 | "auth_new_token_not_work_error": "No s'ha pogut autenticar amb el nou token creat", 8 | "auth_required_error": "No s'ha pogut determinar si cal autoritzaci\u00f3", 9 | "cannot_connect": "Ha fallat la connexi\u00f3", 10 | "no_id": "La inst\u00e0ncia d'HyperHDR Ambilight no ha retornat el seu ID", 11 | "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" 12 | }, 13 | "error": { 14 | "cannot_connect": "Ha fallat la connexi\u00f3", 15 | "invalid_access_token": "Token d'acc\u00e9s no v\u00e0lid" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "Crea un nou token autom\u00e0ticament", 21 | "token": "O proporciona un token ja existent" 22 | }, 23 | "description": "Configura l'autoritzaci\u00f3 amb el teu servidor HyperHDR Ambilight" 24 | }, 25 | "confirm": { 26 | "description": "Vols afegir el seg\u00fcent HyperHDR Ambilight a Home Assistant?\n\n**Host:** {host}\n**Port:** {port}\n**ID**: {id}", 27 | "title": "Confirma l'addici\u00f3 del servei HyperHDR Ambilight" 28 | }, 29 | "create_token": { 30 | "description": "Selecciona **Envia** a continuaci\u00f3 per sol\u00b7licitar un token d'autenticaci\u00f3 nou. Ser\u00e0s redirigit a la interf\u00edcie d'usuari d'HyperHDR perqu\u00e8 puguis aprovar la sol\u00b7licitud. Verifica que l'identificador que es mostra \u00e9s \"{auth_id}\"", 31 | "title": "Crea un nou token d'autenticaci\u00f3 autom\u00e0ticament" 32 | }, 33 | "create_token_external": { 34 | "title": "Accepta el nou token a la IU d'HyperHDR" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "Amfitri\u00f3", 39 | "port": "Port" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "Efectes d'HyperHDR a mostrar", 49 | "priority": "Prioritat HyperHDR a utilitzar per als colors i efectes" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/cs.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "Slu\u017eba je ji\u017e nastavena", 5 | "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", 6 | "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", 7 | "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" 8 | }, 9 | "error": { 10 | "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", 11 | "invalid_access_token": "Neplatn\u00fd p\u0159\u00edstupov\u00fd token" 12 | }, 13 | "step": { 14 | "auth": { 15 | "data": { 16 | "create_token": "Automaticky vytvo\u0159it nov\u00fd token" 17 | } 18 | }, 19 | "create_token_external": { 20 | "title": "P\u0159ijmout nov\u00fd token v u\u017eivatelsk\u00e9m rozhran\u00ed HyperHDR" 21 | }, 22 | "user": { 23 | "data": { 24 | "host": "Hostitel", 25 | "port": "Port" 26 | } 27 | } 28 | } 29 | }, 30 | "entity": { 31 | "switch": { 32 | "all": { 33 | "name": "Component All" 34 | }, 35 | "smoothing": { 36 | "name": "Component Smoothing" 37 | }, 38 | "blackbar_detection": { 39 | "name": "Component Blackbar Detection" 40 | }, 41 | "forwarder": { 42 | "name": "Component Forwarder" 43 | }, 44 | "boblight_server": { 45 | "name": "Component Boblight Server" 46 | }, 47 | "platform_capture": { 48 | "name": "Component Platform Capture" 49 | }, 50 | "led_device": { 51 | "name": "Component LED Device" 52 | }, 53 | "usb_capture": { 54 | "name": "Component USB Capture" 55 | }, 56 | "hdr_tone_mapping": { 57 | "name": "Component HDR Tone Mapping" 58 | } 59 | }, 60 | "sensor": { 61 | "visible_priority": { 62 | "name": "Visible Priority" 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "Der Dienst ist bereits konfiguriert", 5 | "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", 6 | "auth_new_token_not_granted_error": "Neu erstellter Token wurde auf der HyperHDR-Benutzeroberfl\u00e4che nicht genehmigt", 7 | "auth_new_token_not_work_error": "Authentifizierung mit neu erstelltem Token fehlgeschlagen", 8 | "auth_required_error": "Es konnte nicht festgestellt werden, ob eine Autorisierung erforderlich ist", 9 | "cannot_connect": "Verbindung fehlgeschlagen", 10 | "no_id": "Die HyperHDR Ambilight-Instanz hat ihre ID nicht gemeldet", 11 | "reauth_successful": "Die erneute Authentifizierung war erfolgreich" 12 | }, 13 | "error": { 14 | "cannot_connect": "Verbindung fehlgeschlagen", 15 | "invalid_access_token": "Ung\u00fcltiger Zugriffs-Token" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "Automatisch neuen Authentifizierungs-Token erstellen", 21 | "token": "Oder stelle einen bereits vorhandenen Token bereit" 22 | }, 23 | "description": "Konfiguriere die Autorisierung f\u00fcr den HyperHDR-Ambilight-Server" 24 | }, 25 | "confirm": { 26 | "description": "Soll dieses HyperHDR Ambilight zu Home Assistant hinzugef\u00fcgt werden? \n\n ** Host: ** {host}\n ** Port: ** {port}\n ** ID **: {id}", 27 | "title": "Best\u00e4tige das Hinzuf\u00fcgen des HyperHDR-Ambilight-Dienstes" 28 | }, 29 | "create_token": { 30 | "description": "W\u00e4hle **Submit**, um einen neuen Authentifizierungs-Token anzufordern. Du wirst zur HyperHDR-Benutzeroberfl\u00e4che weitergeleitet, um die Anforderung zu best\u00e4tigen. Bitte \u00fcberpr\u00fcfe, ob die angezeigte ID \"{auth_id}\" lautet.", 31 | "title": "Automatisch neuen Authentifizierungs-Token erstellen" 32 | }, 33 | "create_token_external": { 34 | "title": "Neuen Token in HyperHDR UI akzeptieren" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "Host", 39 | "port": "Port" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "HyperHDR-Effekte zum Anzeigen", 49 | "priority": "HyperHDR-Priorit\u00e4t f\u00fcr Farben und Effekte" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/el.json: -------------------------------------------------------------------------------- 1 | { 2 | "options": { 3 | "step": { 4 | "init": { 5 | "data": { 6 | "effect_show_list": "\u0395\u03c6\u03ad HyperHDR \u03b3\u03b9\u03b1 \u03b5\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7" 7 | } 8 | } 9 | } 10 | }, 11 | "entity": { 12 | "switch": { 13 | "all": { 14 | "name": "Component All" 15 | }, 16 | "smoothing": { 17 | "name": "Component Smoothing" 18 | }, 19 | "blackbar_detection": { 20 | "name": "Component Blackbar Detection" 21 | }, 22 | "forwarder": { 23 | "name": "Component Forwarder" 24 | }, 25 | "boblight_server": { 26 | "name": "Component Boblight Server" 27 | }, 28 | "platform_capture": { 29 | "name": "Component Platform Capture" 30 | }, 31 | "led_device": { 32 | "name": "Component LED Device" 33 | }, 34 | "usb_capture": { 35 | "name": "Component USB Capture" 36 | }, 37 | "hdr_tone_mapping": { 38 | "name": "Component HDR Tone Mapping" 39 | } 40 | }, 41 | "sensor": { 42 | "visible_priority": { 43 | "name": "Visible Priority" 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "Service is already configured", 5 | "already_in_progress": "Configuration flow is already in progress", 6 | "auth_new_token_not_granted_error": "Newly created token was not approved on HyperHDR UI", 7 | "auth_new_token_not_work_error": "Failed to authenticate using newly created token", 8 | "auth_required_error": "Failed to determine if authorization is required", 9 | "cannot_connect": "Failed to connect", 10 | "no_id": "The HyperHDR Ambilight instance did not report its id", 11 | "reauth_successful": "Re-authentication was successful" 12 | }, 13 | "error": { 14 | "cannot_connect": "Failed to connect", 15 | "invalid_access_token": "Invalid access token" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "Automatically create new token", 21 | "token": "Or provide pre-existing token" 22 | }, 23 | "description": "Configure authorization to your HyperHDR Ambilight server" 24 | }, 25 | "confirm": { 26 | "description": "Do you want to add this HyperHDR Ambilight to Home Assistant?\n\n**Host:** {host}\n**Port:** {port}\n**ID**: {id}", 27 | "title": "Confirm addition of HyperHDR Ambilight service" 28 | }, 29 | "create_token": { 30 | "description": "Choose **Submit** below to request a new authentication token. You will be redirected to the HyperHDR UI to approve the request. Please verify the shown id is \"{auth_id}\"", 31 | "title": "Automatically create new authentication token" 32 | }, 33 | "create_token_external": { 34 | "title": "Accept new token in HyperHDR UI" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "Host", 39 | "port": "Port" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "HyperHDR effects to show", 49 | "priority": "HyperHDR priority to use for colors and effects" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/es-419.json: -------------------------------------------------------------------------------- 1 | { 2 | "options": { 3 | "step": { 4 | "init": { 5 | "data": { 6 | "effect_show_list": "Efectos de HyperHDR a mostrar" 7 | } 8 | } 9 | } 10 | }, 11 | "entity": { 12 | "switch": { 13 | "all": { 14 | "name": "Component All" 15 | }, 16 | "smoothing": { 17 | "name": "Component Smoothing" 18 | }, 19 | "blackbar_detection": { 20 | "name": "Component Blackbar Detection" 21 | }, 22 | "forwarder": { 23 | "name": "Component Forwarder" 24 | }, 25 | "boblight_server": { 26 | "name": "Component Boblight Server" 27 | }, 28 | "platform_capture": { 29 | "name": "Component Platform Capture" 30 | }, 31 | "led_device": { 32 | "name": "Component LED Device" 33 | }, 34 | "usb_capture": { 35 | "name": "Component USB Capture" 36 | }, 37 | "hdr_tone_mapping": { 38 | "name": "Component HDR Tone Mapping" 39 | } 40 | }, 41 | "sensor": { 42 | "visible_priority": { 43 | "name": "Visible Priority" 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "El servicio ya est\u00e1 configurado", 5 | "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", 6 | "auth_new_token_not_granted_error": "El token reci\u00e9n creado no se aprob\u00f3 en la interfaz de usuario de HyperHDR", 7 | "auth_new_token_not_work_error": "Error al autenticarse con el token reci\u00e9n creado", 8 | "auth_required_error": "No se pudo determinar si se requiere autorizaci\u00f3n", 9 | "cannot_connect": "No se pudo conectar", 10 | "no_id": "La instancia de HyperHDR Ambilight no inform\u00f3 su identificaci\u00f3n", 11 | "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" 12 | }, 13 | "error": { 14 | "cannot_connect": "No se pudo conectar", 15 | "invalid_access_token": "Token de acceso no v\u00e1lido" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "Crea un nuevo token autom\u00e1ticamente", 21 | "token": "O proporcionar un token preexistente" 22 | }, 23 | "description": "Configurar autorizaci\u00f3n a tu servidor HyperHDR Ambilight" 24 | }, 25 | "confirm": { 26 | "description": "\u00bfQuieres a\u00f1adir este HyperHDR Ambilight a Home Assistant?\n\n**Host:** {host}\n**Puerto:** {port}\n**Identificaci\u00f3n**: {id}", 27 | "title": "Confirmar la adici\u00f3n del servicio HyperHDR Ambilight" 28 | }, 29 | "create_token": { 30 | "description": "Elige ** Enviar ** a continuaci\u00f3n para solicitar un nuevo token de autenticaci\u00f3n. Se te redirigir\u00e1 a la interfaz de usuario de HyperHDR para aprobar la solicitud. Verifica que la identificaci\u00f3n que se muestra sea \"{auth_id}\"", 31 | "title": "Crear autom\u00e1ticamente un nuevo token de autenticaci\u00f3n" 32 | }, 33 | "create_token_external": { 34 | "title": "Aceptar nuevo token en la interfaz de usuario de HyperHDR" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "Host", 39 | "port": "Puerto" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "Efectos de HyperHDR a mostrar", 49 | "priority": "Prioridad de HyperHDR a usar para colores y efectos" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/et.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "Teenus on juba seadistatud", 5 | "already_in_progress": "Seadistamine on juba k\u00e4imas", 6 | "auth_new_token_not_granted_error": "\u00c4sja loodud juurdep\u00e4\u00e4sut\u00f5end ei ole HyperHDRi oma", 7 | "auth_new_token_not_work_error": "Loodud juurdep\u00e4\u00e4sut\u00f5endiga autentimine nurjus", 8 | "auth_required_error": "Autoriseerimise vajalikkuse tuvastamine nurjus", 9 | "cannot_connect": "\u00dchendamine nurjus", 10 | "no_id": "HyperHDR Ambilighti eksemplar ei teatanud oma ID-d", 11 | "reauth_successful": "Taastuvastamine \u00f5nnestus" 12 | }, 13 | "error": { 14 | "cannot_connect": "\u00dchendamine nurjus", 15 | "invalid_access_token": "Vigane juurdep\u00e4\u00e4sut\u00f5end" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "Loo automaatselt uus juurdep\u00e4\u00e4sut\u00f5end", 21 | "token": "V\u00f5i kasuta juba olemasolevat juurdep\u00e4\u00e4sut\u00f5endit" 22 | }, 23 | "description": "Seadista oma HyperHDR Ambilighti serveri tuvastamine" 24 | }, 25 | "confirm": { 26 | "description": "Kas soovid selle HyperHDR Ambilighti lisada Home Assistanti?\n\n**Host:** {host}\n**Port:** {port}\n**ID**: {id}", 27 | "title": "Kinnita HyperHDR Ambilight teenuse lisamine" 28 | }, 29 | "create_token": { 30 | "description": "Uue juurdep\u00e4\u00e4sut\u00f5endi taotlemiseks vali allpool ** Esita **. Taotluse kinnitamiseks suunatakse HyperHDRi kasutajaliidesesse. Palun kontrolli kas kuvatud ID on \" {auth_id} \"", 31 | "title": "Loo automaatselt uus juurdep\u00e4\u00e4sut\u00f5end" 32 | }, 33 | "create_token_external": { 34 | "title": "N\u00f5ustu uue juurdep\u00e4\u00e4sut\u00f5endiga HyperHDR UI-s" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "", 39 | "port": "" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "Kuvatavad HyperHDRi efektid", 49 | "priority": "V\u00e4rvide ja efektide puhul on kasutatavad HyperHDRi eelistused" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "Le service est d\u00e9ja configur\u00e9 ", 5 | "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", 6 | "auth_new_token_not_granted_error": "Le jeton nouvellement cr\u00e9\u00e9 n'a pas \u00e9t\u00e9 approuv\u00e9 sur l'interface utilisateur HyperHDR", 7 | "auth_new_token_not_work_error": "\u00c9chec de l'authentification \u00e0 l'aide du jeton nouvellement cr\u00e9\u00e9", 8 | "auth_required_error": "Impossible de d\u00e9terminer si une autorisation est requise", 9 | "cannot_connect": "Echec de connection", 10 | "no_id": "L'instance HyperHDR Ambilight n'a pas signal\u00e9 son identifiant", 11 | "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" 12 | }, 13 | "error": { 14 | "cannot_connect": "Echec de la connexion ", 15 | "invalid_access_token": "jeton d'acc\u00e8s Invalide" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "Cr\u00e9er automatiquement un nouveau jeton", 21 | "token": "Ou fournir un jeton pr\u00e9existant" 22 | }, 23 | "description": "Configurer l'autorisation sur votre serveur HyperHDR Ambilight" 24 | }, 25 | "confirm": { 26 | "description": "Voulez-vous ajouter cet HyperHDR Ambilight \u00e0 Home Assistant? \n\n ** H\u00f4te: ** {host}\n ** Port: ** {port}\n ** ID **: {id}", 27 | "title": "Confirmer l'ajout du service HyperHDR Ambilight" 28 | }, 29 | "create_token": { 30 | "description": "Choisissez ** Soumettre ** ci-dessous pour demander un nouveau jeton d'authentification. Vous serez redirig\u00e9 vers l'interface utilisateur HyperHDR pour approuver la demande. Veuillez v\u00e9rifier que l'identifiant affich\u00e9 est \" {auth_id} \"", 31 | "title": "Cr\u00e9er automatiquement un nouveau jeton d'authentification" 32 | }, 33 | "create_token_external": { 34 | "title": "Accepter un nouveau jeton dans l'interface utilisateur HyperHDR" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "H\u00f4te", 39 | "port": "Port" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "Effets HyperHDR \u00e0 montrer", 49 | "priority": "Priorit\u00e9 HyperHDR \u00e0 utiliser pour les couleurs et les effets" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/he.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8", 5 | "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", 6 | "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", 7 | "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" 8 | }, 9 | "error": { 10 | "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", 11 | "invalid_access_token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" 12 | }, 13 | "step": { 14 | "confirm": { 15 | "description": "\u05d4\u05d0\u05dd \u05d0\u05ea\u05d4 \u05e8\u05d5\u05e6\u05d4 \u05dc\u05d4\u05d5\u05e1\u05d9\u05e3 \u05d0\u05ea HyperHDR Ambilight \u05d4\u05d6\u05d4 \u05dc-Home Assistant?\n\n**\u05de\u05d0\u05e8\u05d7:** {host}\n**\u05e4\u05ea\u05d7\u05d4:** {port}\n**\u05de\u05d6\u05d4\u05d4**: {id}" 16 | }, 17 | "user": { 18 | "data": { 19 | "host": "\u05de\u05d0\u05e8\u05d7", 20 | "port": "\u05e4\u05ea\u05d7\u05d4" 21 | } 22 | } 23 | } 24 | }, 25 | "entity": { 26 | "switch": { 27 | "all": { 28 | "name": "Component All" 29 | }, 30 | "smoothing": { 31 | "name": "Component Smoothing" 32 | }, 33 | "blackbar_detection": { 34 | "name": "Component Blackbar Detection" 35 | }, 36 | "forwarder": { 37 | "name": "Component Forwarder" 38 | }, 39 | "boblight_server": { 40 | "name": "Component Boblight Server" 41 | }, 42 | "platform_capture": { 43 | "name": "Component Platform Capture" 44 | }, 45 | "led_device": { 46 | "name": "Component LED Device" 47 | }, 48 | "usb_capture": { 49 | "name": "Component USB Capture" 50 | }, 51 | "hdr_tone_mapping": { 52 | "name": "Component HDR Tone Mapping" 53 | } 54 | }, 55 | "sensor": { 56 | "visible_priority": { 57 | "name": "Visible Priority" 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/hu.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van", 5 | "already_in_progress": "A konfigur\u00e1ci\u00f3 m\u00e1r folyamatban van.", 6 | "auth_new_token_not_granted_error": "Az \u00fajonnan l\u00e9trehozott tokent nem hagyt\u00e1k j\u00f3v\u00e1 a HyperHDR felhaszn\u00e1l\u00f3i fel\u00fclet\u00e9n", 7 | "auth_new_token_not_work_error": "Nem siker\u00fclt hiteles\u00edteni az \u00fajonnan l\u00e9trehozott token haszn\u00e1lat\u00e1val", 8 | "auth_required_error": "Nem siker\u00fclt meghat\u00e1rozni, hogy sz\u00fcks\u00e9ges-e enged\u00e9ly", 9 | "cannot_connect": "Sikertelen csatlakoz\u00e1s", 10 | "no_id": "A HyperHDR Ambilight p\u00e9ld\u00e1ny nem szolg\u00e1ltatja az azonos\u00edt\u00f3j\u00e1t", 11 | "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt" 12 | }, 13 | "error": { 14 | "cannot_connect": "Sikertelen csatlakoz\u00e1s", 15 | "invalid_access_token": "\u00c9rv\u00e9nytelen hozz\u00e1f\u00e9r\u00e9si token" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "\u00daj token automatikus l\u00e9trehoz\u00e1sa", 21 | "token": "Vagy adjon meg m\u00e1r l\u00e9tez\u0151 tokent" 22 | }, 23 | "description": "Konfigur\u00e1lja a jogosults\u00e1got a HyperHDR Ambilight kiszolg\u00e1l\u00f3hoz" 24 | }, 25 | "confirm": { 26 | "description": "Hozz\u00e1 szeretn\u00e9 adni ezt a HyperHDR Ambilight-ot az Otthoni asszisztenshez? \n\n ** Host: ** {host}\n ** Port: ** {port}\n ** azonos\u00edt\u00f3 **: {id}", 27 | "title": "Er\u0151s\u00edtse meg a HyperHDR Ambilight szolg\u00e1ltat\u00e1s hozz\u00e1ad\u00e1s\u00e1t" 28 | }, 29 | "create_token": { 30 | "description": "Az al\u00e1bbiakban v\u00e1lassza a ** K\u00fcld\u00e9s ** lehet\u0151s\u00e9get \u00faj hiteles\u00edt\u00e9si token k\u00e9r\u00e9s\u00e9hez. A k\u00e9relem j\u00f3v\u00e1hagy\u00e1s\u00e1hoz \u00e1tir\u00e1ny\u00edtunk a HyperHDR felhaszn\u00e1l\u00f3i fel\u00fcletre. K\u00e9rj\u00fck, ellen\u0151rizze, hogy a megjelen\u00edtett azonos\u00edt\u00f3 \" {auth_id} \"", 31 | "title": "\u00daj hiteles\u00edt\u00e9si token automatikus l\u00e9trehoz\u00e1sa" 32 | }, 33 | "create_token_external": { 34 | "title": "\u00daj token elfogad\u00e1sa a HyperHDR felhaszn\u00e1l\u00f3i fel\u00fcleten" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "Hoszt", 39 | "port": "Port" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "Megjelen\u00edtend\u0151 HyperHDR effektusok", 49 | "priority": "HyperHDR priorit\u00e1s a sz\u00ednekhez \u00e9s effektekhez" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/id.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "Layanan sudah dikonfigurasi", 5 | "already_in_progress": "Alur konfigurasi sedang berlangsung", 6 | "auth_new_token_not_granted_error": "Token yang baru dibuat tidak disetujui di antarmuka HyperHDR", 7 | "auth_new_token_not_work_error": "Gagal mengautentikasi menggunakan token yang baru dibuat", 8 | "auth_required_error": "Gagal menentukan apakah otorisasi diperlukan", 9 | "cannot_connect": "Gagal terhubung", 10 | "no_id": "Instans HyperHDR Ambilight tidak melaporkan ID-nya", 11 | "reauth_successful": "Autentikasi ulang berhasil" 12 | }, 13 | "error": { 14 | "cannot_connect": "Gagal terhubung", 15 | "invalid_access_token": "Token akses tidak valid" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "Buat token baru secara otomatis", 21 | "token": "Atau berikan token yang sudah ada sebelumnya" 22 | }, 23 | "description": "Konfigurasikan otorisasi ke server HyperHDR Ambilight Anda" 24 | }, 25 | "confirm": { 26 | "description": "Apakah Anda ingin menambahkan HyperHDR Ambilight ini ke Home Assistant?\n\n**Host:** {host}\n**Port:** {port}\n**ID**: {id}", 27 | "title": "Konfirmasikan penambahan layanan HyperHDR Ambilight" 28 | }, 29 | "create_token": { 30 | "description": "Pilih **Kirim** di bawah ini untuk meminta token autentikasi baru. Anda akan diarahkan ke antarmuka HyperHDR untuk menyetujui permintaan. Pastikan ID yang ditampilkan adalah \"{auth_id}\"", 31 | "title": "Buat token autentikasi baru secara otomatis" 32 | }, 33 | "create_token_external": { 34 | "title": "Terima token baru di antarmuka HyperHDR" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "Host", 39 | "port": "Port" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "Efek hyperhdr untuk ditampilkan", 49 | "priority": "Prioritas hyperhdr digunakan untuk warna dan efek" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "Il servizio \u00e8 gi\u00e0 configurato", 5 | "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", 6 | "auth_new_token_not_granted_error": "Il token appena creato non \u00e8 stato approvato sull'interfaccia utente di HyperHDR", 7 | "auth_new_token_not_work_error": "Autenticazione utilizzando il token appena creato non riuscita", 8 | "auth_required_error": "Impossibile determinare se \u00e8 necessaria l'autorizzazione", 9 | "cannot_connect": "Impossibile connettersi", 10 | "no_id": "L'istanza HyperHDR Ambilight non ha segnalato il suo ID", 11 | "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" 12 | }, 13 | "error": { 14 | "cannot_connect": "Impossibile connettersi", 15 | "invalid_access_token": "Token di accesso non valido" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "Crea automaticamente un nuovo token", 21 | "token": "Oppure fornisci un token preesistente" 22 | }, 23 | "description": "Configura l'autorizzazione per il tuo server HyperHDR Ambilight" 24 | }, 25 | "confirm": { 26 | "description": "Vuoi aggiungere questo HyperHDR Ambilight a Home Assistant? \n\n ** Host:** {host}\n ** Porta:** {port}\n ** ID:** {id}", 27 | "title": "Conferma l'aggiunta del servizio HyperHDR Ambilight" 28 | }, 29 | "create_token": { 30 | "description": "Scegli **Invia** di seguito per richiedere un nuovo token di autenticazione. Verrai reindirizzato all'interfaccia utente di HyperHDR per approvare la richiesta. Verifica che l'ID visualizzato sia \"{auth_id}\"", 31 | "title": "Crea automaticamente un nuovo token di autenticazione" 32 | }, 33 | "create_token_external": { 34 | "title": "Accetta il nuovo token nell'interfaccia utente di HyperHDR" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "Host", 39 | "port": "Porta" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "Effetti di HyperHDR da mostrare", 49 | "priority": "Priorit\u00e0 HyperHDR da usare per colori ed effetti" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", 5 | "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", 6 | "auth_new_token_not_granted_error": "\uc0c8\ub85c \uc0dd\uc131\ub41c \ud1a0\ud070\uc774 HyperHDR UI\uc5d0\uc11c \uc2b9\uc778\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4", 7 | "auth_new_token_not_work_error": "\uc0c8\ub85c \uc0dd\uc131\ub41c \ud1a0\ud070\uc744 \uc0ac\uc6a9\ud558\uc5ec \uc778\uc99d\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", 8 | "auth_required_error": "\uc778\uc99d\uc774 \ud544\uc694\ud55c\uc9c0 \ud655\uc778\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", 9 | "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", 10 | "no_id": "HyperHDR Amblight \uc778\uc2a4\ud134\uc2a4\uac00 \ud574\ub2f9 ID\ub97c \ubcf4\uace0\ud558\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4", 11 | "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" 12 | }, 13 | "error": { 14 | "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", 15 | "invalid_access_token": "\uc561\uc138\uc2a4 \ud1a0\ud070\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "\uc0c8\ub85c\uc6b4 \ud1a0\ud070\uc744 \uc790\ub3d9\uc73c\ub85c \uc0dd\uc131\ud558\uae30", 21 | "token": "\ub610\ub294 \uae30\uc874 \ud1a0\ud070 \uc81c\uacf5\ud558\uae30" 22 | }, 23 | "description": "HyperHDR Amblight \uc11c\ubc84\uc5d0 \ub300\ud55c \uad8c\ud55c \uad6c\uc131\ud558\uae30" 24 | }, 25 | "confirm": { 26 | "description": "\uc774 HyperHDR Amblight\ub97c Home Assistant\uc5d0 \ucd94\uac00\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?\n\n**\ud638\uc2a4\ud2b8**: {host}\n**\ud3ec\ud2b8**: {port}\n**ID**: {id}", 27 | "title": "HyperHDR Amblight \uc11c\ube44\uc2a4 \ucd94\uac00 \ud655\uc778\ud558\uae30" 28 | }, 29 | "create_token": { 30 | "description": "\uc544\ub798 **\ud655\uc778**\uc744 \uc120\ud0dd\ud558\uc5ec \uc0c8\ub85c\uc6b4 \uc778\uc99d \ud1a0\ud070\uc744 \uc694\uccad\ud574\uc8fc\uc138\uc694. \uc694\uccad\uc744 \uc2b9\uc778\ud558\ub3c4\ub85d HyperHDR UI\ub85c \ub9ac\ub514\ub809\uc158\ub429\ub2c8\ub2e4. \ud45c\uc2dc\ub41c ID\uac00 \"{auth_id}\"\uc778\uc9c0 \ud655\uc778\ud574\uc8fc\uc138\uc694", 31 | "title": "\uc0c8\ub85c\uc6b4 \uc778\uc99d \ud1a0\ud070\uc744 \uc790\ub3d9\uc73c\ub85c \uc0dd\uc131\ud558\uae30" 32 | }, 33 | "create_token_external": { 34 | "title": "HyperHDR UI\uc5d0\uc11c \uc0c8\ub85c\uc6b4 \ud1a0\ud070 \uc218\ub77d\ud558\uae30" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "\ud638\uc2a4\ud2b8", 39 | "port": "\ud3ec\ud2b8" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "\ucd9c\ub825\ud560 HyperHDR \ud6a8\uacfc", 49 | "priority": "\uc0c9\uc0c1 \ubc0f \ud6a8\uacfc\uc5d0 \uc0ac\uc6a9\ud560 HyperHDR \uc6b0\uc120 \uc21c\uc704" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/lb.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "Service ass scho konfigur\u00e9iert", 5 | "already_in_progress": "Konfiguratioun's Oflaf ass schon am gaang", 6 | "auth_new_token_not_granted_error": "Nei erstallte Jeton ass net an der HyperHDR UI accord\u00e9iert", 7 | "auth_new_token_not_work_error": "Feeler bei der Authentifikatioun mam nei erstallte Jeton", 8 | "auth_required_error": "Feeler beim best\u00ebmmen ob Autorisatioun erfuerderlech ass", 9 | "cannot_connect": "Feeler beim verbannen", 10 | "no_id": "D\u00ebs HyperHDR Ambilight Instanz huet seng ID net gemellt." 11 | }, 12 | "error": { 13 | "cannot_connect": "Feeler beim verbannen", 14 | "invalid_access_token": "Ong\u00ebltegen Acc\u00e8s Jeton" 15 | }, 16 | "step": { 17 | "auth": { 18 | "data": { 19 | "create_token": "Neie Jeton automatesch erstellen", 20 | "token": "oder scho bestehenden Jeton uginn" 21 | }, 22 | "description": "Autorisatioun mat dengem HyperHDR Ambilight Server konfigur\u00e9ieren" 23 | }, 24 | "confirm": { 25 | "description": "Soll d\u00ebsen HyperHDR Ambilight am Home Assistant dob\u00e4i gesaat ginn?\n\n**Host:** {host}\n**Port:** {port}\n**ID**: {id}", 26 | "title": "Dob\u00e4isetzen vum HyperHDR Ambilight Service best\u00e4tegen" 27 | }, 28 | "create_token": { 29 | "description": "Klick **Ofsch\u00e9cken** fir een neien Authentifikatioun's Jeton unzefroen. Du g\u00ebss dann an HyperHDR UI weidergeleet fir d'Ufro z'accord\u00e9ieren. Iwwerpr\u00e9if dass d\u00e9i ugewisen id \"{auth_id}\" ass.", 30 | "title": "Neien Authentifikatioun's Jeton automatesch erstellen" 31 | }, 32 | "create_token_external": { 33 | "title": "Neie Jeton an der HyperHDR UI accord\u00e9ieren" 34 | }, 35 | "user": { 36 | "data": { 37 | "host": "Host", 38 | "port": "Port" 39 | } 40 | } 41 | } 42 | }, 43 | "options": { 44 | "step": { 45 | "init": { 46 | "data": { 47 | "priority": "HyperHDR Priorit\u00e9it fir Faarwen an Effekter" 48 | } 49 | } 50 | } 51 | }, 52 | "entity": { 53 | "switch": { 54 | "all": { 55 | "name": "Component All" 56 | }, 57 | "smoothing": { 58 | "name": "Component Smoothing" 59 | }, 60 | "blackbar_detection": { 61 | "name": "Component Blackbar Detection" 62 | }, 63 | "forwarder": { 64 | "name": "Component Forwarder" 65 | }, 66 | "boblight_server": { 67 | "name": "Component Boblight Server" 68 | }, 69 | "platform_capture": { 70 | "name": "Component Platform Capture" 71 | }, 72 | "led_device": { 73 | "name": "Component LED Device" 74 | }, 75 | "usb_capture": { 76 | "name": "Component USB Capture" 77 | }, 78 | "hdr_tone_mapping": { 79 | "name": "Component HDR Tone Mapping" 80 | } 81 | }, 82 | "sensor": { 83 | "visible_priority": { 84 | "name": "Visible Priority" 85 | } 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "Service is al geconfigureerd", 5 | "already_in_progress": "De configuratiestroom is al aan de gang", 6 | "auth_new_token_not_granted_error": "Nieuw aangemaakte token is niet goedgekeurd in HyperHDR UI", 7 | "auth_new_token_not_work_error": "Verificatie met nieuw aangemaakt token mislukt", 8 | "auth_required_error": "Kan niet bepalen of autorisatie vereist is", 9 | "cannot_connect": "Kan geen verbinding maken", 10 | "no_id": "De HyperHDR Ambilight instantie heeft zijn id niet gerapporteerd", 11 | "reauth_successful": "Herauthenticatie was succesvol" 12 | }, 13 | "error": { 14 | "cannot_connect": "Kan geen verbinding maken", 15 | "invalid_access_token": "Ongeldig toegangstoken" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "Maak automatisch een nieuw token aan", 21 | "token": "Of geef een reeds bestaand token op" 22 | }, 23 | "description": "Configureer autorisatie voor uw HyperHDR Ambilight-server" 24 | }, 25 | "confirm": { 26 | "description": "Wilt u deze HyperHDR Ambilight toevoegen aan Home Assistant? \n\n ** Host: ** {host}\n ** Poort: ** {port}\n ** ID **: {id}", 27 | "title": "Bevestig de toevoeging van HyperHDR Ambilight-service" 28 | }, 29 | "create_token": { 30 | "description": "Kies **Submit** hieronder om een nieuw authenticatie token aan te vragen. U wordt doorgestuurd naar de HyperHDR UI om de aanvraag goed te keuren. Controleer of de getoonde id \"{auth_id}\" is.", 31 | "title": "Automatisch nieuw authenticatie token aanmaken" 32 | }, 33 | "create_token_external": { 34 | "title": "Accepteer nieuwe token in HyperHDR UI" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "Host", 39 | "port": "Poort" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "HyperHDR-effecten om te laten zien", 49 | "priority": "HyperHDR prioriteit te gebruiken voor kleuren en effecten" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/no.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "Tjenesten er allerede konfigurert", 5 | "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", 6 | "auth_new_token_not_granted_error": "Nyopprettet token ble ikke godkjent p\u00e5 HyperHDR UI", 7 | "auth_new_token_not_work_error": "Kunne ikke godkjenne ved hjelp av nylig opprettet token", 8 | "auth_required_error": "Kan ikke fastsl\u00e5 om autorisasjon er n\u00f8dvendig", 9 | "cannot_connect": "Tilkobling mislyktes", 10 | "no_id": "HyperHDR Ambilight-forekomsten rapporterte ikke ID-en", 11 | "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" 12 | }, 13 | "error": { 14 | "cannot_connect": "Tilkobling mislyktes", 15 | "invalid_access_token": "Ugyldig tilgangstoken" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "Opprett nytt token automatisk", 21 | "token": "Eller oppgi eksisterende token" 22 | }, 23 | "description": "Konfigurer autorisasjon til HyperHDR Ambilight-serveren" 24 | }, 25 | "confirm": { 26 | "description": "Vil du legge til denne HyperHDR Ambilight i Home Assistant? \n\n ** Vert: ** {host}\n ** Port: ** {port}\n ** ID **: {id}", 27 | "title": "Bekreft tillegg av HyperHDR Ambilight-tjenesten" 28 | }, 29 | "create_token": { 30 | "description": "Velg **Send** nedenfor for \u00e5 be om et nytt godkjenningstoken. Du vil bli omdirigert til HyperHDR UI for \u00e5 godkjenne foresp\u00f8rselen. Kontroller at den viste IDen er {auth_id}.", 31 | "title": "Opprett nytt godkjenningstoken automatisk" 32 | }, 33 | "create_token_external": { 34 | "title": "Godta nytt token i HyperHDR UI" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "Vert", 39 | "port": "Port" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "HyperHDR-effekter \u00e5 vise", 49 | "priority": "HyperHDR-prioritet for bruke til farger og effekter" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/pl.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "Us\u0142uga jest ju\u017c skonfigurowana", 5 | "already_in_progress": "Konfiguracja jest ju\u017c w toku", 6 | "auth_new_token_not_granted_error": "Nowo utworzony token nie zosta\u0142 zatwierdzony w interfejsie u\u017cytkownika HyperHDR", 7 | "auth_new_token_not_work_error": "Nie uda\u0142o si\u0119 uwierzytelni\u0107 przy u\u017cyciu nowo utworzonego tokena", 8 | "auth_required_error": "Nie uda\u0142o si\u0119 okre\u015bli\u0107, czy wymagana jest autoryzacja", 9 | "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", 10 | "no_id": "Instancja HyperHDR Ambilight nie zg\u0142osi\u0142a swojego identyfikatora", 11 | "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" 12 | }, 13 | "error": { 14 | "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", 15 | "invalid_access_token": "Niepoprawny token dost\u0119pu" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "Automatycznie utw\u00f3rz nowy token", 21 | "token": "Lub podaj istniej\u0105cy token" 22 | }, 23 | "description": "Skonfiguruj autoryzacj\u0119 do serwera HyperHDR Ambilight" 24 | }, 25 | "confirm": { 26 | "description": "Czy chcesz doda\u0107 ten HyperHDR Ambilight do Home Assistanta? \n\n **Host:** {host}\n **Port:** {port}\n **ID**: {id}", 27 | "title": "Potwierdzanie dodania us\u0142ugi HyperHDR Ambilight" 28 | }, 29 | "create_token": { 30 | "description": "Naci\u015bnij **Prze\u015blij** poni\u017cej, aby za\u017c\u0105da\u0107 nowego tokena uwierzytelniania. Nast\u0105pi przekierowanie do interfejsu u\u017cytkownika HyperHDR, aby zatwierdzi\u0107 \u017c\u0105danie. Sprawd\u017a, czy wy\u015bwietlany identyfikator to \u201e {auth_id} \u201d", 31 | "title": "Automatyczne tworzenie nowego tokena uwierzytelniaj\u0105cego" 32 | }, 33 | "create_token_external": { 34 | "title": "Akceptowanie nowego tokena w interfejsie u\u017cytkownika HyperHDR" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "Nazwa hosta lub adres IP", 39 | "port": "Port" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "Efekty HyperHDRa do pokazania", 49 | "priority": "HyperHDR ma pierwsze\u0144stwo w u\u017cyciu dla kolor\u00f3w i efekt\u00f3w" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado", 5 | "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", 6 | "cannot_connect": "Falha na liga\u00e7\u00e3o" 7 | }, 8 | "error": { 9 | "cannot_connect": "Falha na liga\u00e7\u00e3o", 10 | "invalid_access_token": "Token de acesso inv\u00e1lido" 11 | }, 12 | "step": { 13 | "user": { 14 | "data": { 15 | "host": "Servidor", 16 | "port": "Porta" 17 | } 18 | } 19 | } 20 | }, 21 | "entity": { 22 | "switch": { 23 | "all": { 24 | "name": "Component All" 25 | }, 26 | "smoothing": { 27 | "name": "Component Smoothing" 28 | }, 29 | "blackbar_detection": { 30 | "name": "Component Blackbar Detection" 31 | }, 32 | "forwarder": { 33 | "name": "Component Forwarder" 34 | }, 35 | "boblight_server": { 36 | "name": "Component Boblight Server" 37 | }, 38 | "platform_capture": { 39 | "name": "Component Platform Capture" 40 | }, 41 | "led_device": { 42 | "name": "Component LED Device" 43 | }, 44 | "usb_capture": { 45 | "name": "Component USB Capture" 46 | }, 47 | "hdr_tone_mapping": { 48 | "name": "Component HDR Tone Mapping" 49 | } 50 | }, 51 | "sensor": { 52 | "visible_priority": { 53 | "name": "Visible Priority" 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "\u042d\u0442\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", 5 | "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", 6 | "auth_new_token_not_granted_error": "\u0421\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u043d\u0435 \u0431\u044b\u043b \u043e\u0434\u043e\u0431\u0440\u0435\u043d \u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 HyperHDR.", 7 | "auth_new_token_not_work_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0439\u0442\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0442\u043e\u043a\u0435\u043d\u0430.", 8 | "auth_required_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c, \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043b\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f.", 9 | "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", 10 | "no_id": "HyperHDR Ambilight \u043d\u0435 \u0441\u043e\u043e\u0431\u0449\u0438\u043b \u0441\u0432\u043e\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440.", 11 | "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." 12 | }, 13 | "error": { 14 | "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", 15 | "invalid_access_token": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430." 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0442\u043e\u043a\u0435\u043d", 21 | "token": "\u0418\u043b\u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0442\u043e\u043a\u0435\u043d" 22 | }, 23 | "description": "\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 HyperHDR Ambilight." 24 | }, 25 | "confirm": { 26 | "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c HyperHDR Ambilight?\n\n**\u0425\u043e\u0441\u0442:** {host}\n**\u041f\u043e\u0440\u0442:** {port}\n**ID**: {id}", 27 | "title": "\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043b\u0443\u0436\u0431\u044b HyperHDR Ambilight" 28 | }, 29 | "create_token": { 30 | "description": "\u041d\u0430\u0436\u043c\u0438\u0442\u0435 **\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c** \u043d\u0438\u0436\u0435, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u0412\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u044b \u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 HyperHDR \u0434\u043b\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 - \"{auth_id}\"", 31 | "title": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438" 32 | }, 33 | "create_token_external": { 34 | "title": "\u041f\u0440\u0438\u043d\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 HyperHDR" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "\u0425\u043e\u0441\u0442", 39 | "port": "\u041f\u043e\u0440\u0442" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "\u042d\u0444\u0444\u0435\u043a\u0442\u044b HyperHDR", 49 | "priority": "\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 HyperHDR \u0434\u043b\u044f \u0446\u0432\u0435\u0442\u043e\u0432 \u0438 \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/sl.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "Storitev je \u017ee name\u0161\u010dena", 5 | "already_in_progress": "Name\u0161\u010danje se \u017ee izvaja", 6 | "auth_new_token_not_granted_error": "Uporabni\u0161ki vmesnik HyperHDR UI ni potrdil novo ustvarjenega \u017eetona", 7 | "auth_new_token_not_work_error": "Overjanje s pomo\u010djo novo ustvarjenega \u017eetona ni uspelo", 8 | "auth_required_error": "Ni mogo\u010de dolo\u010diti ali je overjanje potrebno", 9 | "cannot_connect": "Povezovanje ni bilo uspe\u0161no", 10 | "no_id": "HyperHDR Ambilight instanca ni prijavila tega id", 11 | "reauth_successful": "Ponovno overjanje je uspelo." 12 | }, 13 | "error": { 14 | "cannot_connect": "Neuspelo povezovanje", 15 | "invalid_access_token": "Neveljaven \u017eeton za dostop" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "Samodejno ustvari nov \u017eeton", 21 | "token": "ali zagotovite \u017ee obstoje\u010di \u017eeton" 22 | }, 23 | "description": "Nastavite overitev za HyperHDR Ambilight stre\u017enik" 24 | }, 25 | "confirm": { 26 | "description": "\u017delite dodati ta HyperHDR Ambilight v Home Assistant?\n\n**Gostitelj:** {host}\n**Vrata:** {port}\n**ID**: {id}", 27 | "title": "Potrdi dodajanje storitve HyperHDR Ambilight" 28 | }, 29 | "create_token": { 30 | "description": "Izberite **Posreduj*, \u010de \u017eelite zahtevati nov overitveni \u017eeton. Preusmerjeni boste na uporabni\u0161ki vmesnik HyperHDR, da potrdite zahtevek. Prepri\u010dajte se, da je prikazani id \"{auth_id}\"", 31 | "title": "Samodejno ustvari nov overitveni \u017eeton" 32 | }, 33 | "create_token_external": { 34 | "title": "Sprejmi nov \u017eeton v uporabni\u0161kem vmesniku HyperHDR" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "Gostitelj", 39 | "port": "Vrata" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "priority": "Prednostna raba HyperHDRa za barve in u\u010dinke" 49 | } 50 | } 51 | } 52 | }, 53 | "entity": { 54 | "switch": { 55 | "all": { 56 | "name": "Component All" 57 | }, 58 | "smoothing": { 59 | "name": "Component Smoothing" 60 | }, 61 | "blackbar_detection": { 62 | "name": "Component Blackbar Detection" 63 | }, 64 | "forwarder": { 65 | "name": "Component Forwarder" 66 | }, 67 | "boblight_server": { 68 | "name": "Component Boblight Server" 69 | }, 70 | "platform_capture": { 71 | "name": "Component Platform Capture" 72 | }, 73 | "led_device": { 74 | "name": "Component LED Device" 75 | }, 76 | "usb_capture": { 77 | "name": "Component USB Capture" 78 | }, 79 | "hdr_tone_mapping": { 80 | "name": "Component HDR Tone Mapping" 81 | } 82 | }, 83 | "sensor": { 84 | "visible_priority": { 85 | "name": "Visible Priority" 86 | } 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "Hizmet zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", 5 | "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", 6 | "auth_new_token_not_granted_error": "HyperHDR UI'de yeni olu\u015fturulan belirte\u00e7 onaylanmad\u0131", 7 | "auth_new_token_not_work_error": "Yeni olu\u015fturulan belirte\u00e7 kullan\u0131larak kimlik do\u011frulamas\u0131 ba\u015far\u0131s\u0131z oldu", 8 | "auth_required_error": "Yetkilendirmenin gerekli olup olmad\u0131\u011f\u0131 belirlenemedi", 9 | "cannot_connect": "Ba\u011flanma hatas\u0131", 10 | "no_id": "HyperHDR Ambilight \u00f6rne\u011fi kimli\u011fini bildirmedi", 11 | "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" 12 | }, 13 | "error": { 14 | "cannot_connect": "Ba\u011flanma hatas\u0131", 15 | "invalid_access_token": "Ge\u00e7ersiz eri\u015fim belirteci" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "Otomatik olarak yeni belirte\u00e7 olu\u015fturma", 21 | "token": "Veya \u00f6nceden varolan belirte\u00e7 leri sa\u011flay\u0131n" 22 | } 23 | }, 24 | "confirm": { 25 | "title": "HyperHDR Ambilight hizmetinin eklenmesini onaylay\u0131n" 26 | }, 27 | "create_token": { 28 | "title": "Otomatik olarak yeni kimlik do\u011frulama belirteci olu\u015fturun" 29 | }, 30 | "create_token_external": { 31 | "title": "HyperHDR kullan\u0131c\u0131 aray\u00fcz\u00fcnde yeni belirteci kabul edin" 32 | }, 33 | "user": { 34 | "data": { 35 | "host": "Ana Bilgisayar", 36 | "port": "Port" 37 | } 38 | } 39 | } 40 | }, 41 | "options": { 42 | "step": { 43 | "init": { 44 | "data": { 45 | "priority": "Renkler ve efektler i\u00e7in kullan\u0131lacak hyperhdr \u00f6nceli\u011fi" 46 | } 47 | } 48 | } 49 | }, 50 | "entity": { 51 | "switch": { 52 | "all": { 53 | "name": "Component All" 54 | }, 55 | "smoothing": { 56 | "name": "Component Smoothing" 57 | }, 58 | "blackbar_detection": { 59 | "name": "Component Blackbar Detection" 60 | }, 61 | "forwarder": { 62 | "name": "Component Forwarder" 63 | }, 64 | "boblight_server": { 65 | "name": "Component Boblight Server" 66 | }, 67 | "platform_capture": { 68 | "name": "Component Platform Capture" 69 | }, 70 | "led_device": { 71 | "name": "Component LED Device" 72 | }, 73 | "usb_capture": { 74 | "name": "Component USB Capture" 75 | }, 76 | "hdr_tone_mapping": { 77 | "name": "Component HDR Tone Mapping" 78 | } 79 | }, 80 | "sensor": { 81 | "visible_priority": { 82 | "name": "Visible Priority" 83 | } 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/uk.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "\u0426\u044f \u0441\u043b\u0443\u0436\u0431\u0430 \u0432\u0436\u0435 \u0434\u043e\u0434\u0430\u043d\u0430 \u0432 Home Assistant.", 5 | "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0432\u0436\u0435 \u0442\u0440\u0438\u0432\u0430\u0454.", 6 | "auth_new_token_not_granted_error": "\u0421\u0442\u0432\u043e\u0440\u0435\u043d\u0438\u0439 \u0442\u043e\u043a\u0435\u043d \u043d\u0435 \u0431\u0443\u0432 \u0441\u0445\u0432\u0430\u043b\u0435\u043d\u0438\u0439 \u0432 \u0456\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0456 HyperHDR.", 7 | "auth_new_token_not_work_error": "\u041d\u0435\u043c\u043e\u0436\u043b\u0438\u0432\u043e \u043f\u0440\u043e\u0439\u0442\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044e \u0437 \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u0430\u043d\u043d\u044f\u043c \u0441\u0442\u0432\u043e\u0440\u0435\u043d\u043e\u0433\u043e \u0442\u043e\u043a\u0435\u043d\u0430.", 8 | "auth_required_error": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u0432\u0438\u0437\u043d\u0430\u0447\u0438\u0442\u0438, \u0447\u0438 \u043f\u043e\u0442\u0440\u0456\u0431\u043d\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0456\u044f.", 9 | "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f", 10 | "no_id": "HyperHDR Ambilight \u043d\u0435 \u043d\u0430\u0434\u0430\u0432 \u0441\u0432\u0456\u0439 \u0456\u0434\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0442\u043e\u0440.", 11 | "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f \u043f\u0440\u043e\u0439\u0448\u043b\u0430 \u0443\u0441\u043f\u0456\u0448\u043d\u043e" 12 | }, 13 | "error": { 14 | "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f", 15 | "invalid_access_token": "\u041d\u0435\u0432\u0456\u0440\u043d\u0438\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0443." 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0441\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u043d\u043e\u0432\u0438\u0439 \u0442\u043e\u043a\u0435\u043d", 21 | "token": "\u0410\u0431\u043e \u043d\u0430\u0434\u0430\u0442\u0438 \u043d\u0430\u044f\u0432\u043d\u0438\u0439 \u0442\u043e\u043a\u0435\u043d" 22 | }, 23 | "description": "\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0456\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0456 HyperHDR Ambilight." 24 | }, 25 | "confirm": { 26 | "description": "\u0412\u0438 \u0432\u043f\u0435\u0432\u043d\u0435\u043d\u0456, \u0449\u043e \u0445\u043e\u0447\u0435\u0442\u0435 \u0434\u043e\u0434\u0430\u0442\u0438 HyperHDR Ambilight? \n\n ** \u0425\u043e\u0441\u0442: ** {host}\n ** \u041f\u043e\u0440\u0442: ** {port}\n ** ID **: {id}", 27 | "title": "\u041f\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u0456\u0442\u044c \u0434\u043e\u0434\u0430\u0432\u0430\u043d\u043d\u044f \u0441\u043b\u0443\u0436\u0431\u0438 HyperHDR Ambilight" 28 | }, 29 | "create_token": { 30 | "description": "\u041d\u0430\u0442\u0438\u0441\u043d\u0456\u0442\u044c ** \u041f\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0438 ** \u043d\u0438\u0436\u0447\u0435, \u0449\u043e\u0431 \u043e\u0442\u0440\u0438\u043c\u0430\u0442\u0438 \u043d\u043e\u0432\u0438\u0439 \u0442\u043e\u043a\u0435\u043d \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u0457. \u0412\u0438 \u0431\u0443\u0434\u0435\u0442\u0435 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0456 \u0432 \u0456\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430 HyperHDR \u0434\u043b\u044f \u043f\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u0436\u0435\u043d\u043d\u044f \u0437\u0430\u043f\u0438\u0442\u0443. \u041f\u0435\u0440\u0435\u043a\u043e\u043d\u0430\u0439\u0442\u0435\u0441\u044f, \u0449\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u0438\u0439 \u0456\u0434\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0442\u043e\u0440 - \" {auth_id} \"", 31 | "title": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0441\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u043d\u043e\u0432\u0438\u0439 \u0442\u043e\u043a\u0435\u043d \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u0457" 32 | }, 33 | "create_token_external": { 34 | "title": "\u041f\u0440\u0438\u0439\u043d\u044f\u0442\u0438 \u043d\u043e\u0432\u0438\u0439 \u0442\u043e\u043a\u0435\u043d \u0432 \u0456\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0456 HyperHDR" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "\u0425\u043e\u0441\u0442", 39 | "port": "\u041f\u043e\u0440\u0442" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "priority": "\u041f\u0440\u0456\u043e\u0440\u0438\u0442\u0435\u0442 HyperHDR \u0434\u043b\u044f \u043a\u043e\u043b\u044c\u043e\u0440\u0456\u0432 \u0456 \u0435\u0444\u0435\u043a\u0442\u0456\u0432" 49 | } 50 | } 51 | } 52 | }, 53 | "entity": { 54 | "switch": { 55 | "all": { 56 | "name": "Component All" 57 | }, 58 | "smoothing": { 59 | "name": "Component Smoothing" 60 | }, 61 | "blackbar_detection": { 62 | "name": "Component Blackbar Detection" 63 | }, 64 | "forwarder": { 65 | "name": "Component Forwarder" 66 | }, 67 | "boblight_server": { 68 | "name": "Component Boblight Server" 69 | }, 70 | "platform_capture": { 71 | "name": "Component Platform Capture" 72 | }, 73 | "led_device": { 74 | "name": "Component LED Device" 75 | }, 76 | "usb_capture": { 77 | "name": "Component USB Capture" 78 | }, 79 | "hdr_tone_mapping": { 80 | "name": "Component HDR Tone Mapping" 81 | } 82 | }, 83 | "sensor": { 84 | "visible_priority": { 85 | "name": "Visible Priority" 86 | } 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /custom_components/hyperhdr/translations/zh-Hant.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "abort": { 4 | "already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", 5 | "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", 6 | "auth_new_token_not_granted_error": "\u65b0\u5275\u6b0a\u6756\u672a\u7372\u5f97 HyperHDR UI \u6838\u51c6", 7 | "auth_new_token_not_work_error": "\u4f7f\u7528\u65b0\u5275\u6b0a\u6756\u8a8d\u8b49\u5931\u6557", 8 | "auth_required_error": "\u7121\u6cd5\u5224\u5b9a\u662f\u5426\u9700\u8981\u9a57\u8b49", 9 | "cannot_connect": "\u9023\u7dda\u5931\u6557", 10 | "no_id": "HyperHDR Ambilight \u5be6\u9ad4\u672a\u56de\u5831\u5176 ID", 11 | "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" 12 | }, 13 | "error": { 14 | "cannot_connect": "\u9023\u7dda\u5931\u6557", 15 | "invalid_access_token": "\u5b58\u53d6\u6b0a\u6756\u7121\u6548" 16 | }, 17 | "step": { 18 | "auth": { 19 | "data": { 20 | "create_token": "\u81ea\u52d5\u65b0\u5275\u6b0a\u6756", 21 | "token": "\u6216\u63d0\u4f9b\u73fe\u6709\u6b0a\u6756" 22 | }, 23 | "description": "\u8a2d\u5b9a HyperHDR Ambilight \u4f3a\u670d\u5668\u8a8d\u8b49" 24 | }, 25 | "confirm": { 26 | "description": "\u662f\u5426\u8981\u5c07 HyperHDR Ambilight \u65b0\u589e\u81f3 Home Assistant\uff1f\n\n**\u4e3b\u6a5f\u7aef\uff1a** {host}\n**\u901a\u8a0a\u57e0\uff1a** {port}\n**ID**\uff1a {id}", 27 | "title": "\u78ba\u8a8d\u9644\u52a0 HyperHDR Ambilight \u670d\u52d9" 28 | }, 29 | "create_token": { 30 | "description": "\u9ede\u9078\u4e0b\u65b9 **\u50b3\u9001** \u4ee5\u8acb\u6c42\u65b0\u8a8d\u8b49\u6b0a\u6756\u3002\u5c07\u6703\u91cd\u65b0\u5c0e\u5411\u81f3 HyperHDR UI \u4ee5\u6838\u51c6\u8981\u6c42\u3002\u8acb\u78ba\u8a8d\u986f\u793a ID \u70ba \"{auth_id}\"", 31 | "title": "\u81ea\u52d5\u65b0\u5275\u8a8d\u8b49\u6b0a\u6756" 32 | }, 33 | "create_token_external": { 34 | "title": "\u63a5\u53d7 HyperHDR UI \u4e2d\u7684\u65b0\u6b0a\u6756" 35 | }, 36 | "user": { 37 | "data": { 38 | "host": "\u4e3b\u6a5f\u7aef", 39 | "port": "\u901a\u8a0a\u57e0" 40 | } 41 | } 42 | } 43 | }, 44 | "options": { 45 | "step": { 46 | "init": { 47 | "data": { 48 | "effect_show_list": "\u986f\u793a HyperHDR \u6548\u61c9", 49 | "priority": "HyperHDR \u512a\u5148\u4f7f\u7528\u4e4b\u8272\u6eab\u8207\u7279\u6548" 50 | } 51 | } 52 | } 53 | }, 54 | "entity": { 55 | "switch": { 56 | "all": { 57 | "name": "Component All" 58 | }, 59 | "smoothing": { 60 | "name": "Component Smoothing" 61 | }, 62 | "blackbar_detection": { 63 | "name": "Component Blackbar Detection" 64 | }, 65 | "forwarder": { 66 | "name": "Component Forwarder" 67 | }, 68 | "boblight_server": { 69 | "name": "Component Boblight Server" 70 | }, 71 | "platform_capture": { 72 | "name": "Component Platform Capture" 73 | }, 74 | "led_device": { 75 | "name": "Component LED Device" 76 | }, 77 | "usb_capture": { 78 | "name": "Component USB Capture" 79 | }, 80 | "hdr_tone_mapping": { 81 | "name": "Component HDR Tone Mapping" 82 | } 83 | }, 84 | "sensor": { 85 | "visible_priority": { 86 | "name": "Visible Priority" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /hacs.json: -------------------------------------------------------------------------------- 1 | { 2 | "hacs": "1.34.0", 3 | "name": "HyperHDR", 4 | "homeassistant": "2024.1.6" 5 | } -------------------------------------------------------------------------------- /hyperhdr-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjoshd/hyperhdr-ha/7e245d4db141d99b7955837af4a338a068954ba9/hyperhdr-logo.png -------------------------------------------------------------------------------- /info.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Installation 4 | 5 | 1. Add to your [HACS](https://hacs.xyz/) custom repositories. 6 | 1. Choose `Integration` from the category selection. 7 | 1. Click install. 8 | 1. Return to the Integrations page within HACS then click the `+ Explore & download repositories` button. 9 | 1. Search for `HyperHDR`, select it, then click `Download this repository with HACS`. 10 | 1. Restart Home Assistant to load the integration. 11 | 1. Visit the Wiki for information regarding: 12 | - [Initial Setup](https://github.com/mjoshd/hyperhdr-ha/wiki#initial-setup) 13 | - [Post-setup Advice](https://github.com/mjoshd/hyperhdr-ha/wiki#post-setup-advice) 14 | - [Debug Logging](https://github.com/mjoshd/hyperhdr-ha/wiki#debug-logging) 15 | 16 | ## Configuration 17 | 18 | 1. In Home Assistant navigate to `Configuration` -> `Devices & Services` -> `Integrations`. 19 | 1. Click the `+ Add Integration` button. 20 | 1. Search for `HyperHDR`. 21 | 1. If you cannot find `HyperHDR` in the list then be sure to clear your browser cache and/or perform a hard-refresh of the page. 22 | 1. Enter the IP address of your HyperHDR instance. 23 | 1. Click the `Submit` button. 24 | 25 | 26 | 27 | 28 | # Integration v0.0.7 29 | 30 | - Update most code to match changes present in the official Hyperion component as of 2024.07.4 31 | - Retained code from v0.0.6 `light.py` since updating that to match Hyperion code breaks multiple aspects of the light entity 32 | - Camera is still broken and remains disabled 33 | - If anyoneone can fix it then please do so and create a pull request! 34 | - See `__init__.py` for more info. 35 | 36 | # Integration v0.0.6 37 | 38 | Match changes present in the official Hyperion component as of ha-core-2022.05.2 39 | Bump hyperhdr-py version 40 | 41 | # Integration v0.0.5 42 | 43 | Add support for entity categories. 44 | 45 | # Integration v0.0.4 46 | 47 | ### Breaking changes 48 | 49 | - removed camera (sensor) due to instability 50 | 51 | ### Remediation for existing installations 52 | 53 | Only do the following if you are updating from an earlier version: 54 | 55 | - Install v0.0.4 56 | - Restart Home Assistant 57 | - Remove the orphaned camera entity 58 | - go to Configuration > Devices & Services > Integrations 59 | - locate HyperHDR 60 | - click the '12 entities' link 61 | - select the checkbox next to the orphaned camera entity 62 | - click 'REMOVE SELECTED' 63 | - click 'REMOVE' 64 | 65 | ### Alternative remediation method (nuclear option) 66 | 67 | Only do the following if you are unsuccessful with the previous steps: 68 | 69 | - Install v0.0.4 70 | - Delete the HyperHDR integration 71 | - go to Configuration > Devices & Services > Integrations > HyperHDR > 3-dots menu 72 | - click 'DELETE' 73 | - click 'OK' 74 | - Restart Home Assistant 75 | - Re-add the HyperHDR integration 76 | 77 | --------------------------------------------------------------------------------