This repository has been archived by the owner on Aug 25, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2773a3b
commit c65c920
Showing
21 changed files
with
1,399 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
"""The Philips Hue Play HDMI Sync Box integration.""" | ||
import asyncio | ||
import logging | ||
import json | ||
import os | ||
|
||
import voluptuous as vol | ||
|
||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.helpers import (config_validation as cv) | ||
from homeassistant.helpers.config_validation import make_entity_service_schema | ||
from homeassistant.helpers.service import async_extract_entity_ids | ||
from homeassistant.components.light import ATTR_BRIGHTNESS, ATTR_BRIGHTNESS_STEP | ||
|
||
from .huesyncbox import HueSyncBox, async_remove_entry_from_huesyncbox | ||
from .const import DOMAIN, LOGGER, ATTR_SYNC, ATTR_SYNC_TOGGLE, ATTR_MODE, ATTR_MODE_NEXT, ATTR_MODE_PREV, MODES, ATTR_INTENSITY, ATTR_INTENSITY_NEXT, ATTR_INTENSITY_PREV, INTENSITIES, ATTR_INPUT, ATTR_INPUT_NEXT, ATTR_INPUT_PREV, INPUTS, ATTR_ENTERTAINMENT_AREA, SERVICE_SET_SYNC_STATE, SERVICE_SET_BRIGHTNESS, SERVICE_SET_MODE, SERVICE_SET_INTENSITY, SERVICE_SET_ENTERTAINMENT_AREA | ||
|
||
CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({})}, extra=vol.ALLOW_EXTRA) | ||
|
||
PLATFORMS = ["media_player"] | ||
|
||
HUESYNCBOX_SET_STATE_SCHEMA = make_entity_service_schema( | ||
{ | ||
vol.Optional(ATTR_SYNC): cv.boolean, | ||
vol.Optional(ATTR_SYNC_TOGGLE): cv.boolean, | ||
vol.Optional(ATTR_BRIGHTNESS): cv.small_float, | ||
vol.Optional(ATTR_BRIGHTNESS_STEP): vol.All(vol.Coerce(float), vol.Range(min=-1, max=1)), | ||
vol.Optional(ATTR_MODE): vol.In(MODES), | ||
vol.Optional(ATTR_MODE_NEXT): cv.boolean, | ||
vol.Optional(ATTR_MODE_PREV): cv.boolean, | ||
vol.Optional(ATTR_INTENSITY): vol.In(INTENSITIES), | ||
vol.Optional(ATTR_INTENSITY_NEXT): cv.boolean, | ||
vol.Optional(ATTR_INTENSITY_PREV): cv.boolean, | ||
vol.Optional(ATTR_INPUT): vol.In(INPUTS), | ||
vol.Optional(ATTR_INPUT_NEXT): cv.boolean, | ||
vol.Optional(ATTR_INPUT_PREV): cv.boolean, | ||
vol.Optional(ATTR_ENTERTAINMENT_AREA): cv.string, | ||
} | ||
) | ||
|
||
HUESYNCBOX_SET_BRIGHTNESS_SCHEMA = make_entity_service_schema( | ||
{vol.Required(ATTR_BRIGHTNESS): cv.small_float} | ||
) | ||
|
||
HUESYNCBOX_SET_MODE_SCHEMA = make_entity_service_schema( | ||
{vol.Required(ATTR_MODE): vol.In(MODES)} | ||
) | ||
|
||
HUESYNCBOX_SET_INTENSITY_SCHEMA = make_entity_service_schema( | ||
{vol.Required(ATTR_INTENSITY): vol.In(INTENSITIES), vol.Optional(ATTR_MODE): vol.In(MODES)} | ||
) | ||
|
||
HUESYNCBOX_SET_ENTERTAINMENT_AREA_SCHEMA = make_entity_service_schema( | ||
{vol.Required(ATTR_ENTERTAINMENT_AREA): cv.string} | ||
) | ||
|
||
services_registered = False | ||
|
||
async def async_setup(hass: HomeAssistant, config: dict): | ||
""" | ||
Set up the Philips Hue Play HDMI Sync Box integration. | ||
Only supporting zeroconf, so nothing to do here. | ||
""" | ||
hass.data[DOMAIN] = {} | ||
|
||
return True | ||
|
||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): | ||
"""Set up a config entry for Philips Hue Play HDMI Sync Box.""" | ||
|
||
LOGGER.debug("%s async_setup_entry\nentry:\n%s\nhass.data\n%s" % (__name__, str(entry), hass.data[DOMAIN])) | ||
|
||
huesyncbox = HueSyncBox(hass, entry) | ||
hass.data[DOMAIN][entry.data["unique_id"]] = huesyncbox | ||
|
||
if not await huesyncbox.async_setup(): | ||
return False | ||
|
||
for platform in PLATFORMS: | ||
hass.async_create_task( | ||
hass.config_entries.async_forward_entry_setup(entry, platform) | ||
) | ||
|
||
# Register services on first entry | ||
global services_registered | ||
if not services_registered: | ||
await async_register_services(hass) | ||
services_registered = True | ||
|
||
return True | ||
|
||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): | ||
"""Unload a config entry.""" | ||
unload_ok = all( | ||
await asyncio.gather( | ||
*[ | ||
hass.config_entries.async_forward_entry_unload(entry, platform) | ||
for platform in PLATFORMS | ||
] | ||
) | ||
) | ||
|
||
if unload_ok: | ||
huesyncbox = hass.data[DOMAIN].pop(entry.data["unique_id"]) | ||
await huesyncbox.async_reset() | ||
|
||
# Unregister services when last entry is unloaded | ||
if len(hass.data[DOMAIN].items()) == 0: | ||
await async_unregister_services(hass) | ||
global services_registered | ||
services_registered = False | ||
|
||
return unload_ok | ||
|
||
|
||
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: | ||
# Best effort cleanup. User might not even have the device anymore or had it factory reset. | ||
# Note that the entry already has been unloaded. | ||
try: | ||
await async_remove_entry_from_huesyncbox(entry) | ||
except Exception as e: | ||
LOGGER.warning("Unregistering Philips Hue Play HDMI Sync Box failed: %s ", e) | ||
|
||
|
||
async def async_register_services(hass: HomeAssistant): | ||
async def async_set_sync_state(call): | ||
entity_ids = await async_extract_entity_ids(hass, call) | ||
for _, entry in hass.data[DOMAIN].items(): | ||
if entry.entity and entry.entity.entity_id in entity_ids: | ||
await entry.entity.async_set_sync_state(call.data) | ||
|
||
hass.services.async_register( | ||
DOMAIN, SERVICE_SET_SYNC_STATE, async_set_sync_state, schema=HUESYNCBOX_SET_STATE_SCHEMA | ||
) | ||
|
||
async def async_set_sync_mode(call): | ||
entity_ids = await async_extract_entity_ids(hass, call) | ||
for _, entry in hass.data[DOMAIN].items(): | ||
if entry.entity and entry.entity.entity_id in entity_ids: | ||
await entry.entity.async_set_sync_mode(call.data.get(ATTR_MODE)) | ||
|
||
hass.services.async_register( | ||
DOMAIN, SERVICE_SET_MODE, async_set_sync_mode, schema=HUESYNCBOX_SET_MODE_SCHEMA | ||
) | ||
|
||
async def async_set_intensity(call): | ||
entity_ids = await async_extract_entity_ids(hass, call) | ||
for _, entry in hass.data[DOMAIN].items(): | ||
if entry.entity and entry.entity.entity_id in entity_ids: | ||
await entry.entity.async_set_intensity(call.data.get(ATTR_INTENSITY), call.data.get(ATTR_MODE, None)) | ||
|
||
hass.services.async_register( | ||
DOMAIN, SERVICE_SET_INTENSITY, async_set_intensity, schema=HUESYNCBOX_SET_INTENSITY_SCHEMA | ||
) | ||
|
||
async def async_set_brightness(call): | ||
entity_ids = await async_extract_entity_ids(hass, call) | ||
for _, entry in hass.data[DOMAIN].items(): | ||
if entry.entity and entry.entity.entity_id in entity_ids: | ||
await entry.entity.async_set_brightness(call.data.get(ATTR_BRIGHTNESS)) | ||
|
||
hass.services.async_register( | ||
DOMAIN, SERVICE_SET_BRIGHTNESS, async_set_brightness, schema=HUESYNCBOX_SET_BRIGHTNESS_SCHEMA | ||
) | ||
|
||
async def async_set_entertainment_area(call): | ||
entity_ids = await async_extract_entity_ids(hass, call) | ||
for _, entry in hass.data[DOMAIN].items(): | ||
if entry.entity and entry.entity.entity_id in entity_ids: | ||
await entry.entity.async_select_entertainment_area(call.data.get(ATTR_ENTERTAINMENT_AREA)) | ||
|
||
hass.services.async_register( | ||
DOMAIN, SERVICE_SET_ENTERTAINMENT_AREA, async_set_entertainment_area, schema=HUESYNCBOX_SET_ENTERTAINMENT_AREA_SCHEMA | ||
) | ||
|
||
|
||
async def async_unregister_services(hass): | ||
hass.services.async_remove(DOMAIN, SERVICE_SET_SYNC_STATE) | ||
hass.services.async_remove(DOMAIN, SERVICE_SET_BRIGHTNESS) | ||
hass.services.async_remove(DOMAIN, SERVICE_SET_MODE) | ||
hass.services.async_remove(DOMAIN, SERVICE_SET_INTENSITY) | ||
hass.services.async_remove(DOMAIN, SERVICE_SET_ENTERTAINMENT_AREA) |
Binary file added
BIN
+6.01 KB
config/custom_components/huesyncbox/__pycache__/__init__.cpython-38.pyc
Binary file not shown.
Binary file added
BIN
+3.68 KB
config/custom_components/huesyncbox/__pycache__/config_flow.cpython-38.pyc
Binary file not shown.
Binary file not shown.
Binary file added
BIN
+3.78 KB
config/custom_components/huesyncbox/__pycache__/device_action.cpython-38.pyc
Binary file not shown.
Binary file added
BIN
+808 Bytes
config/custom_components/huesyncbox/__pycache__/errors.cpython-38.pyc
Binary file not shown.
Binary file added
BIN
+4.71 KB
config/custom_components/huesyncbox/__pycache__/huesyncbox.cpython-38.pyc
Binary file not shown.
Binary file added
BIN
+13.2 KB
config/custom_components/huesyncbox/__pycache__/media_player.cpython-38.pyc
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
"""Config flow for Philips Hue Play HDMI Sync Box integration.""" | ||
import asyncio | ||
import logging | ||
|
||
import voluptuous as vol | ||
|
||
from homeassistant import core, config_entries, exceptions | ||
from homeassistant.core import callback | ||
|
||
from .const import DOMAIN, LOGGER # pylint:disable=unused-import | ||
from .errors import AuthenticationRequired, CannotConnect | ||
from .huesyncbox import async_get_aiohuesyncbox_from_entry_data, async_register_aiohuesyncbox | ||
|
||
|
||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): | ||
"""Handle a config flow for Philips Hue Play HDMI Sync Box.""" | ||
|
||
VERSION = 1 | ||
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL | ||
|
||
def __init__(self): | ||
"""Initialize the config flow.""" | ||
pass | ||
|
||
async def async_step_user(self, user_input=None): | ||
"""Handle a flow initialized by the user.""" | ||
return self.async_abort(reason="manual_not_supported") | ||
|
||
|
||
async def async_step_link(self, user_input=None): | ||
""" | ||
Attempt to link with the huesyncbox. | ||
We will only end up in this step when the token is invalid. | ||
""" | ||
if user_input is None: | ||
return self.async_show_form(step_id="link") | ||
|
||
errors = {} | ||
|
||
try: | ||
api = await async_get_aiohuesyncbox_from_entry_data(self.context) | ||
result = await async_register_aiohuesyncbox(self.hass, api) | ||
self.context['access_token'] = result['access_token'] | ||
self.context['registration_id'] = result['registration_id'] | ||
await api.close() | ||
|
||
return await self._async_create_entry_from_context() | ||
except AuthenticationRequired: | ||
errors["base"] = "register_failed" | ||
except CannotConnect: | ||
errors["base"] = "cannot_connect" | ||
except Exception: # pylint: disable=broad-except | ||
LOGGER.exception( | ||
"Unknown error connecting to the Phlips Hue Play HDMI Sync Box %s at %s", self.context["unique_id"], self.context["host"] | ||
) | ||
errors["base"] = "unknown" | ||
|
||
return self.async_show_form(step_id="link", errors=errors) | ||
|
||
async def async_step_zeroconf(self, discovery_info): | ||
"""Handle zeroconf discovery.""" | ||
|
||
entry_info = { | ||
"host": discovery_info["host"], | ||
"port": discovery_info["port"], | ||
"path": discovery_info["properties"]["path"], | ||
"unique_id": discovery_info["properties"]["uniqueid"], | ||
"name": discovery_info["properties"]["name"], | ||
"devicetype": discovery_info["properties"]["devicetype"] | ||
} | ||
|
||
return await self.async_step_check(entry_info) | ||
|
||
|
||
async def async_step_check(self, entry_info): | ||
"""Perform some checks and create entry if OK.""" | ||
|
||
await self.async_set_unique_id(entry_info['unique_id']) | ||
self._abort_if_unique_id_configured(updates=entry_info) | ||
|
||
self.context["host"] = entry_info["host"] | ||
self.context["unique_id"] = entry_info["unique_id"] | ||
self.context["port"] = entry_info["port"] | ||
self.context["name"] = entry_info["name"] | ||
self.context["path"] = entry_info["path"] | ||
self.context['access_token'] = entry_info.get('access_token') | ||
self.context['registration_id'] = entry_info.get('registration_id') | ||
|
||
self.context["title_placeholders"] = {"name": self.context["name"], "unique_id": self.context["unique_id"]} | ||
|
||
api = await async_get_aiohuesyncbox_from_entry_data(entry_info) | ||
if await api.is_registered(): | ||
await api.close() | ||
return await self._async_create_entry_from_context() | ||
|
||
return await self.async_step_link() | ||
|
||
|
||
async def _async_create_entry_from_context(self): | ||
"""Return create entry with data from context.""" | ||
entry_data = { | ||
"host": self.context["host"], | ||
"name": self.context["name"], | ||
"path": self.context["path"], | ||
"port": self.context["port"], | ||
"unique_id": self.context["unique_id"], | ||
"access_token": self.context["access_token"], | ||
"registration_id": self.context["registration_id"], | ||
} | ||
|
||
LOGGER.debug("%s _async_create_entry_from_context\entry_data:\n%s\n" % (__name__, entry_data)) | ||
|
||
return self.async_create_entry( | ||
# Title should identify this entry, so use device name and lets include unique_id in case of multiple devices with the same name | ||
title=f"{entry_data['name']} ({entry_data['unique_id']})", | ||
data=entry_data, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
"""Constants for the Philips Hue Play HDMI Sync Box integration.""" | ||
|
||
import logging | ||
|
||
LOGGER = logging.getLogger(__package__) | ||
DOMAIN = "huesyncbox" | ||
|
||
MANUFACTURER_NAME = "Signify" | ||
|
||
SERVICE_SET_SYNC_STATE = 'set_sync_state' | ||
SERVICE_SET_BRIGHTNESS = 'set_brightness' | ||
SERVICE_SET_INTENSITY = 'set_intensity' | ||
SERVICE_SET_MODE = 'set_sync_mode' | ||
SERVICE_SET_ENTERTAINMENT_AREA = 'set_entertainment_area' | ||
|
||
ATTR_SYNC = 'sync' | ||
ATTR_SYNC_TOGGLE = 'sync_toggle' | ||
|
||
ATTR_MODE = 'mode' | ||
ATTR_MODE_NEXT = 'mode_next' | ||
ATTR_MODE_PREV = 'mode_prev' | ||
|
||
MODE_VIDEO = 'video' | ||
MODE_MUSIC = 'music' | ||
MODE_GAME = 'game' | ||
|
||
MODES = [ MODE_VIDEO, MODE_MUSIC, MODE_GAME ] | ||
|
||
ATTR_INTENSITY = "intensity" | ||
ATTR_INTENSITY_NEXT = "intensity_next" | ||
ATTR_INTENSITY_PREV = "intensity_prev" | ||
|
||
INTENSITY_SUBTLE = "subtle" | ||
INTENSITY_MODERATE = "moderate" | ||
INTENSITY_HIGH = "high" | ||
INTENSITY_INTENSE = "intense" | ||
|
||
INTENSITIES = [ INTENSITY_SUBTLE, INTENSITY_MODERATE, INTENSITY_HIGH, INTENSITY_INTENSE ] | ||
|
||
ATTR_INPUT = "input" | ||
ATTR_INPUT_NEXT = "input_next" | ||
ATTR_INPUT_PREV = "input_prev" | ||
|
||
INPUT_HDMI1 = 'input1' | ||
INPUT_HDMI2 = 'input2' | ||
INPUT_HDMI3 = 'input3' | ||
INPUT_HDMI4 = 'input4' | ||
|
||
INPUTS = [ INPUT_HDMI1, INPUT_HDMI2, INPUT_HDMI3, INPUT_HDMI4 ] | ||
|
||
ATTR_ENTERTAINMENT_AREA = "entertainment_area" |
Oops, something went wrong.