Add network resource button entities to ISY994 and bump PyISY to 3.0.12 (#85429)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
cdafd94550
commit
a8cdb86b23
12 changed files with 146 additions and 36 deletions
|
@ -7,6 +7,7 @@ from urllib.parse import urlparse
|
|||
from aiohttp import CookieJar
|
||||
import async_timeout
|
||||
from pyisy import ISY, ISYConnectionError, ISYInvalidAuthError, ISYResponseParseError
|
||||
from pyisy.constants import PROTO_NETWORK_RESOURCE
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
|
@ -38,9 +39,19 @@ from .const import (
|
|||
ISY994_NODES,
|
||||
ISY994_PROGRAMS,
|
||||
ISY994_VARIABLES,
|
||||
ISY_CONF_FIRMWARE,
|
||||
ISY_CONF_MODEL,
|
||||
ISY_CONF_NAME,
|
||||
ISY_CONF_NETWORKING,
|
||||
ISY_CONF_UUID,
|
||||
ISY_CONN_ADDRESS,
|
||||
ISY_CONN_PORT,
|
||||
ISY_CONN_TLS,
|
||||
MANUFACTURER,
|
||||
PLATFORMS,
|
||||
PROGRAM_PLATFORMS,
|
||||
SCHEME_HTTP,
|
||||
SCHEME_HTTPS,
|
||||
SENSOR_AUX,
|
||||
)
|
||||
from .helpers import _categorize_nodes, _categorize_programs, _categorize_variables
|
||||
|
@ -122,7 +133,7 @@ async def async_setup_entry(
|
|||
hass.data[DOMAIN][entry.entry_id] = {}
|
||||
hass_isy_data = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
hass_isy_data[ISY994_NODES] = {SENSOR_AUX: []}
|
||||
hass_isy_data[ISY994_NODES] = {SENSOR_AUX: [], PROTO_NETWORK_RESOURCE: []}
|
||||
for platform in PLATFORMS:
|
||||
hass_isy_data[ISY994_NODES][platform] = []
|
||||
|
||||
|
@ -148,13 +159,13 @@ async def async_setup_entry(
|
|||
CONF_VAR_SENSOR_STRING, DEFAULT_VAR_SENSOR_STRING
|
||||
)
|
||||
|
||||
if host.scheme == "http":
|
||||
if host.scheme == SCHEME_HTTP:
|
||||
https = False
|
||||
port = host.port or 80
|
||||
session = aiohttp_client.async_create_clientsession(
|
||||
hass, verify_ssl=False, cookie_jar=CookieJar(unsafe=True)
|
||||
)
|
||||
elif host.scheme == "https":
|
||||
elif host.scheme == SCHEME_HTTPS:
|
||||
https = True
|
||||
port = host.port or 443
|
||||
session = aiohttp_client.async_get_clientsession(hass)
|
||||
|
@ -202,6 +213,9 @@ async def async_setup_entry(
|
|||
_categorize_nodes(hass_isy_data, isy.nodes, ignore_identifier, sensor_identifier)
|
||||
_categorize_programs(hass_isy_data, isy.programs)
|
||||
_categorize_variables(hass_isy_data, isy.variables, variable_identifier)
|
||||
if isy.configuration[ISY_CONF_NETWORKING]:
|
||||
for resource in isy.networking.nobjs:
|
||||
hass_isy_data[ISY994_NODES][PROTO_NETWORK_RESOURCE].append(resource)
|
||||
|
||||
# Dump ISY Clock Information. Future: Add ISY as sensor to Hass with attrs
|
||||
_LOGGER.info(repr(isy.clock))
|
||||
|
@ -262,8 +276,8 @@ def _async_import_options_from_data_if_missing(
|
|||
def _async_isy_to_configuration_url(isy: ISY) -> str:
|
||||
"""Extract the configuration url from the isy."""
|
||||
connection_info = isy.conn.connection_info
|
||||
proto = "https" if "tls" in connection_info else "http"
|
||||
return f"{proto}://{connection_info['addr']}:{connection_info['port']}"
|
||||
proto = SCHEME_HTTPS if ISY_CONN_TLS in connection_info else SCHEME_HTTP
|
||||
return f"{proto}://{connection_info[ISY_CONN_ADDRESS]}:{connection_info[ISY_CONN_PORT]}"
|
||||
|
||||
|
||||
@callback
|
||||
|
@ -274,12 +288,12 @@ def _async_get_or_create_isy_device_in_registry(
|
|||
url = _async_isy_to_configuration_url(isy)
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, isy.configuration["uuid"])},
|
||||
identifiers={(DOMAIN, isy.configuration["uuid"])},
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, isy.configuration[ISY_CONF_UUID])},
|
||||
identifiers={(DOMAIN, isy.configuration[ISY_CONF_UUID])},
|
||||
manufacturer=MANUFACTURER,
|
||||
name=isy.configuration["name"],
|
||||
model=isy.configuration["model"],
|
||||
sw_version=isy.configuration["firmware"],
|
||||
name=isy.configuration[ISY_CONF_NAME],
|
||||
model=isy.configuration[ISY_CONF_MODEL],
|
||||
sw_version=isy.configuration[ISY_CONF_FIRMWARE],
|
||||
configuration_url=url,
|
||||
)
|
||||
|
||||
|
|
|
@ -2,17 +2,29 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pyisy import ISY
|
||||
from pyisy.constants import PROTO_INSTEON
|
||||
from pyisy.constants import PROTO_INSTEON, PROTO_NETWORK_RESOURCE
|
||||
from pyisy.nodes import Node
|
||||
|
||||
from homeassistant.components.button import ButtonEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType
|
||||
from homeassistant.helpers.entity import DeviceInfo, EntityCategory
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN as ISY994_DOMAIN, ISY994_ISY, ISY994_NODES
|
||||
from . import _async_isy_to_configuration_url
|
||||
from .const import (
|
||||
DOMAIN as ISY994_DOMAIN,
|
||||
ISY994_ISY,
|
||||
ISY994_NODES,
|
||||
ISY_CONF_FIRMWARE,
|
||||
ISY_CONF_MODEL,
|
||||
ISY_CONF_NAME,
|
||||
ISY_CONF_NETWORKING,
|
||||
ISY_CONF_UUID,
|
||||
MANUFACTURER,
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
@ -23,13 +35,23 @@ async def async_setup_entry(
|
|||
"""Set up ISY/IoX button from config entry."""
|
||||
hass_isy_data = hass.data[ISY994_DOMAIN][config_entry.entry_id]
|
||||
isy: ISY = hass_isy_data[ISY994_ISY]
|
||||
uuid = isy.configuration["uuid"]
|
||||
entities: list[ISYNodeQueryButtonEntity | ISYNodeBeepButtonEntity] = []
|
||||
for node in hass_isy_data[ISY994_NODES][Platform.BUTTON]:
|
||||
uuid = isy.configuration[ISY_CONF_UUID]
|
||||
entities: list[
|
||||
ISYNodeQueryButtonEntity
|
||||
| ISYNodeBeepButtonEntity
|
||||
| ISYNetworkResourceButtonEntity
|
||||
] = []
|
||||
nodes: dict = hass_isy_data[ISY994_NODES]
|
||||
for node in nodes[Platform.BUTTON]:
|
||||
entities.append(ISYNodeQueryButtonEntity(node, f"{uuid}_{node.address}"))
|
||||
if node.protocol == PROTO_INSTEON:
|
||||
entities.append(ISYNodeBeepButtonEntity(node, f"{uuid}_{node.address}"))
|
||||
|
||||
for node in nodes[PROTO_NETWORK_RESOURCE]:
|
||||
entities.append(
|
||||
ISYNetworkResourceButtonEntity(node, f"{uuid}_{PROTO_NETWORK_RESOURCE}")
|
||||
)
|
||||
|
||||
# Add entity to query full system
|
||||
entities.append(ISYNodeQueryButtonEntity(isy, uuid))
|
||||
|
||||
|
@ -80,3 +102,39 @@ class ISYNodeBeepButtonEntity(ButtonEntity):
|
|||
async def async_press(self) -> None:
|
||||
"""Press the button."""
|
||||
await self._node.beep()
|
||||
|
||||
|
||||
class ISYNetworkResourceButtonEntity(ButtonEntity):
|
||||
"""Representation of an ISY/IoX Network Resource button entity."""
|
||||
|
||||
_attr_should_poll = False
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(self, node: Node, base_unique_id: str) -> None:
|
||||
"""Initialize an ISY network resource button entity."""
|
||||
self._node = node
|
||||
|
||||
# Entity class attributes
|
||||
self._attr_name = node.name
|
||||
self._attr_unique_id = f"{base_unique_id}_{node.address}"
|
||||
url = _async_isy_to_configuration_url(node.isy)
|
||||
config = node.isy.configuration
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={
|
||||
(
|
||||
ISY994_DOMAIN,
|
||||
f"{config[ISY_CONF_UUID]}_{PROTO_NETWORK_RESOURCE}",
|
||||
)
|
||||
},
|
||||
manufacturer=MANUFACTURER,
|
||||
name=f"{config[ISY_CONF_NAME]} {ISY_CONF_NETWORKING}",
|
||||
model=config[ISY_CONF_MODEL],
|
||||
sw_version=config[ISY_CONF_FIRMWARE],
|
||||
configuration_url=url,
|
||||
via_device=(ISY994_DOMAIN, config[ISY_CONF_UUID]),
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
)
|
||||
|
||||
async def async_press(self) -> None:
|
||||
"""Press the button."""
|
||||
await self._node.run()
|
||||
|
|
|
@ -34,6 +34,8 @@ from .const import (
|
|||
DOMAIN,
|
||||
HTTP_PORT,
|
||||
HTTPS_PORT,
|
||||
ISY_CONF_NAME,
|
||||
ISY_CONF_UUID,
|
||||
ISY_URL_POSTFIX,
|
||||
SCHEME_HTTP,
|
||||
SCHEME_HTTPS,
|
||||
|
@ -106,11 +108,14 @@ async def validate_input(
|
|||
isy_conf = Configuration(xml=isy_conf_xml)
|
||||
except ISYResponseParseError as error:
|
||||
raise CannotConnect from error
|
||||
if not isy_conf or "name" not in isy_conf or not isy_conf["name"]:
|
||||
if not isy_conf or ISY_CONF_NAME not in isy_conf or not isy_conf[ISY_CONF_NAME]:
|
||||
raise CannotConnect
|
||||
|
||||
# Return info that you want to store in the config entry.
|
||||
return {"title": f"{isy_conf['name']} ({host.hostname})", "uuid": isy_conf["uuid"]}
|
||||
return {
|
||||
"title": f"{isy_conf[ISY_CONF_NAME]} ({host.hostname})",
|
||||
ISY_CONF_UUID: isy_conf[ISY_CONF_UUID],
|
||||
}
|
||||
|
||||
|
||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
@ -151,7 +156,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
errors["base"] = "unknown"
|
||||
|
||||
if not errors:
|
||||
await self.async_set_unique_id(info["uuid"], raise_on_progress=False)
|
||||
await self.async_set_unique_id(
|
||||
info[ISY_CONF_UUID], raise_on_progress=False
|
||||
)
|
||||
self._abort_if_unique_id_configured()
|
||||
return self.async_create_entry(title=info["title"], data=user_input)
|
||||
|
||||
|
|
|
@ -104,6 +104,16 @@ ISY994_NODES = "isy994_nodes"
|
|||
ISY994_PROGRAMS = "isy994_programs"
|
||||
ISY994_VARIABLES = "isy994_variables"
|
||||
|
||||
ISY_CONF_NETWORKING = "Networking Module"
|
||||
ISY_CONF_UUID = "uuid"
|
||||
ISY_CONF_NAME = "name"
|
||||
ISY_CONF_MODEL = "model"
|
||||
ISY_CONF_FIRMWARE = "firmware"
|
||||
|
||||
ISY_CONN_PORT = "port"
|
||||
ISY_CONN_ADDRESS = "addr"
|
||||
ISY_CONN_TLS = "tls"
|
||||
|
||||
FILTER_UOM = "uom"
|
||||
FILTER_STATES = "states"
|
||||
FILTER_NODE_DEF_ID = "node_def_id"
|
||||
|
|
|
@ -21,7 +21,7 @@ from homeassistant.exceptions import HomeAssistantError
|
|||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||
|
||||
from . import _async_isy_to_configuration_url
|
||||
from .const import DOMAIN
|
||||
from .const import DOMAIN, ISY_CONF_UUID
|
||||
|
||||
|
||||
class ISYEntity(Entity):
|
||||
|
@ -73,7 +73,7 @@ class ISYEntity(Entity):
|
|||
def device_info(self) -> DeviceInfo | None:
|
||||
"""Return the device_info of the device."""
|
||||
isy = self._node.isy
|
||||
uuid = isy.configuration["uuid"]
|
||||
uuid = isy.configuration[ISY_CONF_UUID]
|
||||
node = self._node
|
||||
url = _async_isy_to_configuration_url(isy)
|
||||
|
||||
|
@ -127,7 +127,7 @@ class ISYEntity(Entity):
|
|||
def unique_id(self) -> str | None:
|
||||
"""Get the unique identifier of the device."""
|
||||
if hasattr(self._node, "address"):
|
||||
return f"{self._node.isy.configuration['uuid']}_{self._node.address}"
|
||||
return f"{self._node.isy.configuration[ISY_CONF_UUID]}_{self._node.address}"
|
||||
return None
|
||||
|
||||
@property
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Universal Devices ISY/IoX",
|
||||
"integration_type": "hub",
|
||||
"documentation": "https://www.home-assistant.io/integrations/isy994",
|
||||
"requirements": ["pyisy==3.0.11"],
|
||||
"requirements": ["pyisy==3.0.12"],
|
||||
"codeowners": ["@bdraco", "@shbatm"],
|
||||
"config_flow": true,
|
||||
"ssdp": [
|
||||
|
|
|
@ -36,6 +36,7 @@ from .const import (
|
|||
DOMAIN as ISY994_DOMAIN,
|
||||
ISY994_NODES,
|
||||
ISY994_VARIABLES,
|
||||
ISY_CONF_UUID,
|
||||
SENSOR_AUX,
|
||||
UOM_DOUBLE_TEMP,
|
||||
UOM_FRIENDLY_NAME,
|
||||
|
@ -255,7 +256,7 @@ class ISYAuxSensorEntity(ISYSensorEntity):
|
|||
"""Get the unique identifier of the device and aux sensor."""
|
||||
if not hasattr(self._node, "address"):
|
||||
return None
|
||||
return f"{self._node.isy.configuration['uuid']}_{self._node.address}_{self._control}"
|
||||
return f"{self._node.isy.configuration[ISY_CONF_UUID]}_{self._node.address}_{self._control}"
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
|
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pyisy.constants import COMMAND_FRIENDLY_NAME
|
||||
from pyisy.constants import COMMAND_FRIENDLY_NAME, PROTO_NETWORK_RESOURCE
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
|
@ -23,7 +23,14 @@ import homeassistant.helpers.entity_registry as er
|
|||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
from homeassistant.helpers.service import entity_service_call
|
||||
|
||||
from .const import _LOGGER, DOMAIN, ISY994_ISY
|
||||
from .const import (
|
||||
_LOGGER,
|
||||
DOMAIN,
|
||||
ISY994_ISY,
|
||||
ISY_CONF_NAME,
|
||||
ISY_CONF_NETWORKING,
|
||||
ISY_CONF_UUID,
|
||||
)
|
||||
from .util import unique_ids_for_config_entry_id
|
||||
|
||||
# Common Services for All Platforms:
|
||||
|
@ -194,7 +201,7 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
|
|||
_LOGGER.debug(
|
||||
"Requesting query of device %s on ISY %s",
|
||||
address,
|
||||
isy.configuration["uuid"],
|
||||
isy.configuration[ISY_CONF_UUID],
|
||||
)
|
||||
await isy.query(address)
|
||||
async_log_deprecated_service_call(
|
||||
|
@ -204,13 +211,13 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
|
|||
alternate_target=entity_registry.async_get_entity_id(
|
||||
Platform.BUTTON,
|
||||
DOMAIN,
|
||||
f"{isy.configuration['uuid']}_{address}_query",
|
||||
f"{isy.configuration[ISY_CONF_UUID]}_{address}_query",
|
||||
),
|
||||
breaks_in_ha_version="2023.5.0",
|
||||
)
|
||||
return
|
||||
_LOGGER.debug(
|
||||
"Requesting system query of ISY %s", isy.configuration["uuid"]
|
||||
"Requesting system query of ISY %s", isy.configuration[ISY_CONF_UUID]
|
||||
)
|
||||
await isy.query()
|
||||
async_log_deprecated_service_call(
|
||||
|
@ -218,7 +225,7 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
|
|||
call=service,
|
||||
alternate_service="button.press",
|
||||
alternate_target=entity_registry.async_get_entity_id(
|
||||
Platform.BUTTON, DOMAIN, f"{isy.configuration['uuid']}_query"
|
||||
Platform.BUTTON, DOMAIN, f"{isy.configuration[ISY_CONF_UUID]}_query"
|
||||
),
|
||||
breaks_in_ha_version="2023.5.0",
|
||||
)
|
||||
|
@ -231,9 +238,9 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
|
|||
|
||||
for config_entry_id in hass.data[DOMAIN]:
|
||||
isy = hass.data[DOMAIN][config_entry_id][ISY994_ISY]
|
||||
if isy_name and isy_name != isy.configuration["name"]:
|
||||
if isy_name and isy_name != isy.configuration[ISY_CONF_NAME]:
|
||||
continue
|
||||
if not hasattr(isy, "networking") or isy.networking is None:
|
||||
if isy.networking is None or not isy.configuration[ISY_CONF_NETWORKING]:
|
||||
continue
|
||||
command = None
|
||||
if address:
|
||||
|
@ -242,6 +249,18 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
|
|||
command = isy.networking.get_by_name(name)
|
||||
if command is not None:
|
||||
await command.run()
|
||||
entity_registry = er.async_get(hass)
|
||||
async_log_deprecated_service_call(
|
||||
hass,
|
||||
call=service,
|
||||
alternate_service="button.press",
|
||||
alternate_target=entity_registry.async_get_entity_id(
|
||||
Platform.BUTTON,
|
||||
DOMAIN,
|
||||
f"{isy.configuration[ISY_CONF_UUID]}_{PROTO_NETWORK_RESOURCE}_{address}",
|
||||
),
|
||||
breaks_in_ha_version="2023.5.0",
|
||||
)
|
||||
return
|
||||
_LOGGER.error(
|
||||
"Could not run network resource command; not found or enabled on the ISY"
|
||||
|
|
|
@ -267,8 +267,8 @@ send_program_command:
|
|||
selector:
|
||||
text:
|
||||
run_network_resource:
|
||||
name: Run network resource
|
||||
description: Run a network resource on the ISY.
|
||||
name: Run network resource (Deprecated)
|
||||
description: "Run a network resource on the ISY. Deprecated: Use Network Resource button entity."
|
||||
fields:
|
||||
address:
|
||||
name: Address
|
||||
|
|
|
@ -9,6 +9,7 @@ from .const import (
|
|||
ISY994_NODES,
|
||||
ISY994_PROGRAMS,
|
||||
ISY994_VARIABLES,
|
||||
ISY_CONF_UUID,
|
||||
PLATFORMS,
|
||||
PROGRAM_PLATFORMS,
|
||||
)
|
||||
|
@ -19,7 +20,7 @@ def unique_ids_for_config_entry_id(
|
|||
) -> set[str]:
|
||||
"""Find all the unique ids for a config entry id."""
|
||||
hass_isy_data = hass.data[DOMAIN][config_entry_id]
|
||||
uuid = hass_isy_data[ISY994_ISY].configuration["uuid"]
|
||||
uuid = hass_isy_data[ISY994_ISY].configuration[ISY_CONF_UUID]
|
||||
current_unique_ids: set[str] = {uuid}
|
||||
|
||||
for platform in PLATFORMS:
|
||||
|
|
|
@ -1693,7 +1693,7 @@ pyirishrail==0.0.2
|
|||
pyiss==1.0.1
|
||||
|
||||
# homeassistant.components.isy994
|
||||
pyisy==3.0.11
|
||||
pyisy==3.0.12
|
||||
|
||||
# homeassistant.components.itach
|
||||
pyitachip2ir==0.0.7
|
||||
|
|
|
@ -1206,7 +1206,7 @@ pyiqvia==2022.04.0
|
|||
pyiss==1.0.1
|
||||
|
||||
# homeassistant.components.isy994
|
||||
pyisy==3.0.11
|
||||
pyisy==3.0.12
|
||||
|
||||
# homeassistant.components.kaleidescape
|
||||
pykaleidescape==1.0.1
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue