mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Add KEF services for DSP (#31967)
* add services for DSP * add homeassistant/components/kef/const.py * add services.yaml * fix set_mode * fix services * media_player.py fixes * bump aiokef to 0.2.9 * update requirements_all.txt * add basic sensor.py * add DSP settings as attributes * add message about kef.update_dsp * remove sensor.py * fix pylint issues * update_dsp inside async_added_to_hass * use {...} instead of dict(...) * get DSP settings when connecting to HA or once on update * simplify condition * do not get mode twice * remove async_added_to_hass * use async_register_entity_service * remove entity_id from schema and prepend _value * invalidate self._dsp after setting a DSP setting * schedule update_dsp every hour * subscribe and unsubscribe on adding and removing to HA * don't pass hass and set _update_dsp_task_remover to None after removing
This commit is contained in:
parent
bcd1eb952c
commit
3e0ccd2e86
@ -4,5 +4,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/kef",
|
"documentation": "https://www.home-assistant.io/integrations/kef",
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": ["@basnijholt"],
|
"codeowners": ["@basnijholt"],
|
||||||
"requirements": ["aiokef==0.2.7", "getmac==0.8.1"]
|
"requirements": ["aiokef==0.2.9", "getmac==0.8.1"]
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
"""Platform for the KEF Wireless Speakers."""
|
"""Platform for the KEF Wireless Speakers."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from aiokef import AsyncKefSpeaker
|
from aiokef import AsyncKefSpeaker
|
||||||
|
from aiokef.aiokef import DSP_OPTION_MAPPING
|
||||||
from getmac import get_mac_address
|
from getmac import get_mac_address
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -31,7 +33,8 @@ from homeassistant.const import (
|
|||||||
STATE_OFF,
|
STATE_OFF,
|
||||||
STATE_ON,
|
STATE_ON,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||||
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -55,6 +58,17 @@ CONF_INVERSE_SPEAKER_MODE = "inverse_speaker_mode"
|
|||||||
CONF_SUPPORTS_ON = "supports_on"
|
CONF_SUPPORTS_ON = "supports_on"
|
||||||
CONF_STANDBY_TIME = "standby_time"
|
CONF_STANDBY_TIME = "standby_time"
|
||||||
|
|
||||||
|
SERVICE_MODE = "set_mode"
|
||||||
|
SERVICE_DESK_DB = "set_desk_db"
|
||||||
|
SERVICE_WALL_DB = "set_wall_db"
|
||||||
|
SERVICE_TREBLE_DB = "set_treble_db"
|
||||||
|
SERVICE_HIGH_HZ = "set_high_hz"
|
||||||
|
SERVICE_LOW_HZ = "set_low_hz"
|
||||||
|
SERVICE_SUB_DB = "set_sub_db"
|
||||||
|
SERVICE_UPDATE_DSP = "update_dsp"
|
||||||
|
|
||||||
|
DSP_SCAN_INTERVAL = 3600
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_HOST): cv.string,
|
vol.Required(CONF_HOST): cv.string,
|
||||||
@ -118,6 +132,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
|||||||
inverse_speaker_mode,
|
inverse_speaker_mode,
|
||||||
supports_on,
|
supports_on,
|
||||||
sources,
|
sources,
|
||||||
|
speaker_type,
|
||||||
ioloop=hass.loop,
|
ioloop=hass.loop,
|
||||||
unique_id=unique_id,
|
unique_id=unique_id,
|
||||||
)
|
)
|
||||||
@ -128,6 +143,36 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
|||||||
hass.data[DOMAIN][host] = media_player
|
hass.data[DOMAIN][host] = media_player
|
||||||
async_add_entities([media_player], update_before_add=True)
|
async_add_entities([media_player], update_before_add=True)
|
||||||
|
|
||||||
|
platform = entity_platform.current_platform.get()
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
SERVICE_MODE,
|
||||||
|
{
|
||||||
|
vol.Optional("desk_mode"): cv.boolean,
|
||||||
|
vol.Optional("wall_mode"): cv.boolean,
|
||||||
|
vol.Optional("phase_correction"): cv.boolean,
|
||||||
|
vol.Optional("high_pass"): cv.boolean,
|
||||||
|
vol.Optional("sub_polarity"): vol.In(["-", "+"]),
|
||||||
|
vol.Optional("bass_extension"): vol.In(["Less", "Standard", "Extra"]),
|
||||||
|
},
|
||||||
|
"set_mode",
|
||||||
|
)
|
||||||
|
platform.async_register_entity_service(SERVICE_UPDATE_DSP, {}, "update_dsp")
|
||||||
|
|
||||||
|
def add_service(name, which, option):
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
name,
|
||||||
|
{vol.Required(option): vol.In(DSP_OPTION_MAPPING[which])},
|
||||||
|
f"set_{which}",
|
||||||
|
)
|
||||||
|
|
||||||
|
add_service(SERVICE_DESK_DB, "desk_db", "db_value")
|
||||||
|
add_service(SERVICE_WALL_DB, "wall_db", "db_value")
|
||||||
|
add_service(SERVICE_TREBLE_DB, "treble_db", "db_value")
|
||||||
|
add_service(SERVICE_HIGH_HZ, "high_hz", "hz_value")
|
||||||
|
add_service(SERVICE_LOW_HZ, "low_hz", "hz_value")
|
||||||
|
add_service(SERVICE_SUB_DB, "sub_db", "db_value")
|
||||||
|
|
||||||
|
|
||||||
class KefMediaPlayer(MediaPlayerDevice):
|
class KefMediaPlayer(MediaPlayerDevice):
|
||||||
"""Kef Player Object."""
|
"""Kef Player Object."""
|
||||||
@ -143,6 +188,7 @@ class KefMediaPlayer(MediaPlayerDevice):
|
|||||||
inverse_speaker_mode,
|
inverse_speaker_mode,
|
||||||
supports_on,
|
supports_on,
|
||||||
sources,
|
sources,
|
||||||
|
speaker_type,
|
||||||
ioloop,
|
ioloop,
|
||||||
unique_id,
|
unique_id,
|
||||||
):
|
):
|
||||||
@ -160,12 +206,15 @@ class KefMediaPlayer(MediaPlayerDevice):
|
|||||||
)
|
)
|
||||||
self._unique_id = unique_id
|
self._unique_id = unique_id
|
||||||
self._supports_on = supports_on
|
self._supports_on = supports_on
|
||||||
|
self._speaker_type = speaker_type
|
||||||
|
|
||||||
self._state = None
|
self._state = None
|
||||||
self._muted = None
|
self._muted = None
|
||||||
self._source = None
|
self._source = None
|
||||||
self._volume = None
|
self._volume = None
|
||||||
self._is_online = None
|
self._is_online = None
|
||||||
|
self._dsp = None
|
||||||
|
self._update_dsp_task_remover = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -190,6 +239,9 @@ class KefMediaPlayer(MediaPlayerDevice):
|
|||||||
state = await self._speaker.get_state()
|
state = await self._speaker.get_state()
|
||||||
self._source = state.source
|
self._source = state.source
|
||||||
self._state = STATE_ON if state.is_on else STATE_OFF
|
self._state = STATE_ON if state.is_on else STATE_OFF
|
||||||
|
if self._dsp is None:
|
||||||
|
# Only do this when necessary because it is a slow operation
|
||||||
|
await self.update_dsp()
|
||||||
else:
|
else:
|
||||||
self._muted = None
|
self._muted = None
|
||||||
self._source = None
|
self._source = None
|
||||||
@ -291,11 +343,11 @@ class KefMediaPlayer(MediaPlayerDevice):
|
|||||||
|
|
||||||
async def async_media_play(self):
|
async def async_media_play(self):
|
||||||
"""Send play command."""
|
"""Send play command."""
|
||||||
await self._speaker.play_pause()
|
await self._speaker.set_play_pause()
|
||||||
|
|
||||||
async def async_media_pause(self):
|
async def async_media_pause(self):
|
||||||
"""Send pause command."""
|
"""Send pause command."""
|
||||||
await self._speaker.play_pause()
|
await self._speaker.set_play_pause()
|
||||||
|
|
||||||
async def async_media_previous_track(self):
|
async def async_media_previous_track(self):
|
||||||
"""Send previous track command."""
|
"""Send previous track command."""
|
||||||
@ -304,3 +356,87 @@ class KefMediaPlayer(MediaPlayerDevice):
|
|||||||
async def async_media_next_track(self):
|
async def async_media_next_track(self):
|
||||||
"""Send next track command."""
|
"""Send next track command."""
|
||||||
await self._speaker.next_track()
|
await self._speaker.next_track()
|
||||||
|
|
||||||
|
async def update_dsp(self) -> None:
|
||||||
|
"""Update the DSP settings."""
|
||||||
|
if self._speaker_type == "LS50" and self._state == STATE_OFF:
|
||||||
|
# The LSX is able to respond when off the LS50 has to be on.
|
||||||
|
return
|
||||||
|
|
||||||
|
(mode, *rest) = await asyncio.gather(
|
||||||
|
self._speaker.get_mode(),
|
||||||
|
self._speaker.get_desk_db(),
|
||||||
|
self._speaker.get_wall_db(),
|
||||||
|
self._speaker.get_treble_db(),
|
||||||
|
self._speaker.get_high_hz(),
|
||||||
|
self._speaker.get_low_hz(),
|
||||||
|
self._speaker.get_sub_db(),
|
||||||
|
)
|
||||||
|
keys = ["desk_db", "wall_db", "treble_db", "high_hz", "low_hz", "sub_db"]
|
||||||
|
self._dsp = dict(zip(keys, rest), **mode._asdict())
|
||||||
|
|
||||||
|
async def async_added_to_hass(self):
|
||||||
|
"""Subscribe to DSP updates."""
|
||||||
|
self._update_dsp_task_remover = async_track_time_interval(
|
||||||
|
self.hass, self.update_dsp, DSP_SCAN_INTERVAL
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self):
|
||||||
|
"""Unsubscribe to DSP updates."""
|
||||||
|
self._update_dsp_task_remover()
|
||||||
|
self._update_dsp_task_remover = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_state_attributes(self):
|
||||||
|
"""Return the DSP settings of the KEF device."""
|
||||||
|
return self._dsp or {}
|
||||||
|
|
||||||
|
async def set_mode(
|
||||||
|
self,
|
||||||
|
desk_mode=None,
|
||||||
|
wall_mode=None,
|
||||||
|
phase_correction=None,
|
||||||
|
high_pass=None,
|
||||||
|
sub_polarity=None,
|
||||||
|
bass_extension=None,
|
||||||
|
):
|
||||||
|
"""Set the speaker mode."""
|
||||||
|
await self._speaker.set_mode(
|
||||||
|
desk_mode=desk_mode,
|
||||||
|
wall_mode=wall_mode,
|
||||||
|
phase_correction=phase_correction,
|
||||||
|
high_pass=high_pass,
|
||||||
|
sub_polarity=sub_polarity,
|
||||||
|
bass_extension=bass_extension,
|
||||||
|
)
|
||||||
|
self._dsp = None
|
||||||
|
|
||||||
|
async def set_desk_db(self, db_value):
|
||||||
|
"""Set desk_db of the KEF speakers."""
|
||||||
|
await self._speaker.set_desk_db(db_value)
|
||||||
|
self._dsp = None
|
||||||
|
|
||||||
|
async def set_wall_db(self, db_value):
|
||||||
|
"""Set wall_db of the KEF speakers."""
|
||||||
|
await self._speaker.set_wall_db(db_value)
|
||||||
|
self._dsp = None
|
||||||
|
|
||||||
|
async def set_treble_db(self, db_value):
|
||||||
|
"""Set treble_db of the KEF speakers."""
|
||||||
|
await self._speaker.set_treble_db(db_value)
|
||||||
|
self._dsp = None
|
||||||
|
|
||||||
|
async def set_high_hz(self, hz_value):
|
||||||
|
"""Set high_hz of the KEF speakers."""
|
||||||
|
await self._speaker.set_high_hz(hz_value)
|
||||||
|
self._dsp = None
|
||||||
|
|
||||||
|
async def set_low_hz(self, hz_value):
|
||||||
|
"""Set low_hz of the KEF speakers."""
|
||||||
|
await self._speaker.set_low_hz(hz_value)
|
||||||
|
self._dsp = None
|
||||||
|
|
||||||
|
async def set_sub_db(self, db_value):
|
||||||
|
"""Set sub_db of the KEF speakers."""
|
||||||
|
await self._speaker.set_sub_db(db_value)
|
||||||
|
self._dsp = None
|
||||||
|
97
homeassistant/components/kef/services.yaml
Normal file
97
homeassistant/components/kef/services.yaml
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
update_dsp:
|
||||||
|
description: Update all DSP settings.
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: The entity_id of the KEF speaker.
|
||||||
|
example: media_player.kef_lsx
|
||||||
|
|
||||||
|
set_mode:
|
||||||
|
description: Set the mode of the speaker.
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: The entity_id of the KEF speaker.
|
||||||
|
example: media_player.kef_lsx
|
||||||
|
desk_mode:
|
||||||
|
description: >
|
||||||
|
"Desk mode" (true or false)
|
||||||
|
example: true
|
||||||
|
wall_mode:
|
||||||
|
description: >
|
||||||
|
"Wall mode" (true or false)
|
||||||
|
example: true
|
||||||
|
phase_correction:
|
||||||
|
description: >
|
||||||
|
"Phase correction" (true or false)
|
||||||
|
example: true
|
||||||
|
high_pass:
|
||||||
|
description: >
|
||||||
|
"High-pass mode" (true or false)
|
||||||
|
example: true
|
||||||
|
sub_polarity:
|
||||||
|
description: >
|
||||||
|
"Sub polarity" ("-" or "+")
|
||||||
|
example: "+"
|
||||||
|
bass_extension:
|
||||||
|
description: >
|
||||||
|
"Bass extension" selector ("Less", "Standard", or "Extra")
|
||||||
|
example: "Extra"
|
||||||
|
|
||||||
|
set_desk_db:
|
||||||
|
description: Set the "Desk mode" slider of the speaker in dB.
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: The entity_id of the KEF speaker.
|
||||||
|
example: media_player.kef_lsx
|
||||||
|
db_value:
|
||||||
|
description: Value of the slider (-6 to 0 with steps of 0.5)
|
||||||
|
example: 0.0
|
||||||
|
|
||||||
|
set_wall_db:
|
||||||
|
description: Set the "Wall mode" slider of the speaker in dB.
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: The entity_id of the KEF speaker.
|
||||||
|
example: media_player.kef_lsx
|
||||||
|
db_value:
|
||||||
|
description: Value of the slider (-6 to 0 with steps of 0.5)
|
||||||
|
example: 0.0
|
||||||
|
|
||||||
|
set_treble_db:
|
||||||
|
description: Set desk the "Treble trim" slider of the speaker in dB.
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: The entity_id of the KEF speaker.
|
||||||
|
example: media_player.kef_lsx
|
||||||
|
db_value:
|
||||||
|
description: Value of the slider (-2 to 2 with steps of 0.5)
|
||||||
|
example: 0.0
|
||||||
|
|
||||||
|
set_high_hz:
|
||||||
|
description: Set the "High-pass mode" slider of the speaker in Hz.
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: The entity_id of the KEF speaker.
|
||||||
|
example: media_player.kef_lsx
|
||||||
|
hz_value:
|
||||||
|
description: Value of the slider (50 to 120 with steps of 5)
|
||||||
|
example: 95
|
||||||
|
|
||||||
|
set_low_hz:
|
||||||
|
description: Set the "Sub out low-pass frequency" slider of the speaker in Hz.
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: The entity_id of the KEF speaker.
|
||||||
|
example: media_player.kef_lsx
|
||||||
|
hz_value:
|
||||||
|
description: Value of the slider (40 to 250 with steps of 5)
|
||||||
|
example: 80
|
||||||
|
|
||||||
|
set_sub_db:
|
||||||
|
description: Set the "Sub gain" slider of the speaker in dB.
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: The entity_id of the KEF speaker.
|
||||||
|
example: media_player.kef_lsx
|
||||||
|
db_value:
|
||||||
|
description: Value of the slider (-10 to 10 with steps of 1)
|
||||||
|
example: 0
|
@ -184,7 +184,7 @@ aioimaplib==0.7.15
|
|||||||
aiokafka==0.5.1
|
aiokafka==0.5.1
|
||||||
|
|
||||||
# homeassistant.components.kef
|
# homeassistant.components.kef
|
||||||
aiokef==0.2.7
|
aiokef==0.2.9
|
||||||
|
|
||||||
# homeassistant.components.lifx
|
# homeassistant.components.lifx
|
||||||
aiolifx==0.6.7
|
aiolifx==0.6.7
|
||||||
|
Loading…
x
Reference in New Issue
Block a user