diff --git a/homeassistant/components/zha/api.py b/homeassistant/components/zha/api.py index 7ef42ef7da7aa20561c84d14eb047fe8fc9a44c6..df6b6591bacca081e5e1ea6d2212770059119ee8 100644 --- a/homeassistant/components/zha/api.py +++ b/homeassistant/components/zha/api.py @@ -14,7 +14,8 @@ import homeassistant.helpers.config_validation as cv from .core.const import ( DOMAIN, ATTR_CLUSTER_ID, ATTR_CLUSTER_TYPE, ATTR_ATTRIBUTE, ATTR_VALUE, ATTR_MANUFACTURER, ATTR_COMMAND, ATTR_COMMAND_TYPE, ATTR_ARGS, IN, OUT, - CLIENT_COMMANDS, SERVER_COMMANDS, SERVER, NAME, ATTR_ENDPOINT_ID) + CLIENT_COMMANDS, SERVER_COMMANDS, SERVER, NAME, ATTR_ENDPOINT_ID, + DATA_ZHA_GATEWAY, DATA_ZHA) from .core.helpers import get_matched_clusters, async_is_bindable_target _LOGGER = logging.getLogger(__name__) @@ -71,73 +72,308 @@ SERVICE_SCHEMAS = { }), } -WS_RECONFIGURE_NODE = 'zha/devices/reconfigure' -SCHEMA_WS_RECONFIGURE_NODE = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ - vol.Required(TYPE): WS_RECONFIGURE_NODE, - vol.Required(ATTR_IEEE): str -}) -WS_DEVICES = 'zha/devices' -SCHEMA_WS_LIST_DEVICES = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ - vol.Required(TYPE): WS_DEVICES, +@websocket_api.async_response +@websocket_api.websocket_command({ + vol.Required(TYPE): 'zha/devices' }) - -WS_DEVICE_CLUSTERS = 'zha/devices/clusters' -SCHEMA_WS_CLUSTERS = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ - vol.Required(TYPE): WS_DEVICE_CLUSTERS, +async def websocket_get_devices(hass, connection, msg): + """Get ZHA devices.""" + zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] + devices = [ + { + **device.device_info, + 'entities': [{ + 'entity_id': entity_ref.reference_id, + NAME: entity_ref.device_info[NAME] + } for entity_ref in zha_gateway.device_registry[device.ieee]] + } for device in zha_gateway.devices.values() + ] + + connection.send_result(msg[ID], devices) + + +@websocket_api.async_response +@websocket_api.websocket_command({ + vol.Required(TYPE): 'zha/devices/reconfigure', vol.Required(ATTR_IEEE): str }) - -WS_DEVICE_CLUSTER_ATTRIBUTES = 'zha/devices/clusters/attributes' -SCHEMA_WS_CLUSTER_ATTRIBUTES = \ - websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ - vol.Required(TYPE): WS_DEVICE_CLUSTER_ATTRIBUTES, - vol.Required(ATTR_IEEE): str, - vol.Required(ATTR_ENDPOINT_ID): int, - vol.Required(ATTR_CLUSTER_ID): int, - vol.Required(ATTR_CLUSTER_TYPE): str - }) - -WS_READ_CLUSTER_ATTRIBUTE = 'zha/devices/clusters/attributes/value' -SCHEMA_WS_READ_CLUSTER_ATTRIBUTE = \ - websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ - vol.Required(TYPE): WS_READ_CLUSTER_ATTRIBUTE, - vol.Required(ATTR_IEEE): str, - vol.Required(ATTR_ENDPOINT_ID): int, - vol.Required(ATTR_CLUSTER_ID): int, - vol.Required(ATTR_CLUSTER_TYPE): str, - vol.Required(ATTR_ATTRIBUTE): int, - vol.Optional(ATTR_MANUFACTURER): object, - }) - -WS_DEVICE_CLUSTER_COMMANDS = 'zha/devices/clusters/commands' -SCHEMA_WS_CLUSTER_COMMANDS = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ - vol.Required(TYPE): WS_DEVICE_CLUSTER_COMMANDS, +async def websocket_reconfigure_node(hass, connection, msg): + """Reconfigure a ZHA nodes entities by its ieee address.""" + zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] + ieee = msg[ATTR_IEEE] + device = zha_gateway.get_device(ieee) + _LOGGER.debug("Reconfiguring node with ieee_address: %s", ieee) + hass.async_create_task(device.async_configure()) + + +@websocket_api.async_response +@websocket_api.websocket_command({ + vol.Required(TYPE): 'zha/devices/clusters', + vol.Required(ATTR_IEEE): str +}) +async def websocket_device_clusters(hass, connection, msg): + """Return a list of device clusters.""" + zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] + ieee = msg[ATTR_IEEE] + zha_device = zha_gateway.get_device(ieee) + response_clusters = [] + if zha_device is not None: + clusters_by_endpoint = zha_device.async_get_clusters() + for ep_id, clusters in clusters_by_endpoint.items(): + for c_id, cluster in clusters[IN].items(): + response_clusters.append({ + TYPE: IN, + ID: c_id, + NAME: cluster.__class__.__name__, + 'endpoint_id': ep_id + }) + for c_id, cluster in clusters[OUT].items(): + response_clusters.append({ + TYPE: OUT, + ID: c_id, + NAME: cluster.__class__.__name__, + 'endpoint_id': ep_id + }) + + connection.send_result(msg[ID], response_clusters) + + +@websocket_api.async_response +@websocket_api.websocket_command({ + vol.Required(TYPE): 'zha/devices/clusters/attributes', vol.Required(ATTR_IEEE): str, vol.Required(ATTR_ENDPOINT_ID): int, vol.Required(ATTR_CLUSTER_ID): int, vol.Required(ATTR_CLUSTER_TYPE): str }) - -WS_BIND_DEVICE = 'zha/devices/bind' -SCHEMA_WS_BIND_DEVICE = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ - vol.Required(TYPE): WS_BIND_DEVICE, +async def websocket_device_cluster_attributes(hass, connection, msg): + """Return a list of cluster attributes.""" + zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] + ieee = msg[ATTR_IEEE] + endpoint_id = msg[ATTR_ENDPOINT_ID] + cluster_id = msg[ATTR_CLUSTER_ID] + cluster_type = msg[ATTR_CLUSTER_TYPE] + cluster_attributes = [] + zha_device = zha_gateway.get_device(ieee) + attributes = None + if zha_device is not None: + attributes = zha_device.async_get_cluster_attributes( + endpoint_id, + cluster_id, + cluster_type) + if attributes is not None: + for attr_id in attributes: + cluster_attributes.append( + { + ID: attr_id, + NAME: attributes[attr_id][0] + } + ) + _LOGGER.debug("Requested attributes for: %s %s %s %s", + "{}: [{}]".format(ATTR_CLUSTER_ID, cluster_id), + "{}: [{}]".format(ATTR_CLUSTER_TYPE, cluster_type), + "{}: [{}]".format(ATTR_ENDPOINT_ID, endpoint_id), + "{}: [{}]".format(RESPONSE, cluster_attributes) + ) + + connection.send_result(msg[ID], cluster_attributes) + + +@websocket_api.async_response +@websocket_api.websocket_command({ + vol.Required(TYPE): 'zha/devices/clusters/commands', + vol.Required(ATTR_IEEE): str, + vol.Required(ATTR_ENDPOINT_ID): int, + vol.Required(ATTR_CLUSTER_ID): int, + vol.Required(ATTR_CLUSTER_TYPE): str +}) +async def websocket_device_cluster_commands(hass, connection, msg): + """Return a list of cluster commands.""" + zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] + cluster_id = msg[ATTR_CLUSTER_ID] + cluster_type = msg[ATTR_CLUSTER_TYPE] + ieee = msg[ATTR_IEEE] + endpoint_id = msg[ATTR_ENDPOINT_ID] + zha_device = zha_gateway.get_device(ieee) + cluster_commands = [] + commands = None + if zha_device is not None: + commands = zha_device.async_get_cluster_commands( + endpoint_id, + cluster_id, + cluster_type) + + if commands is not None: + for cmd_id in commands[CLIENT_COMMANDS]: + cluster_commands.append( + { + TYPE: CLIENT, + ID: cmd_id, + NAME: commands[CLIENT_COMMANDS][cmd_id][0] + } + ) + for cmd_id in commands[SERVER_COMMANDS]: + cluster_commands.append( + { + TYPE: SERVER, + ID: cmd_id, + NAME: commands[SERVER_COMMANDS][cmd_id][0] + } + ) + _LOGGER.debug("Requested commands for: %s %s %s %s", + "{}: [{}]".format(ATTR_CLUSTER_ID, cluster_id), + "{}: [{}]".format(ATTR_CLUSTER_TYPE, cluster_type), + "{}: [{}]".format(ATTR_ENDPOINT_ID, endpoint_id), + "{}: [{}]".format(RESPONSE, cluster_commands) + ) + + connection.send_result(msg[ID], cluster_commands) + + +@websocket_api.async_response +@websocket_api.websocket_command({ + vol.Required(TYPE): 'zha/devices/clusters/attributes/value', + vol.Required(ATTR_IEEE): str, + vol.Required(ATTR_ENDPOINT_ID): int, + vol.Required(ATTR_CLUSTER_ID): int, + vol.Required(ATTR_CLUSTER_TYPE): str, + vol.Required(ATTR_ATTRIBUTE): int, + vol.Optional(ATTR_MANUFACTURER): object, +}) +async def websocket_read_zigbee_cluster_attributes(hass, connection, msg): + """Read zigbee attribute for cluster on zha entity.""" + zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] + ieee = msg[ATTR_IEEE] + endpoint_id = msg[ATTR_ENDPOINT_ID] + cluster_id = msg[ATTR_CLUSTER_ID] + cluster_type = msg[ATTR_CLUSTER_TYPE] + attribute = msg[ATTR_ATTRIBUTE] + manufacturer = msg.get(ATTR_MANUFACTURER) or None + zha_device = zha_gateway.get_device(ieee) + success = failure = None + if zha_device is not None: + cluster = zha_device.async_get_cluster( + endpoint_id, cluster_id, cluster_type=cluster_type) + success, failure = await cluster.read_attributes( + [attribute], + allow_cache=False, + only_cache=False, + manufacturer=manufacturer + ) + _LOGGER.debug("Read attribute for: %s %s %s %s %s %s %s", + "{}: [{}]".format(ATTR_CLUSTER_ID, cluster_id), + "{}: [{}]".format(ATTR_CLUSTER_TYPE, cluster_type), + "{}: [{}]".format(ATTR_ENDPOINT_ID, endpoint_id), + "{}: [{}]".format(ATTR_ATTRIBUTE, attribute), + "{}: [{}]".format(ATTR_MANUFACTURER, manufacturer), + "{}: [{}]".format(RESPONSE, str(success.get(attribute))), + "{}: [{}]".format('failure', failure) + ) + connection.send_result(msg[ID], str(success.get(attribute))) + + +@websocket_api.async_response +@websocket_api.websocket_command({ + vol.Required(TYPE): 'zha/devices/bindable', + vol.Required(ATTR_IEEE): str, +}) +async def websocket_get_bindable_devices(hass, connection, msg): + """Directly bind devices.""" + zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] + source_ieee = msg[ATTR_IEEE] + source_device = zha_gateway.get_device(source_ieee) + devices = [ + { + **device.device_info + } for device in zha_gateway.devices.values() if + async_is_bindable_target(source_device, device) + ] + + _LOGGER.debug("Get bindable devices: %s %s", + "{}: [{}]".format(ATTR_SOURCE_IEEE, source_ieee), + "{}: [{}]".format('bindable devices:', devices) + ) + + connection.send_message(websocket_api.result_message( + msg[ID], + devices + )) + + +@websocket_api.async_response +@websocket_api.websocket_command({ + vol.Required(TYPE): 'zha/devices/bind', vol.Required(ATTR_SOURCE_IEEE): str, vol.Required(ATTR_TARGET_IEEE): str }) - -WS_UNBIND_DEVICE = 'zha/devices/unbind' -SCHEMA_WS_UNBIND_DEVICE = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ - vol.Required(TYPE): WS_UNBIND_DEVICE, +async def websocket_bind_devices(hass, connection, msg): + """Directly bind devices.""" + zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] + source_ieee = msg[ATTR_SOURCE_IEEE] + target_ieee = msg[ATTR_TARGET_IEEE] + await async_binding_operation( + zha_gateway, source_ieee, target_ieee, BIND_REQUEST) + _LOGGER.info("Issue bind devices: %s %s", + "{}: [{}]".format(ATTR_SOURCE_IEEE, source_ieee), + "{}: [{}]".format(ATTR_TARGET_IEEE, target_ieee) + ) + + +@websocket_api.async_response +@websocket_api.websocket_command({ + vol.Required(TYPE): 'zha/devices/unbind', vol.Required(ATTR_SOURCE_IEEE): str, vol.Required(ATTR_TARGET_IEEE): str }) - -WS_BINDABLE_DEVICES = 'zha/devices/bindable' -SCHEMA_WS_BINDABLE_DEVICES = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ - vol.Required(TYPE): WS_BINDABLE_DEVICES, - vol.Required(ATTR_IEEE): str -}) +async def websocket_unbind_devices(hass, connection, msg): + """Remove a direct binding between devices.""" + zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] + source_ieee = msg[ATTR_SOURCE_IEEE] + target_ieee = msg[ATTR_TARGET_IEEE] + await async_binding_operation( + zha_gateway, source_ieee, target_ieee, UNBIND_REQUEST) + _LOGGER.info("Issue unbind devices: %s %s", + "{}: [{}]".format(ATTR_SOURCE_IEEE, source_ieee), + "{}: [{}]".format(ATTR_TARGET_IEEE, target_ieee) + ) + + +async def async_binding_operation(zha_gateway, source_ieee, target_ieee, + operation): + """Create or remove a direct zigbee binding between 2 devices.""" + from zigpy.zdo import types as zdo_types + source_device = zha_gateway.get_device(source_ieee) + target_device = zha_gateway.get_device(target_ieee) + + clusters_to_bind = await get_matched_clusters(source_device, + target_device) + + bind_tasks = [] + for cluster_pair in clusters_to_bind: + destination_address = zdo_types.MultiAddress() + destination_address.addrmode = 3 + destination_address.ieee = target_device.ieee + destination_address.endpoint = \ + cluster_pair.target_cluster.endpoint.endpoint_id + + zdo = cluster_pair.source_cluster.endpoint.device.zdo + + _LOGGER.debug("processing binding operation for: %s %s %s", + "{}: [{}]".format(ATTR_SOURCE_IEEE, source_ieee), + "{}: [{}]".format(ATTR_TARGET_IEEE, target_ieee), + "{}: {}".format( + 'cluster', + cluster_pair.source_cluster.cluster_id) + ) + bind_tasks.append(zdo.request( + operation, + source_device.ieee, + cluster_pair.source_cluster.endpoint.endpoint_id, + cluster_pair.source_cluster.cluster_id, + destination_address + )) + await asyncio.gather(*bind_tasks) def async_load_api(hass, application_controller, zha_gateway): @@ -237,301 +473,18 @@ def async_load_api(hass, application_controller, zha_gateway): SERVICE_ISSUE_ZIGBEE_CLUSTER_COMMAND ]) - @websocket_api.async_response - async def websocket_get_devices(hass, connection, msg): - """Get ZHA devices.""" - devices = [ - { - **device.device_info, - 'entities': [{ - 'entity_id': entity_ref.reference_id, - NAME: entity_ref.device_info[NAME] - } for entity_ref in zha_gateway.device_registry[device.ieee]] - } for device in zha_gateway.devices.values() - ] - - connection.send_message(websocket_api.result_message( - msg[ID], - devices - )) - - hass.components.websocket_api.async_register_command( - WS_DEVICES, websocket_get_devices, - SCHEMA_WS_LIST_DEVICES - ) - - @websocket_api.async_response - async def websocket_reconfigure_node(hass, connection, msg): - """Reconfigure a ZHA nodes entities by its ieee address.""" - ieee = msg[ATTR_IEEE] - device = zha_gateway.get_device(ieee) - _LOGGER.debug("Reconfiguring node with ieee_address: %s", ieee) - hass.async_create_task(device.async_configure()) - - hass.components.websocket_api.async_register_command( - WS_RECONFIGURE_NODE, websocket_reconfigure_node, - SCHEMA_WS_RECONFIGURE_NODE - ) - - @websocket_api.async_response - async def websocket_get_bindable_devices(hass, connection, msg): - """Directly bind devices.""" - source_ieee = msg[ATTR_IEEE] - source_device = zha_gateway.get_device(source_ieee) - devices = [ - { - **device.device_info - } for device in zha_gateway.devices.values() if - async_is_bindable_target(source_device, device) - ] - - _LOGGER.debug("Get bindable devices: %s %s", - "{}: [{}]".format(ATTR_SOURCE_IEEE, source_ieee), - "{}: [{}]".format('bindable devices:', devices) - ) - - connection.send_message(websocket_api.result_message( - msg[ID], - devices - )) - - hass.components.websocket_api.async_register_command( - WS_BINDABLE_DEVICES, websocket_get_bindable_devices, - SCHEMA_WS_BINDABLE_DEVICES - ) - - @websocket_api.async_response - async def websocket_bind_devices(hass, connection, msg): - """Directly bind devices.""" - source_ieee = msg[ATTR_SOURCE_IEEE] - target_ieee = msg[ATTR_TARGET_IEEE] - await async_binding_operation( - source_ieee, target_ieee, BIND_REQUEST) - _LOGGER.info("Issue bind devices: %s %s", - "{}: [{}]".format(ATTR_SOURCE_IEEE, source_ieee), - "{}: [{}]".format(ATTR_TARGET_IEEE, target_ieee) - ) - - hass.components.websocket_api.async_register_command( - WS_BIND_DEVICE, websocket_bind_devices, - SCHEMA_WS_BIND_DEVICE - ) - - @websocket_api.async_response - async def websocket_unbind_devices(hass, connection, msg): - """Remove a direct binding between devices.""" - source_ieee = msg[ATTR_SOURCE_IEEE] - target_ieee = msg[ATTR_TARGET_IEEE] - await async_binding_operation( - source_ieee, target_ieee, UNBIND_REQUEST) - _LOGGER.info("Issue unbind devices: %s %s", - "{}: [{}]".format(ATTR_SOURCE_IEEE, source_ieee), - "{}: [{}]".format(ATTR_TARGET_IEEE, target_ieee) - ) - - hass.components.websocket_api.async_register_command( - WS_UNBIND_DEVICE, websocket_unbind_devices, - SCHEMA_WS_UNBIND_DEVICE - ) - - async def async_binding_operation(source_ieee, target_ieee, - operation): - """Create or remove a direct zigbee binding between 2 devices.""" - from zigpy.zdo import types as zdo_types - source_device = zha_gateway.get_device(source_ieee) - target_device = zha_gateway.get_device(target_ieee) - - clusters_to_bind = await get_matched_clusters(source_device, - target_device) - - bind_tasks = [] - for cluster_pair in clusters_to_bind: - destination_address = zdo_types.MultiAddress() - destination_address.addrmode = 3 - destination_address.ieee = target_device.ieee - destination_address.endpoint = \ - cluster_pair.target_cluster.endpoint.endpoint_id - - zdo = cluster_pair.source_cluster.endpoint.device.zdo - - _LOGGER.debug("processing binding operation for: %s %s %s", - "{}: [{}]".format(ATTR_SOURCE_IEEE, source_ieee), - "{}: [{}]".format(ATTR_TARGET_IEEE, target_ieee), - "{}: {}".format( - 'cluster', - cluster_pair.source_cluster.cluster_id) - ) - bind_tasks.append(zdo.request( - operation, - source_device.ieee, - cluster_pair.source_cluster.endpoint.endpoint_id, - cluster_pair.source_cluster.cluster_id, - destination_address - )) - await asyncio.gather(*bind_tasks) - - @websocket_api.async_response - async def websocket_device_clusters(hass, connection, msg): - """Return a list of device clusters.""" - ieee = msg[ATTR_IEEE] - zha_device = zha_gateway.get_device(ieee) - response_clusters = [] - if zha_device is not None: - clusters_by_endpoint = zha_device.async_get_clusters() - for ep_id, clusters in clusters_by_endpoint.items(): - for c_id, cluster in clusters[IN].items(): - response_clusters.append({ - TYPE: IN, - ID: c_id, - NAME: cluster.__class__.__name__, - 'endpoint_id': ep_id - }) - for c_id, cluster in clusters[OUT].items(): - response_clusters.append({ - TYPE: OUT, - ID: c_id, - NAME: cluster.__class__.__name__, - 'endpoint_id': ep_id - }) - - connection.send_message(websocket_api.result_message( - msg[ID], - response_clusters - )) - - hass.components.websocket_api.async_register_command( - WS_DEVICE_CLUSTERS, websocket_device_clusters, - SCHEMA_WS_CLUSTERS - ) - - @websocket_api.async_response - async def websocket_device_cluster_attributes(hass, connection, msg): - """Return a list of cluster attributes.""" - ieee = msg[ATTR_IEEE] - endpoint_id = msg[ATTR_ENDPOINT_ID] - cluster_id = msg[ATTR_CLUSTER_ID] - cluster_type = msg[ATTR_CLUSTER_TYPE] - cluster_attributes = [] - zha_device = zha_gateway.get_device(ieee) - attributes = None - if zha_device is not None: - attributes = zha_device.async_get_cluster_attributes( - endpoint_id, - cluster_id, - cluster_type) - if attributes is not None: - for attr_id in attributes: - cluster_attributes.append( - { - ID: attr_id, - NAME: attributes[attr_id][0] - } - ) - _LOGGER.debug("Requested attributes for: %s %s %s %s", - "{}: [{}]".format(ATTR_CLUSTER_ID, cluster_id), - "{}: [{}]".format(ATTR_CLUSTER_TYPE, cluster_type), - "{}: [{}]".format(ATTR_ENDPOINT_ID, endpoint_id), - "{}: [{}]".format(RESPONSE, cluster_attributes) - ) - - connection.send_message(websocket_api.result_message( - msg[ID], - cluster_attributes - )) - - hass.components.websocket_api.async_register_command( - WS_DEVICE_CLUSTER_ATTRIBUTES, websocket_device_cluster_attributes, - SCHEMA_WS_CLUSTER_ATTRIBUTES - ) - - @websocket_api.async_response - async def websocket_device_cluster_commands(hass, connection, msg): - """Return a list of cluster commands.""" - cluster_id = msg[ATTR_CLUSTER_ID] - cluster_type = msg[ATTR_CLUSTER_TYPE] - ieee = msg[ATTR_IEEE] - endpoint_id = msg[ATTR_ENDPOINT_ID] - zha_device = zha_gateway.get_device(ieee) - cluster_commands = [] - commands = None - if zha_device is not None: - commands = zha_device.async_get_cluster_commands( - endpoint_id, - cluster_id, - cluster_type) - - if commands is not None: - for cmd_id in commands[CLIENT_COMMANDS]: - cluster_commands.append( - { - TYPE: CLIENT, - ID: cmd_id, - NAME: commands[CLIENT_COMMANDS][cmd_id][0] - } - ) - for cmd_id in commands[SERVER_COMMANDS]: - cluster_commands.append( - { - TYPE: SERVER, - ID: cmd_id, - NAME: commands[SERVER_COMMANDS][cmd_id][0] - } - ) - _LOGGER.debug("Requested commands for: %s %s %s %s", - "{}: [{}]".format(ATTR_CLUSTER_ID, cluster_id), - "{}: [{}]".format(ATTR_CLUSTER_TYPE, cluster_type), - "{}: [{}]".format(ATTR_ENDPOINT_ID, endpoint_id), - "{}: [{}]".format(RESPONSE, cluster_commands) - ) - - connection.send_message(websocket_api.result_message( - msg[ID], - cluster_commands - )) - - hass.components.websocket_api.async_register_command( - WS_DEVICE_CLUSTER_COMMANDS, websocket_device_cluster_commands, - SCHEMA_WS_CLUSTER_COMMANDS - ) - - @websocket_api.async_response - async def websocket_read_zigbee_cluster_attributes(hass, connection, msg): - """Read zigbee attribute for cluster on zha entity.""" - ieee = msg[ATTR_IEEE] - endpoint_id = msg[ATTR_ENDPOINT_ID] - cluster_id = msg[ATTR_CLUSTER_ID] - cluster_type = msg[ATTR_CLUSTER_TYPE] - attribute = msg[ATTR_ATTRIBUTE] - manufacturer = msg.get(ATTR_MANUFACTURER) or None - zha_device = zha_gateway.get_device(ieee) - success = failure = None - if zha_device is not None: - cluster = zha_device.async_get_cluster( - endpoint_id, cluster_id, cluster_type=cluster_type) - success, failure = await cluster.read_attributes( - [attribute], - allow_cache=False, - only_cache=False, - manufacturer=manufacturer - ) - _LOGGER.debug("Read attribute for: %s %s %s %s %s %s %s", - "{}: [{}]".format(ATTR_CLUSTER_ID, cluster_id), - "{}: [{}]".format(ATTR_CLUSTER_TYPE, cluster_type), - "{}: [{}]".format(ATTR_ENDPOINT_ID, endpoint_id), - "{}: [{}]".format(ATTR_ATTRIBUTE, attribute), - "{}: [{}]".format(ATTR_MANUFACTURER, manufacturer), - "{}: [{}]".format(RESPONSE, str(success.get(attribute))), - "{}: [{}]".format('failure', failure) - ) - connection.send_message(websocket_api.result_message( - msg[ID], - str(success.get(attribute)) - )) - - hass.components.websocket_api.async_register_command( - WS_READ_CLUSTER_ATTRIBUTE, websocket_read_zigbee_cluster_attributes, - SCHEMA_WS_READ_CLUSTER_ATTRIBUTE - ) + websocket_api.async_register_command(hass, websocket_get_devices) + websocket_api.async_register_command(hass, websocket_reconfigure_node) + websocket_api.async_register_command(hass, websocket_device_clusters) + websocket_api.async_register_command( + hass, websocket_device_cluster_attributes) + websocket_api.async_register_command( + hass, websocket_device_cluster_commands) + websocket_api.async_register_command( + hass, websocket_read_zigbee_cluster_attributes) + websocket_api.async_register_command(hass, websocket_get_bindable_devices) + websocket_api.async_register_command(hass, websocket_bind_devices) + websocket_api.async_register_command(hass, websocket_unbind_devices) def async_unload_api(hass): diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index 3c8adb097482117f0deef03be126dd48d59396d2..757ffbaa328c4b7ce1e7293df686d55091d951a8 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -14,6 +14,7 @@ DATA_ZHA_RADIO = 'zha_radio' DATA_ZHA_DISPATCHERS = 'zha_dispatchers' DATA_ZHA_CORE_COMPONENT = 'zha_core_component' DATA_ZHA_CORE_EVENTS = 'zha_core_events' +DATA_ZHA_GATEWAY = 'zha_gateway' ZHA_DISCOVERY_NEW = 'zha_discovery_new_{}' COMPONENTS = [ diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index 35a79311253e75eb9bbaa7c5962e3a132c71c285..a498e1e8ee17bd425b758298e6d821f4eb3d501a 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -22,7 +22,8 @@ from .const import ( OCCUPANCY, CLUSTER_REPORT_CONFIGS, REPORT_CONFIG_IMMEDIATE, REPORT_CONFIG_ASAP, REPORT_CONFIG_DEFAULT, REPORT_CONFIG_MIN_INT, REPORT_CONFIG_MAX_INT, REPORT_CONFIG_OP, SIGNAL_REMOVE, - NO_SENSOR_CLUSTERS, POWER_CONFIGURATION_CHANNEL, BINDABLE_CLUSTERS) + NO_SENSOR_CLUSTERS, POWER_CONFIGURATION_CHANNEL, BINDABLE_CLUSTERS, + DATA_ZHA_GATEWAY) from .device import ZHADevice, DeviceStatus from ..device_entity import ZhaDeviceEntity from .channels import ( @@ -52,6 +53,7 @@ class ZHAGateway: self._devices = {} self._device_registry = collections.defaultdict(list) hass.data[DATA_ZHA][DATA_ZHA_CORE_COMPONENT] = self._component + hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] = self def device_joined(self, device): """Handle device joined. diff --git a/tests/components/zha/test_api.py b/tests/components/zha/test_api.py index 616a94e8b89928ae547fca528ea5a29d3f31d9ca..5858c7560d986221d1d24352e0ec3cad7304e246 100644 --- a/tests/components/zha/test_api.py +++ b/tests/components/zha/test_api.py @@ -3,9 +3,7 @@ from unittest.mock import Mock import pytest from homeassistant.components.switch import DOMAIN from homeassistant.components.zha.api import ( - async_load_api, WS_DEVICE_CLUSTERS, ATTR_IEEE, TYPE, - ID, WS_DEVICE_CLUSTER_ATTRIBUTES, WS_DEVICE_CLUSTER_COMMANDS, - WS_DEVICES + async_load_api, ATTR_IEEE, TYPE, ID ) from homeassistant.components.zha.core.const import ( ATTR_CLUSTER_ID, ATTR_CLUSTER_TYPE, IN, IEEE, MODEL, NAME, QUIRK_APPLIED, @@ -38,7 +36,7 @@ async def test_device_clusters(hass, config_entry, zha_gateway, zha_client): """Test getting device cluster info.""" await zha_client.send_json({ ID: 5, - TYPE: WS_DEVICE_CLUSTERS, + TYPE: 'zha/devices/clusters', ATTR_IEEE: '00:0d:6f:00:0a:90:69:e7' }) @@ -64,7 +62,7 @@ async def test_device_cluster_attributes( """Test getting device cluster attributes.""" await zha_client.send_json({ ID: 5, - TYPE: WS_DEVICE_CLUSTER_ATTRIBUTES, + TYPE: 'zha/devices/clusters/attributes', ATTR_ENDPOINT_ID: 1, ATTR_IEEE: '00:0d:6f:00:0a:90:69:e7', ATTR_CLUSTER_ID: 6, @@ -86,7 +84,7 @@ async def test_device_cluster_commands( """Test getting device cluster commands.""" await zha_client.send_json({ ID: 5, - TYPE: WS_DEVICE_CLUSTER_COMMANDS, + TYPE: 'zha/devices/clusters/commands', ATTR_ENDPOINT_ID: 1, ATTR_IEEE: '00:0d:6f:00:0a:90:69:e7', ATTR_CLUSTER_ID: 6, @@ -109,7 +107,7 @@ async def test_list_devices( """Test getting entity cluster commands.""" await zha_client.send_json({ ID: 5, - TYPE: WS_DEVICES + TYPE: 'zha/devices' }) msg = await zha_client.receive_json()