Add Device and Integration Services to ISY994 (#35467)
* ISY994 Add Services ISY994 Add support for climate platform * Remove device registry cleanup Remove device registry cleanup from service in favor of #35106 * ISY994 Update Services Definitions Rename entry to config_entry_id * Grammar corrections Fix Typo * Add await and lower logging per review. * Rename to entries and remove unused device_id refs * Fix tuple typo * Fix Typo in strings * Fix typo in stringspull/35412/head
parent
0a9b373edb
commit
9eb1505aa1
|
@ -36,6 +36,7 @@ from .const import (
|
|||
UNDO_UPDATE_LISTENER,
|
||||
)
|
||||
from .helpers import _categorize_nodes, _categorize_programs, _categorize_variables
|
||||
from .services import async_setup_services, async_unload_services
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
|
@ -189,6 +190,9 @@ async def async_setup_entry(
|
|||
|
||||
hass_isy_data[UNDO_UPDATE_LISTENER] = undo_listener
|
||||
|
||||
# Register Integration-wide Services:
|
||||
async_setup_services(hass)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
@ -263,4 +267,6 @@ async def async_unload_entry(
|
|||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
async_unload_services(hass)
|
||||
|
||||
return unload_ok
|
||||
|
|
|
@ -50,6 +50,7 @@ from .const import (
|
|||
)
|
||||
from .entity import ISYNodeEntity, ISYProgramEntity
|
||||
from .helpers import migrate_old_unique_ids
|
||||
from .services import async_setup_device_services
|
||||
|
||||
DEVICE_PARENT_REQUIRED = [
|
||||
DEVICE_CLASS_OPENING,
|
||||
|
@ -172,6 +173,7 @@ async def async_setup_entry(
|
|||
|
||||
await migrate_old_unique_ids(hass, BINARY_SENSOR, devices)
|
||||
async_add_entities(devices)
|
||||
async_setup_device_services(hass)
|
||||
|
||||
|
||||
def _detect_device_type_and_class(node: Union[Group, Node]) -> (str, str):
|
||||
|
|
|
@ -55,6 +55,7 @@ from .const import (
|
|||
)
|
||||
from .entity import ISYNodeEntity
|
||||
from .helpers import migrate_old_unique_ids
|
||||
from .services import async_setup_device_services
|
||||
|
||||
ISY_SUPPORTED_FEATURES = (
|
||||
SUPPORT_FAN_MODE | SUPPORT_TARGET_TEMPERATURE | SUPPORT_TARGET_TEMPERATURE_RANGE
|
||||
|
@ -75,6 +76,7 @@ async def async_setup_entry(
|
|||
|
||||
await migrate_old_unique_ids(hass, CLIMATE, entities)
|
||||
async_add_entities(entities)
|
||||
async_setup_device_services(hass)
|
||||
|
||||
|
||||
def convert_isy_temp_to_hass(
|
||||
|
|
|
@ -17,6 +17,7 @@ from .const import (
|
|||
)
|
||||
from .entity import ISYNodeEntity, ISYProgramEntity
|
||||
from .helpers import migrate_old_unique_ids
|
||||
from .services import async_setup_device_services
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
@ -35,6 +36,7 @@ async def async_setup_entry(
|
|||
|
||||
await migrate_old_unique_ids(hass, COVER, devices)
|
||||
async_add_entities(devices)
|
||||
async_setup_device_services(hass)
|
||||
|
||||
|
||||
class ISYCoverEntity(ISYNodeEntity, CoverEntity):
|
||||
|
|
|
@ -14,7 +14,7 @@ from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNKNOWN
|
|||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.typing import Dict
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import _LOGGER, DOMAIN
|
||||
|
||||
|
||||
class ISYEntity(Entity):
|
||||
|
@ -166,6 +166,26 @@ class ISYNodeEntity(ISYEntity):
|
|||
self._attrs.update(attr)
|
||||
return self._attrs
|
||||
|
||||
def send_node_command(self, command):
|
||||
"""Respond to an entity service command call."""
|
||||
if not hasattr(self._node, command):
|
||||
_LOGGER.error(
|
||||
"Invalid Service Call %s for device %s.", command, self.entity_id
|
||||
)
|
||||
return
|
||||
getattr(self._node, command)()
|
||||
|
||||
def send_raw_node_command(
|
||||
self, command, value=None, unit_of_measurement=None, parameters=None
|
||||
):
|
||||
"""Respond to an entity service raw command call."""
|
||||
if not hasattr(self._node, "send_cmd"):
|
||||
_LOGGER.error(
|
||||
"Invalid Service Call %s for device %s.", command, self.entity_id
|
||||
)
|
||||
return
|
||||
self._node.send_cmd(command, value, unit_of_measurement, parameters)
|
||||
|
||||
|
||||
class ISYProgramEntity(ISYEntity):
|
||||
"""Representation of an ISY994 program base."""
|
||||
|
|
|
@ -16,6 +16,7 @@ from homeassistant.helpers.typing import HomeAssistantType
|
|||
from .const import _LOGGER, DOMAIN as ISY994_DOMAIN, ISY994_NODES, ISY994_PROGRAMS
|
||||
from .entity import ISYNodeEntity, ISYProgramEntity
|
||||
from .helpers import migrate_old_unique_ids
|
||||
from .services import async_setup_device_services
|
||||
|
||||
VALUE_TO_STATE = {
|
||||
0: SPEED_OFF,
|
||||
|
@ -48,6 +49,7 @@ async def async_setup_entry(
|
|||
|
||||
await migrate_old_unique_ids(hass, FAN, devices)
|
||||
async_add_entities(devices)
|
||||
async_setup_device_services(hass)
|
||||
|
||||
|
||||
class ISYFanEntity(ISYNodeEntity, FanEntity):
|
||||
|
|
|
@ -21,6 +21,7 @@ from .const import (
|
|||
)
|
||||
from .entity import ISYNodeEntity
|
||||
from .helpers import migrate_old_unique_ids
|
||||
from .services import async_setup_device_services, async_setup_light_services
|
||||
|
||||
ATTR_LAST_BRIGHTNESS = "last_brightness"
|
||||
|
||||
|
@ -41,6 +42,8 @@ async def async_setup_entry(
|
|||
|
||||
await migrate_old_unique_ids(hass, LIGHT, devices)
|
||||
async_add_entities(devices)
|
||||
async_setup_device_services(hass)
|
||||
async_setup_light_services(hass)
|
||||
|
||||
|
||||
class ISYLightEntity(ISYNodeEntity, LightEntity, RestoreEntity):
|
||||
|
@ -110,3 +113,11 @@ class ISYLightEntity(ISYNodeEntity, LightEntity, RestoreEntity):
|
|||
and last_state.attributes[ATTR_LAST_BRIGHTNESS]
|
||||
):
|
||||
self._last_brightness = last_state.attributes[ATTR_LAST_BRIGHTNESS]
|
||||
|
||||
def set_on_level(self, value):
|
||||
"""Set the ON Level for a device."""
|
||||
self._node.set_on_level(value)
|
||||
|
||||
def set_ramp_rate(self, value):
|
||||
"""Set the Ramp Rate for a device."""
|
||||
self._node.set_ramp_rate(value)
|
||||
|
|
|
@ -11,6 +11,7 @@ from homeassistant.helpers.typing import HomeAssistantType
|
|||
from .const import _LOGGER, DOMAIN as ISY994_DOMAIN, ISY994_NODES, ISY994_PROGRAMS
|
||||
from .entity import ISYNodeEntity, ISYProgramEntity
|
||||
from .helpers import migrate_old_unique_ids
|
||||
from .services import async_setup_device_services
|
||||
|
||||
VALUE_TO_STATE = {0: STATE_UNLOCKED, 100: STATE_LOCKED}
|
||||
|
||||
|
@ -31,6 +32,7 @@ async def async_setup_entry(
|
|||
|
||||
await migrate_old_unique_ids(hass, LOCK, devices)
|
||||
async_add_entities(devices)
|
||||
async_setup_device_services(hass)
|
||||
|
||||
|
||||
class ISYLockEntity(ISYNodeEntity, LockEntity):
|
||||
|
|
|
@ -18,6 +18,7 @@ from .const import (
|
|||
)
|
||||
from .entity import ISYEntity, ISYNodeEntity
|
||||
from .helpers import migrate_old_unique_ids
|
||||
from .services import async_setup_device_services
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
@ -38,6 +39,7 @@ async def async_setup_entry(
|
|||
|
||||
await migrate_old_unique_ids(hass, SENSOR, devices)
|
||||
async_add_entities(devices)
|
||||
async_setup_device_services(hass)
|
||||
|
||||
|
||||
class ISYSensorEntity(ISYNodeEntity):
|
||||
|
|
|
@ -0,0 +1,408 @@
|
|||
"""ISY Services and Commands."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pyisy.constants import COMMAND_FRIENDLY_NAME
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_COMMAND,
|
||||
CONF_NAME,
|
||||
CONF_TYPE,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
SERVICE_RELOAD,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import entity_platform
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
import homeassistant.helpers.entity_registry as er
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
from .const import (
|
||||
_LOGGER,
|
||||
DOMAIN,
|
||||
ISY994_ISY,
|
||||
ISY994_NODES,
|
||||
ISY994_PROGRAMS,
|
||||
ISY994_VARIABLES,
|
||||
SUPPORTED_PLATFORMS,
|
||||
SUPPORTED_PROGRAM_PLATFORMS,
|
||||
)
|
||||
|
||||
# Common Services for All Platforms:
|
||||
SERVICE_SYSTEM_QUERY = "system_query"
|
||||
SERVICE_SET_VARIABLE = "set_variable"
|
||||
SERVICE_SEND_PROGRAM_COMMAND = "send_program_command"
|
||||
SERVICE_RUN_NETWORK_RESOURCE = "run_network_resource"
|
||||
SERVICE_CLEANUP = "cleanup_entities"
|
||||
|
||||
INTEGRATION_SERVICES = [
|
||||
SERVICE_SYSTEM_QUERY,
|
||||
SERVICE_SET_VARIABLE,
|
||||
SERVICE_SEND_PROGRAM_COMMAND,
|
||||
SERVICE_RUN_NETWORK_RESOURCE,
|
||||
SERVICE_CLEANUP,
|
||||
]
|
||||
|
||||
# Entity specific methods (valid for most Groups/ISY Scenes, Lights, Switches, Fans)
|
||||
SERVICE_SEND_RAW_NODE_COMMAND = "send_raw_node_command"
|
||||
SERVICE_SEND_NODE_COMMAND = "send_node_command"
|
||||
|
||||
# Services valid only for dimmable lights.
|
||||
SERVICE_SET_ON_LEVEL = "set_on_level"
|
||||
SERVICE_SET_RAMP_RATE = "set_ramp_rate"
|
||||
|
||||
CONF_PARAMETERS = "parameters"
|
||||
CONF_VALUE = "value"
|
||||
CONF_INIT = "init"
|
||||
CONF_ISY = "isy"
|
||||
|
||||
VALID_NODE_COMMANDS = [
|
||||
"beep",
|
||||
"brighten",
|
||||
"dim",
|
||||
"disable",
|
||||
"enable",
|
||||
"fade_down",
|
||||
"fade_stop",
|
||||
"fade_up",
|
||||
"fast_off",
|
||||
"fast_on",
|
||||
"query",
|
||||
]
|
||||
VALID_PROGRAM_COMMANDS = [
|
||||
"run",
|
||||
"run_then",
|
||||
"run_else",
|
||||
"stop",
|
||||
"enable",
|
||||
"disable",
|
||||
"enable_run_at_startup",
|
||||
"disable_run_at_startup",
|
||||
]
|
||||
|
||||
|
||||
def valid_isy_commands(value: Any) -> str:
|
||||
"""Validate the command is valid."""
|
||||
value = str(value).upper()
|
||||
if value in COMMAND_FRIENDLY_NAME.keys():
|
||||
return value
|
||||
raise vol.Invalid("Invalid ISY Command.")
|
||||
|
||||
|
||||
SCHEMA_GROUP = "name-address"
|
||||
|
||||
SERVICE_SYSTEM_QUERY_SCHEMA = vol.Schema(
|
||||
{vol.Optional(CONF_ADDRESS): cv.string, vol.Optional(CONF_ISY): cv.string}
|
||||
)
|
||||
|
||||
SERVICE_SET_RAMP_RATE_SCHEMA = {
|
||||
vol.Required(CONF_VALUE): vol.All(vol.Coerce(int), vol.Range(0, 31))
|
||||
}
|
||||
|
||||
SERVICE_SET_VALUE_SCHEMA = {
|
||||
vol.Required(CONF_VALUE): vol.All(vol.Coerce(int), vol.Range(0, 255))
|
||||
}
|
||||
|
||||
SERVICE_SEND_RAW_NODE_COMMAND_SCHEMA = {
|
||||
vol.Required(CONF_COMMAND): vol.All(cv.string, valid_isy_commands),
|
||||
vol.Optional(CONF_VALUE): vol.All(vol.Coerce(int), vol.Range(0, 255)),
|
||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT): vol.All(vol.Coerce(int), vol.Range(0, 120)),
|
||||
vol.Optional(CONF_PARAMETERS, default={}): {cv.string: cv.string},
|
||||
}
|
||||
|
||||
SERVICE_SEND_NODE_COMMAND_SCHEMA = {
|
||||
vol.Required(CONF_COMMAND): vol.In(VALID_NODE_COMMANDS)
|
||||
}
|
||||
|
||||
SERVICE_SET_VARIABLE_SCHEMA = vol.All(
|
||||
cv.has_at_least_one_key(CONF_ADDRESS, CONF_TYPE, CONF_NAME),
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Exclusive(CONF_NAME, SCHEMA_GROUP): cv.string,
|
||||
vol.Inclusive(CONF_ADDRESS, SCHEMA_GROUP): vol.Coerce(int),
|
||||
vol.Inclusive(CONF_TYPE, SCHEMA_GROUP): vol.All(
|
||||
vol.Coerce(int), vol.Range(1, 2)
|
||||
),
|
||||
vol.Optional(CONF_INIT, default=False): bool,
|
||||
vol.Required(CONF_VALUE): vol.Coerce(int),
|
||||
vol.Optional(CONF_ISY): cv.string,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
SERVICE_SEND_PROGRAM_COMMAND_SCHEMA = vol.All(
|
||||
cv.has_at_least_one_key(CONF_ADDRESS, CONF_NAME),
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Exclusive(CONF_NAME, SCHEMA_GROUP): cv.string,
|
||||
vol.Exclusive(CONF_ADDRESS, SCHEMA_GROUP): cv.string,
|
||||
vol.Required(CONF_COMMAND): vol.In(VALID_PROGRAM_COMMANDS),
|
||||
vol.Optional(CONF_ISY): cv.string,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
SERVICE_RUN_NETWORK_RESOURCE_SCHEMA = vol.All(
|
||||
cv.has_at_least_one_key(CONF_ADDRESS, CONF_NAME),
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Exclusive(CONF_NAME, SCHEMA_GROUP): cv.string,
|
||||
vol.Exclusive(CONF_ADDRESS, SCHEMA_GROUP): vol.Coerce(int),
|
||||
vol.Optional(CONF_ISY): cv.string,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_services(hass: HomeAssistantType):
|
||||
"""Create and register services for the ISY integration."""
|
||||
existing_services = hass.services.async_services().get(DOMAIN)
|
||||
if existing_services and any(
|
||||
service in INTEGRATION_SERVICES for service in existing_services.keys()
|
||||
):
|
||||
# Integration-level services have already been added. Return.
|
||||
return
|
||||
|
||||
async def async_system_query_service_handler(service):
|
||||
"""Handle a system query service call."""
|
||||
address = service.data.get(CONF_ADDRESS)
|
||||
isy_name = service.data.get(CONF_ISY)
|
||||
|
||||
for config_entry_id in hass.data[DOMAIN]:
|
||||
isy = hass.data[DOMAIN][config_entry_id][ISY994_ISY]
|
||||
if isy_name and not isy_name == isy.configuration["name"]:
|
||||
continue
|
||||
# If an address is provided, make sure we query the correct ISY.
|
||||
# Otherwise, query the whole system on all ISY's connected.
|
||||
if address and isy.nodes.get_by_id(address) is not None:
|
||||
_LOGGER.debug(
|
||||
"Requesting query of device %s on ISY %s",
|
||||
address,
|
||||
isy.configuration["uuid"],
|
||||
)
|
||||
await hass.async_add_executor_job(isy.query, address)
|
||||
return
|
||||
_LOGGER.debug(
|
||||
"Requesting system query of ISY %s", isy.configuration["uuid"]
|
||||
)
|
||||
await hass.async_add_executor_job(isy.query)
|
||||
|
||||
async def async_run_network_resource_service_handler(service):
|
||||
"""Handle a network resource service call."""
|
||||
address = service.data.get(CONF_ADDRESS)
|
||||
name = service.data.get(CONF_NAME)
|
||||
isy_name = service.data.get(CONF_ISY)
|
||||
|
||||
for config_entry_id in hass.data[DOMAIN]:
|
||||
isy = hass.data[DOMAIN][config_entry_id][ISY994_ISY]
|
||||
if isy_name and not isy_name == isy.configuration["name"]:
|
||||
continue
|
||||
if not hasattr(isy, "networking") or isy.networking is None:
|
||||
continue
|
||||
command = None
|
||||
if address:
|
||||
command = isy.networking.get_by_id(address)
|
||||
if name:
|
||||
command = isy.networking.get_by_name(name)
|
||||
if command is not None:
|
||||
await hass.async_add_executor_job(command.run)
|
||||
return
|
||||
_LOGGER.error(
|
||||
"Could not run network resource command. Not found or enabled on the ISY."
|
||||
)
|
||||
|
||||
async def async_send_program_command_service_handler(service):
|
||||
"""Handle a send program command service call."""
|
||||
address = service.data.get(CONF_ADDRESS)
|
||||
name = service.data.get(CONF_NAME)
|
||||
command = service.data.get(CONF_COMMAND)
|
||||
isy_name = service.data.get(CONF_ISY)
|
||||
|
||||
for config_entry_id in hass.data[DOMAIN]:
|
||||
isy = hass.data[DOMAIN][config_entry_id][ISY994_ISY]
|
||||
if isy_name and not isy_name == isy.configuration["name"]:
|
||||
continue
|
||||
program = None
|
||||
if address:
|
||||
program = isy.programs.get_by_id(address)
|
||||
if name:
|
||||
program = isy.programs.get_by_name(name)
|
||||
if program is not None:
|
||||
await hass.async_add_executor_job(getattr(program, command))
|
||||
return
|
||||
_LOGGER.error(
|
||||
"Could not send program command. Not found or enabled on the ISY."
|
||||
)
|
||||
|
||||
async def async_set_variable_service_handler(service):
|
||||
"""Handle a set variable service call."""
|
||||
address = service.data.get(CONF_ADDRESS)
|
||||
vtype = service.data.get(CONF_TYPE)
|
||||
name = service.data.get(CONF_NAME)
|
||||
value = service.data.get(CONF_VALUE)
|
||||
init = service.data.get(CONF_INIT, False)
|
||||
isy_name = service.data.get(CONF_ISY)
|
||||
|
||||
for config_entry_id in hass.data[DOMAIN]:
|
||||
isy = hass.data[DOMAIN][config_entry_id][ISY994_ISY]
|
||||
if isy_name and not isy_name == isy.configuration["name"]:
|
||||
continue
|
||||
variable = None
|
||||
if name:
|
||||
variable = isy.variables.get_by_name(name)
|
||||
if address and vtype:
|
||||
variable = isy.variables.vobjs[vtype].get(address)
|
||||
if variable is not None:
|
||||
await hass.async_add_executor_job(variable.set_value, value, init)
|
||||
return
|
||||
_LOGGER.error("Could not set variable value. Not found or enabled on the ISY.")
|
||||
|
||||
async def async_cleanup_registry_entries(service) -> None:
|
||||
"""Remove extra entities that are no longer part of the integration."""
|
||||
entity_registry = await er.async_get_registry(hass)
|
||||
config_ids = []
|
||||
current_unique_ids = []
|
||||
|
||||
for config_entry_id in hass.data[DOMAIN]:
|
||||
entries_for_this_config = er.async_entries_for_config_entry(
|
||||
entity_registry, config_entry_id
|
||||
)
|
||||
config_ids.extend(
|
||||
[
|
||||
(entity.unique_id, entity.entity_id)
|
||||
for entity in entries_for_this_config
|
||||
]
|
||||
)
|
||||
|
||||
hass_isy_data = hass.data[DOMAIN][config_entry_id]
|
||||
uuid = hass_isy_data[ISY994_ISY].configuration["uuid"]
|
||||
|
||||
for platform in SUPPORTED_PLATFORMS:
|
||||
for node in hass_isy_data[ISY994_NODES][platform]:
|
||||
if hasattr(node, "address"):
|
||||
current_unique_ids.append(f"{uuid}_{node.address}")
|
||||
|
||||
for platform in SUPPORTED_PROGRAM_PLATFORMS:
|
||||
for _, node, _ in hass_isy_data[ISY994_PROGRAMS][platform]:
|
||||
if hasattr(node, "address"):
|
||||
current_unique_ids.append(f"{uuid}_{node.address}")
|
||||
|
||||
for node in hass_isy_data[ISY994_VARIABLES]:
|
||||
if hasattr(node, "address"):
|
||||
current_unique_ids.append(f"{uuid}_{node.address}")
|
||||
|
||||
extra_entities = [
|
||||
entity_id
|
||||
for unique_id, entity_id in config_ids
|
||||
if unique_id not in current_unique_ids
|
||||
]
|
||||
|
||||
for entity_id in extra_entities:
|
||||
if entity_registry.async_is_registered(entity_id):
|
||||
entity_registry.async_remove(entity_id)
|
||||
|
||||
_LOGGER.debug(
|
||||
"Cleaning up ISY994 Entities and devices: Config Entries: %s, Current Entries: %s, "
|
||||
"Extra Entries Removed: %s",
|
||||
len(config_ids),
|
||||
len(current_unique_ids),
|
||||
len(extra_entities),
|
||||
)
|
||||
|
||||
async def async_reload_config_entries(service) -> None:
|
||||
"""Trigger a reload of all ISY994 config entries."""
|
||||
for config_entry_id in hass.data[DOMAIN]:
|
||||
hass.async_create_task(hass.config_entries.async_reload(config_entry_id))
|
||||
|
||||
hass.services.async_register(
|
||||
domain=DOMAIN,
|
||||
service=SERVICE_SYSTEM_QUERY,
|
||||
service_func=async_system_query_service_handler,
|
||||
schema=SERVICE_SYSTEM_QUERY_SCHEMA,
|
||||
)
|
||||
|
||||
hass.services.async_register(
|
||||
domain=DOMAIN,
|
||||
service=SERVICE_RUN_NETWORK_RESOURCE,
|
||||
service_func=async_run_network_resource_service_handler,
|
||||
schema=SERVICE_RUN_NETWORK_RESOURCE_SCHEMA,
|
||||
)
|
||||
|
||||
hass.services.async_register(
|
||||
domain=DOMAIN,
|
||||
service=SERVICE_SEND_PROGRAM_COMMAND,
|
||||
service_func=async_send_program_command_service_handler,
|
||||
schema=SERVICE_SEND_PROGRAM_COMMAND_SCHEMA,
|
||||
)
|
||||
|
||||
hass.services.async_register(
|
||||
domain=DOMAIN,
|
||||
service=SERVICE_SET_VARIABLE,
|
||||
service_func=async_set_variable_service_handler,
|
||||
schema=SERVICE_SET_VARIABLE_SCHEMA,
|
||||
)
|
||||
|
||||
hass.services.async_register(
|
||||
domain=DOMAIN,
|
||||
service=SERVICE_CLEANUP,
|
||||
service_func=async_cleanup_registry_entries,
|
||||
)
|
||||
|
||||
hass.services.async_register(
|
||||
domain=DOMAIN, service=SERVICE_RELOAD, service_func=async_reload_config_entries
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
def async_unload_services(hass: HomeAssistantType):
|
||||
"""Unload services for the ISY integration."""
|
||||
if hass.data[DOMAIN]:
|
||||
# There is still another config entry for this domain, don't remove services.
|
||||
return
|
||||
|
||||
existing_services = hass.services.async_services().get(DOMAIN)
|
||||
if not existing_services or not any(
|
||||
service in INTEGRATION_SERVICES for service in existing_services.keys()
|
||||
):
|
||||
return
|
||||
|
||||
_LOGGER.info("Unloading ISY994 Services.")
|
||||
hass.services.async_remove(domain=DOMAIN, service=SERVICE_SYSTEM_QUERY)
|
||||
hass.services.async_remove(domain=DOMAIN, service=SERVICE_RUN_NETWORK_RESOURCE)
|
||||
hass.services.async_remove(domain=DOMAIN, service=SERVICE_SEND_PROGRAM_COMMAND)
|
||||
hass.services.async_remove(domain=DOMAIN, service=SERVICE_SET_VARIABLE)
|
||||
hass.services.async_remove(domain=DOMAIN, service=SERVICE_CLEANUP)
|
||||
hass.services.async_remove(domain=DOMAIN, service=SERVICE_RELOAD)
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_device_services(hass: HomeAssistantType):
|
||||
"""Create device-specific services for the ISY Integration."""
|
||||
platform = entity_platform.current_platform.get()
|
||||
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SEND_RAW_NODE_COMMAND,
|
||||
SERVICE_SEND_RAW_NODE_COMMAND_SCHEMA,
|
||||
SERVICE_SEND_RAW_NODE_COMMAND,
|
||||
)
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SEND_NODE_COMMAND,
|
||||
SERVICE_SEND_NODE_COMMAND_SCHEMA,
|
||||
SERVICE_SEND_NODE_COMMAND,
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_light_services(hass: HomeAssistantType):
|
||||
"""Create device-specific services for the ISY Integration."""
|
||||
platform = entity_platform.current_platform.get()
|
||||
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SET_ON_LEVEL, SERVICE_SET_VALUE_SCHEMA, SERVICE_SET_ON_LEVEL
|
||||
)
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SET_RAMP_RATE, SERVICE_SET_RAMP_RATE_SCHEMA, SERVICE_SET_RAMP_RATE
|
||||
)
|
|
@ -0,0 +1,115 @@
|
|||
# Describes the ISY994-specific services available
|
||||
|
||||
# Note: controlling many entity_ids with one call is not recommended since it may result in
|
||||
# flooding the ISY with requests. To control multiple devices with a service call
|
||||
# the recommendation is to add a scene in the ISY and control that scene.
|
||||
send_raw_node_command:
|
||||
description: Send a "raw" ISY REST Device Command to a Node using its Home Assistant Entity ID.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name of an entity to send command.
|
||||
example: "light.front_door"
|
||||
command:
|
||||
description: The ISY REST Command to be sent to the device
|
||||
example: "DON"
|
||||
value:
|
||||
description: (Optional) The integer value to be sent with the command.
|
||||
example: 255
|
||||
parameters:
|
||||
description: (Optional) A dict of parameters to be sent in the query string (e.g. for controlling colored bulbs).
|
||||
example: { GV2: 0, GV3: 0, GV4: 255 }
|
||||
unit_of_measurement:
|
||||
description: (Optional) The ISY Unit of Measurement (UOM) to send with the command, if required.
|
||||
example: 67
|
||||
send_node_command:
|
||||
description: >-
|
||||
Send a command to an ISY Device using its Home Assistant entity ID. Valid commands are: beep, brighten, dim, disable,
|
||||
enable, fade_down, fade_stop, fade_up, fast_off, fast_on, and query.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name of an entity to send command.
|
||||
example: "light.front_door"
|
||||
command:
|
||||
description: The command to be sent to the device.
|
||||
example: "fast_on"
|
||||
set_on_level:
|
||||
description: Send a ISY set_on_level command to a Node.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name of an entity to send command.
|
||||
example: "light.front_door"
|
||||
value:
|
||||
description: integer value to set (0-255).
|
||||
example: 255
|
||||
set_ramp_rate:
|
||||
description: Send a ISY set_ramp_rate command to a Node.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name of an entity to send command.
|
||||
example: "light.front_door"
|
||||
value:
|
||||
description: Integer value to set (0-31), see PyISY/ISY documentation for values to actual ramp times.
|
||||
example: 30
|
||||
system_query:
|
||||
description: Request the ISY Query the connected devices.
|
||||
fields:
|
||||
address:
|
||||
description: (Optional) ISY Address to Query. Omitting this requests a system-wide scan (typically scheduled once per day).
|
||||
example: "1A 2B 3C 1"
|
||||
isy:
|
||||
description: (Optional) If you have more than one ISY connected, provide the name of the ISY to query (as shown on the Device Registry or as the top-first node in the ISY Admin Console). Omitting this will cause all ISYs to be queried.
|
||||
example: "ISY"
|
||||
set_variable:
|
||||
description: Set an ISY variable's current or initial value. Variables can be set by either type/address or by name.
|
||||
fields:
|
||||
address:
|
||||
description: The address of the variable for which to set the value.
|
||||
example: 5
|
||||
type:
|
||||
description: The variable type, 1 = Integer, 2 = State.
|
||||
example: 2
|
||||
name:
|
||||
description: (Optional) The name of the variable to set (use instead of type/address).
|
||||
example: "my_variable_name"
|
||||
init:
|
||||
description: (Optional) If True, the initial (init) value will be updated instead of the current value.
|
||||
example: false
|
||||
value:
|
||||
description: The integer value to be sent.
|
||||
example: 255
|
||||
isy:
|
||||
description: (Optional) If you have more than one ISY connected, provide the name of the ISY to query (as shown on the Device Registry or as the top-first node in the ISY Admin Console). If you have the same variable name or address on multiple ISYs, omitting this will run the command on them all.
|
||||
example: "ISY"
|
||||
send_program_command:
|
||||
description: >-
|
||||
Send a command to control an ISY program or folder. Valid commands are run, run_then, run_else, stop, enable, disable,
|
||||
enable_run_at_startup, and disable_run_at_startup.
|
||||
fields:
|
||||
address:
|
||||
description: The address of the program to control (optional, use either address or name).
|
||||
example: "04B1"
|
||||
name:
|
||||
description: The name of the program to control (optional, use either address or name).
|
||||
example: "My Program"
|
||||
command:
|
||||
description: The ISY Program Command to be sent.
|
||||
example: "run"
|
||||
isy:
|
||||
description: (Optional) If you have more than one ISY connected, provide the name of the ISY to query (as shown on the Device Registry or as the top-first node in the ISY Admin Console). If you have the same program name or address on multiple ISYs, omitting this will run the command on them all.
|
||||
example: "ISY"
|
||||
run_network_resource:
|
||||
description: Run a network resource on the ISY.
|
||||
fields:
|
||||
address:
|
||||
description: The address of the network resource to execute (optional, use either address or name).
|
||||
example: 121
|
||||
name:
|
||||
description: The name of the network resource to execute (optional, use either address or name).
|
||||
example: "Network Resource 1"
|
||||
isy:
|
||||
description: (Optional) If you have more than one ISY connected, provide the name of the ISY to query (as shown on the Device Registry or as the top-first node in the ISY Admin Console). If you have the same resource name or address on multiple ISYs, omitting this will run the command on them all.
|
||||
example: "ISY"
|
||||
reload:
|
||||
description: Reload the ISY994 connection(s) without restarting Home Assistant. Use to pick up new devices that have been added or changed on the ISY.
|
||||
cleanup_entities:
|
||||
description: Cleanup old entities and devices no longer used by the ISY994 integrations. Useful if you've removed devices from the ISY or changed the options in the configuration to exclude additional items.
|
|
@ -14,13 +14,13 @@
|
|||
}
|
||||
},
|
||||
"error": {
|
||||
"unknown": "[%key:common::config_flow::error::unknown%",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]",
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"invalid_host": "The host entry was not in full URL format, e.g., http://192.168.10.100:80"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%"
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
|
|
@ -11,6 +11,7 @@ from homeassistant.helpers.typing import HomeAssistantType
|
|||
from .const import _LOGGER, DOMAIN as ISY994_DOMAIN, ISY994_NODES, ISY994_PROGRAMS
|
||||
from .entity import ISYNodeEntity, ISYProgramEntity
|
||||
from .helpers import migrate_old_unique_ids
|
||||
from .services import async_setup_device_services
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
@ -29,6 +30,7 @@ async def async_setup_entry(
|
|||
|
||||
await migrate_old_unique_ids(hass, SWITCH, devices)
|
||||
async_add_entities(devices)
|
||||
async_setup_device_services(hass)
|
||||
|
||||
|
||||
class ISYSwitchEntity(ISYNodeEntity, SwitchEntity):
|
||||
|
|
Loading…
Reference in New Issue