Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 9 additions & 36 deletions homeassistant/components/denonavr/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
"""The denonavr component."""
import logging

import voluptuous as vol

from homeassistant import config_entries, core
from homeassistant.const import ATTR_COMMAND, ATTR_ENTITY_ID, CONF_HOST
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers.dispatcher import dispatcher_send
from homeassistant.const import CONF_HOST
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.httpx_client import get_async_client

from .config_flow import (
CONF_SHOW_ALL_SOURCES,
Expand All @@ -23,34 +20,9 @@

CONF_RECEIVER = "receiver"
UNDO_UPDATE_LISTENER = "undo_update_listener"
SERVICE_GET_COMMAND = "get_command"

_LOGGER = logging.getLogger(__name__)

CALL_SCHEMA = vol.Schema({vol.Required(ATTR_ENTITY_ID): cv.comp_entity_ids})

GET_COMMAND_SCHEMA = CALL_SCHEMA.extend({vol.Required(ATTR_COMMAND): cv.string})

SERVICE_TO_METHOD = {
SERVICE_GET_COMMAND: {"method": "get_command", "schema": GET_COMMAND_SCHEMA}
}


def setup(hass: core.HomeAssistant, config: dict):
"""Set up the denonavr platform."""

def service_handler(service):
method = SERVICE_TO_METHOD.get(service.service)
data = service.data.copy()
data["method"] = method["method"]
dispatcher_send(hass, DOMAIN, data)

for service in SERVICE_TO_METHOD:
schema = SERVICE_TO_METHOD[service]["schema"]
hass.services.register(DOMAIN, service, service_handler, schema=schema)

return True


async def async_setup_entry(
hass: core.HomeAssistant, entry: config_entries.ConfigEntry
Expand All @@ -60,15 +32,15 @@ async def async_setup_entry(

# Connect to receiver
Comment thread
bdraco marked this conversation as resolved.
Outdated
connect_denonavr = ConnectDenonAVR(
hass,
entry.data[CONF_HOST],
DEFAULT_TIMEOUT,
entry.options.get(CONF_SHOW_ALL_SOURCES, DEFAULT_SHOW_SOURCES),
entry.options.get(CONF_ZONE2, DEFAULT_ZONE2),
entry.options.get(CONF_ZONE3, DEFAULT_ZONE3),
lambda: get_async_client(hass),
entry.state,
)
if not await connect_denonavr.async_connect_receiver():
raise ConfigEntryNotReady
await connect_denonavr.async_connect_receiver()
Comment thread
bdraco marked this conversation as resolved.
Outdated
receiver = connect_denonavr.receiver

undo_listener = entry.add_update_listener(update_listener)
Expand Down Expand Up @@ -98,8 +70,9 @@ async def async_unload_entry(
# Remove zone2 and zone3 entities if needed
entity_registry = await er.async_get_registry(hass)
entries = er.async_entries_for_config_entry(entity_registry, config_entry.entry_id)
zone2_id = f"{config_entry.unique_id}-Zone2"
zone3_id = f"{config_entry.unique_id}-Zone3"
unique_id = config_entry.unique_id or config_entry.entry_id
zone2_id = f"{unique_id}-Zone2"
zone3_id = f"{unique_id}-Zone3"
for entry in entries:
if entry.unique_id == zone2_id and not config_entry.options.get(CONF_ZONE2):
entity_registry.async_remove(entry.entity_id)
Expand Down
65 changes: 29 additions & 36 deletions homeassistant/components/denonavr/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
"""Config flow to configure Denon AVR receivers using their HTTP interface."""
from functools import partial
import logging
from typing import Any, Dict, Optional
from urllib.parse import urlparse

import denonavr
from getmac import get_mac_address
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.components import ssdp
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_TYPE
from homeassistant.const import CONF_HOST, CONF_TYPE
from homeassistant.core import callback
from homeassistant.helpers.device_registry import format_mac
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.httpx_client import get_async_client

from .receiver import ConnectDenonAVR

Expand Down Expand Up @@ -44,7 +44,7 @@ def __init__(self, config_entry: config_entries.ConfigEntry):
"""Init object."""
self.config_entry = config_entry

async def async_step_init(self, user_input=None):
async def async_step_init(self, user_input: Optional[Dict[str, Any]] = None):
"""Manage the options."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
Expand Down Expand Up @@ -90,11 +90,13 @@ def __init__(self):

@staticmethod
@callback
def async_get_options_flow(config_entry) -> OptionsFlowHandler:
def async_get_options_flow(
config_entry: config_entries.ConfigEntry,
) -> OptionsFlowHandler:
"""Get the options flow."""
return OptionsFlowHandler(config_entry)

async def async_step_user(self, user_input=None):
async def async_step_user(self, user_input: Optional[Dict[str, Any]] = None):
"""Handle a flow initialized by the user."""
errors = {}
if user_input is not None:
Expand All @@ -105,7 +107,7 @@ async def async_step_user(self, user_input=None):
return await self.async_step_connect()

# discovery using denonavr library
self.d_receivers = await self.hass.async_add_executor_job(denonavr.discover)
self.d_receivers = await denonavr.async_discover()
# More than one receiver could be discovered by that method
if len(self.d_receivers) == 1:
self.host = self.d_receivers[0]["host"]
Expand All @@ -120,7 +122,9 @@ async def async_step_user(self, user_input=None):
step_id="user", data_schema=CONFIG_SCHEMA, errors=errors
)

async def async_step_select(self, user_input=None):
async def async_step_select(
self, user_input: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Handle multiple receivers found."""
errors = {}
if user_input is not None:
Expand All @@ -139,29 +143,37 @@ async def async_step_select(self, user_input=None):
step_id="select", data_schema=select_scheme, errors=errors
)

async def async_step_confirm(self, user_input=None):
async def async_step_confirm(
self, user_input: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Allow the user to confirm adding the device."""
if user_input is not None:
return await self.async_step_connect()

self._set_confirm_only()
return self.async_show_form(step_id="confirm")
Comment thread
bdraco marked this conversation as resolved.

async def async_step_connect(self, user_input=None):
async def async_step_connect(
self, user_input: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Connect to the receiver."""
connect_denonavr = ConnectDenonAVR(
self.hass,
Comment thread
ol-iver marked this conversation as resolved.
self.host,
self.timeout,
self.show_all_sources,
self.zone2,
self.zone3,
lambda: get_async_client(self.hass),
)
if not await connect_denonavr.async_connect_receiver():

try:
success = await connect_denonavr.async_connect_receiver()
except ConfigEntryNotReady:
success = False
Comment thread
bdraco marked this conversation as resolved.
if not success:
return self.async_abort(reason="cannot_connect")
receiver = connect_denonavr.receiver

mac_address = await self.async_get_mac(self.host)

if not self.serial_number:
self.serial_number = receiver.serial_number
if not self.model_name:
Expand All @@ -185,15 +197,14 @@ async def async_step_connect(self, user_input=None):
title=receiver.name,
data={
CONF_HOST: self.host,
CONF_MAC: mac_address,
CONF_TYPE: receiver.receiver_type,
CONF_MODEL: self.model_name,
CONF_MANUFACTURER: receiver.manufacturer,
CONF_SERIAL_NUMBER: self.serial_number,
},
)

async def async_step_ssdp(self, discovery_info):
async def async_step_ssdp(self, discovery_info: Dict[str, Any]) -> Dict[str, Any]:
"""Handle a discovered Denon AVR.

This flow is triggered by the SSDP component. It will check if the
Expand Down Expand Up @@ -235,24 +246,6 @@ async def async_step_ssdp(self, discovery_info):
return await self.async_step_confirm()

@staticmethod
def construct_unique_id(model_name, serial_number):
def construct_unique_id(model_name: str, serial_number: str) -> str:
"""Construct the unique id from the ssdp discovery or user_step."""
return f"{model_name}-{serial_number}"

async def async_get_mac(self, host):
"""Get the mac address of the DenonAVR receiver."""
try:
mac_address = await self.hass.async_add_executor_job(
partial(get_mac_address, **{"ip": host})
)
if not mac_address:
mac_address = await self.hass.async_add_executor_job(
partial(get_mac_address, **{"hostname": host})
)
except Exception as err: # pylint: disable=broad-except
_LOGGER.error("Unable to get mac address: %s", err)
mac_address = None

if mac_address is not None:
mac_address = format_mac(mac_address)
return mac_address
2 changes: 1 addition & 1 deletion homeassistant/components/denonavr/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "Denon AVR Network Receivers",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/denonavr",
"requirements": ["denonavr==0.9.10", "getmac==0.8.2"],
"requirements": ["denonavr==0.10.5"],
"codeowners": ["@scarface-4711", "@starkillerOG"],
"ssdp": [
{
Expand Down
Loading