async def websocket_playerqueue_settings( hass: HomeAssistant, connection: ActiveConnection, msg: dict, mass: MusicAssistant, ) -> None: """Set PlayerQueue setting/preference.""" if player_queue := mass.players.get_player_queue(msg[QUEUE_ID]): for key, value in msg["settings"].items(): setattr(player_queue.settings, key, value) connection.send_result( msg[ID], "OK", ) return
async def websocket_check_for_config_updates( hass: HomeAssistant, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Check for config updates.""" config_update = await client.driver.async_check_for_config_updates() connection.send_result( msg[ID], { "update_available": config_update.update_available, "new_version": config_update.new_version, }, )
async def websocket_add_playlist_tracks( hass: HomeAssistant, connection: ActiveConnection, msg: dict, mass: MusicAssistant, ) -> None: """Add playlist tracks command.""" if isinstance(msg[URI], list): await mass.music.playlists.add_playlist_tracks(msg[ITEM_ID], msg[URI]) else: await mass.music.playlists.add_playlist_track(msg[ITEM_ID], msg[URI]) connection.send_result( msg[ID], "OK", )
async def websocket_get_version( hass: HomeAssistant, connection: ActiveConnection, msg: dict ) -> None: """Handle get version command.""" integration = await async_get_integration(hass, "frontend") frontend = None for req in integration.requirements: if req.startswith("home-assistant-frontend=="): frontend = req.split("==", 1)[1] if frontend is None: connection.send_error(msg["id"], "unknown_version", "Version not found") else: connection.send_result(msg["id"], {"version": frontend})
async def websocket_playlists( hass: HomeAssistant, connection: ActiveConnection, msg: dict, mass: MusicAssistant, ) -> None: """Return playlists.""" result = [ item.to_dict() for item in await mass.music.playlists.library(limit=msg[LIMIT], offset=msg[OFFSET]) ] connection.send_result( msg[ID], result, )
async def websocket_remove_playlist_tracks( hass: HomeAssistant, connection: ActiveConnection, msg: dict, mass: MusicAssistant, ) -> None: """Add playlist tracks command.""" positions = msg[POSITION] if isinstance(msg[POSITION], int): positions = [msg[POSITION]] await mass.music.playlists.remove_playlist_tracks(msg[ITEM_ID], positions) connection.send_result( msg[ID], "OK", )
async def websocket_remove_node( hass: HomeAssistant, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Remove a node from the Z-Wave network.""" controller = client.driver.controller @callback def async_cleanup() -> None: """Remove signal listeners.""" for unsub in unsubs: unsub() @callback def forward_event(event: dict) -> None: connection.send_message( websocket_api.event_message(msg[ID], {"event": event["event"]})) @callback def node_removed(event: dict) -> None: node = event["node"] node_details = { "node_id": node.node_id, } connection.send_message( websocket_api.event_message(msg[ID], { "event": "node removed", "node": node_details })) connection.subscriptions[msg["id"]] = async_cleanup msg[DATA_UNSUBSCRIBE] = unsubs = [ controller.on("exclusion started", forward_event), controller.on("exclusion failed", forward_event), controller.on("exclusion stopped", forward_event), controller.on("node removed", node_removed), ] result = await controller.async_begin_exclusion() connection.send_result( msg[ID], result, )
async def websocket_device_cluster_commands( hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any] ) -> None: """Return a list of cluster commands.""" zha_gateway: ZHAGateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] ieee: EUI64 = msg[ATTR_IEEE] endpoint_id: int = msg[ATTR_ENDPOINT_ID] cluster_id: int = msg[ATTR_CLUSTER_ID] cluster_type: str = msg[ATTR_CLUSTER_TYPE] zha_device = zha_gateway.get_device(ieee) cluster_commands: list[dict[str, Any]] = [] 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, cmd in commands[CLUSTER_COMMANDS_CLIENT].items(): cluster_commands.append( { TYPE: CLIENT, ID: cmd_id, ATTR_NAME: cmd.name, } ) for cmd_id, cmd in commands[CLUSTER_COMMANDS_SERVER].items(): cluster_commands.append( { TYPE: CLUSTER_COMMAND_SERVER, ID: cmd_id, ATTR_NAME: cmd.name, } ) _LOGGER.debug( "Requested commands for: %s: %s, %s: '%s', %s: %s, %s: %s", ATTR_CLUSTER_ID, cluster_id, ATTR_CLUSTER_TYPE, cluster_type, ATTR_ENDPOINT_ID, endpoint_id, RESPONSE, cluster_commands, ) connection.send_result(msg[ID], cluster_commands)
async def websocket_subscribe_firmware_update_status( hass: HomeAssistant, connection: ActiveConnection, msg: dict, node: Node, ) -> None: """Subsribe to the status of a firmware update.""" @callback def async_cleanup() -> None: """Remove signal listeners.""" for unsub in unsubs: unsub() @callback def forward_progress(event: dict) -> None: progress: FirmwareUpdateProgress = event["firmware_update_progress"] connection.send_message( websocket_api.event_message( msg[ID], { "event": event["event"], "sent_fragments": progress.sent_fragments, "total_fragments": progress.total_fragments, }, )) @callback def forward_finished(event: dict) -> None: finished: FirmwareUpdateFinished = event["firmware_update_finished"] connection.send_message( websocket_api.event_message( msg[ID], { "event": event["event"], "status": finished.status, "wait_time": finished.wait_time, }, )) unsubs = [ node.on("firmware update progress", forward_progress), node.on("firmware update finished", forward_finished), ] connection.subscriptions[msg["id"]] = async_cleanup connection.send_result(msg[ID])
async def websocket_library_remove( hass: HomeAssistant, connection: ActiveConnection, msg: dict, mass: MusicAssistant, ) -> None: """Remove item from library.""" item = await mass.music.get_item_by_uri(msg[URI]) if not item: connection.send_error(msg[ID], ERR_NOT_FOUND, f"Item not found: {msg[URI]}") await mass.music.remove_from_library(item.media_type, item.item_id, item.provider) connection.send_result( msg[ID], "OK", )
async def websocket_update_data_collection_preference( hass: HomeAssistant, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Update preference for data collection and enable/disable collection.""" opted_in = msg[OPTED_IN] update_data_collection_preference(hass, entry, opted_in) if opted_in: await async_enable_statistics(client) else: await client.driver.async_disable_statistics() connection.send_result(msg[ID], )
async def websocket_version_info( hass: HomeAssistant, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Get version info from the Z-Wave JS server.""" version_info = { "driver_version": client.version.driver_version, "server_version": client.version.server_version, "min_schema_version": client.version.min_schema_version, "max_schema_version": client.version.max_schema_version, } connection.send_result( msg[ID], version_info, )
def websocket_node_status(hass: HomeAssistant, connection: ActiveConnection, msg: dict) -> None: """Get the status of a Z-Wave JS node.""" entry_id = msg[ENTRY_ID] client = hass.data[DOMAIN][entry_id][DATA_CLIENT] node_id = msg[NODE_ID] node = client.driver.controller.nodes[node_id] data = { "node_id": node.node_id, "is_routing": node.is_routing, "status": node.status, "is_secure": node.is_secure, "ready": node.ready, } connection.send_result( msg[ID], data, )
async def websocket_node_status( hass: HomeAssistant, connection: ActiveConnection, msg: dict, node: Node, ) -> None: """Get the status of a Z-Wave JS node.""" data = { "node_id": node.node_id, "is_routing": node.is_routing, "status": node.status, "is_secure": node.is_secure, "ready": node.ready, } connection.send_result( msg[ID], data, )
async def websocket_playerqueue( hass: HomeAssistant, connection: ActiveConnection, msg: dict, mass: MusicAssistant, ) -> None: """Return single player queue details by id.""" item = mass.players.get_player_queue(msg[QUEUE_ID]) if item is None: connection.send_error(msg[ID], ERR_NOT_FOUND, f"Queue not found: {msg[QUEUE_ID]}") return result = item.to_dict() connection.send_result( msg[ID], result, )
async def websocket_refresh_node_cc_values( hass: HomeAssistant, connection: ActiveConnection, msg: dict, node: Node, ) -> None: """Refresh node values for a particular CommandClass.""" command_class_id = msg[COMMAND_CLASS_ID] try: command_class = CommandClass(command_class_id) except ValueError: connection.send_error(msg[ID], ERR_NOT_FOUND, f"Command class {command_class_id} not found") return await node.async_refresh_cc_values(command_class) connection.send_result(msg[ID])
async def websocket_network_status( hass: HomeAssistant, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Get the status of the Z-Wave JS network.""" controller = client.driver.controller data = { "client": { "ws_server_url": client.ws_server_url, "state": "connected" if client.connected else "disconnected", "driver_version": client.version.driver_version, "server_version": client.version.server_version, }, "controller": { "home_id": controller.home_id, "library_version": controller.library_version, "type": controller.controller_type, "own_node_id": controller.own_node_id, "is_secondary": controller.is_secondary, "is_using_home_id_from_other_network": controller.is_using_home_id_from_other_network, "is_sis_present": controller.is_SIS_present, "was_real_primary": controller.was_real_primary, "is_static_update_controller": controller.is_static_update_controller, "is_slave": controller.is_slave, "serial_api_version": controller.serial_api_version, "manufacturer_id": controller.manufacturer_id, "product_id": controller.product_id, "product_type": controller.product_type, "supported_function_types": controller.supported_function_types, "suc_node_id": controller.suc_node_id, "supports_timers": controller.supports_timers, "is_heal_network_active": controller.is_heal_network_active, "nodes": list(client.driver.controller.nodes), }, } connection.send_result( msg[ID], data, )
async def websocket_update_zha_configuration(hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]) -> None: """Update the ZHA configuration.""" zha_gateway: ZHAGateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] options = zha_gateway.config_entry.options data_to_save = {**options, **{CUSTOM_CONFIGURATION: msg["data"]}} _LOGGER.info( "Updating ZHA custom configuration options from %s to %s", options, data_to_save, ) hass.config_entries.async_update_entry(zha_gateway.config_entry, options=data_to_save) status = await hass.config_entries.async_reload( zha_gateway.config_entry.entry_id) connection.send_result(msg[ID], status)
async def websocket_read_zigbee_cluster_attributes( hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]) -> None: """Read zigbee attribute for cluster on zha entity.""" zha_gateway: ZHAGateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] ieee: EUI64 = msg[ATTR_IEEE] endpoint_id: int = msg[ATTR_ENDPOINT_ID] cluster_id: int = msg[ATTR_CLUSTER_ID] cluster_type: str = msg[ATTR_CLUSTER_TYPE] attribute: int = msg[ATTR_ATTRIBUTE] manufacturer: int | None = msg.get(ATTR_MANUFACTURER) zha_device = zha_gateway.get_device(ieee) success = {} failure = {} if zha_device is not None: if cluster_id >= MFG_CLUSTER_ID_START and manufacturer is None: manufacturer = zha_device.manufacturer_code 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: [%s] %s: [%s] %s: [%s] %s: [%s],", ATTR_CLUSTER_ID, cluster_id, ATTR_CLUSTER_TYPE, cluster_type, ATTR_ENDPOINT_ID, endpoint_id, ATTR_ATTRIBUTE, attribute, ATTR_MANUFACTURER, manufacturer, RESPONSE, str(success.get(attribute)), "failure", failure, ) connection.send_result(msg[ID], str(success.get(attribute)))
def websocket_get_config_parameters(hass: HomeAssistant, connection: ActiveConnection, msg: dict) -> None: """Get a list of configuration parameters for a Z-Wave node.""" entry_id = msg[ENTRY_ID] node_id = msg[NODE_ID] client = hass.data[DOMAIN][entry_id][DATA_CLIENT] node = client.driver.controller.nodes.get(node_id) if node is None: connection.send_error(msg[ID], ERR_NOT_FOUND, f"Node {node_id} not found") return values = node.get_configuration_values() result = {} for value_id, zwave_value in values.items(): metadata = zwave_value.metadata result[value_id] = { "property": zwave_value.property_, "property_key": zwave_value.property_key, "configuration_value_type": zwave_value.configuration_value_type.value, "metadata": { "description": metadata.description, "label": metadata.label, "type": metadata.type, "min": metadata.min, "max": metadata.max, "unit": metadata.unit, "writeable": metadata.writeable, "readable": metadata.readable, }, "value": zwave_value.value, } if zwave_value.metadata.states: result[value_id]["metadata"][ "states"] = zwave_value.metadata.states connection.send_result( msg[ID], result, )
async def websocket_permit_devices(hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]) -> None: """Permit ZHA zigbee devices.""" zha_gateway: ZHAGateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] duration: int = msg[ATTR_DURATION] ieee: EUI64 | None = msg.get(ATTR_IEEE) async def forward_messages(data): """Forward events to websocket.""" connection.send_message(websocket_api.event_message(msg["id"], data)) remove_dispatcher_function = async_dispatcher_connect( hass, "zha_gateway_message", forward_messages) @callback def async_cleanup() -> None: """Remove signal listener and turn off debug mode.""" zha_gateway.async_disable_debug_mode() remove_dispatcher_function() connection.subscriptions[msg["id"]] = async_cleanup zha_gateway.async_enable_debug_mode() src_ieee: EUI64 code: bytes if ATTR_SOURCE_IEEE in msg: src_ieee = msg[ATTR_SOURCE_IEEE] code = msg[ATTR_INSTALL_CODE] _LOGGER.debug("Allowing join for %s device with install code", src_ieee) await zha_gateway.application_controller.permit_with_key( time_s=duration, node=src_ieee, code=code) elif ATTR_QR_CODE in msg: src_ieee, code = msg[ATTR_QR_CODE] _LOGGER.debug("Allowing join for %s device with install code", src_ieee) await zha_gateway.application_controller.permit_with_key( time_s=duration, node=src_ieee, code=code) else: await zha_gateway.application_controller.permit(time_s=duration, node=ieee) connection.send_result(msg["id"])
async def websocket_node_metadata( hass: HomeAssistant, connection: ActiveConnection, msg: dict, node: Node, ) -> None: """Get the metadata of a Z-Wave JS node.""" data = { "node_id": node.node_id, "exclusion": node.device_config.metadata.exclusion, "inclusion": node.device_config.metadata.inclusion, "manual": node.device_config.metadata.manual, "wakeup": node.device_config.metadata.wakeup, "reset": node.device_config.metadata.reset, "device_database_url": node.device_database_url, } connection.send_result( msg[ID], data, )
async def websocket_supervisor_api( hass: HomeAssistant, connection: ActiveConnection, msg: dict ): """Websocket handler to call Supervisor API.""" supervisor: HassIO = hass.data[DOMAIN] result = False try: result = await supervisor.send_command( msg[ATTR_ENDPOINT], method=msg[ATTR_METHOD], timeout=msg.get(ATTR_TIMEOUT, 10), payload=msg.get(ATTR_DATA, {}), ) except hass.components.hassio.HassioAPIError as err: _LOGGER.error("Failed to to call %s - %s", msg[ATTR_ENDPOINT], err) connection.send_error( msg[WS_ID], code=websocket_api.ERR_UNKNOWN_ERROR, message=str(err) ) else: connection.send_result(msg[WS_ID], result.get(ATTR_DATA, {}))
def websocket_network_status(hass: HomeAssistant, connection: ActiveConnection, msg: dict) -> None: """Get the status of the Z-Wave JS network.""" entry_id = msg[ENTRY_ID] client = hass.data[DOMAIN][entry_id][DATA_CLIENT] data = { "client": { "ws_server_url": client.ws_server_url, "state": "connected" if client.connected else "disconnected", "driver_version": client.version.driver_version, "server_version": client.version.server_version, }, "controller": { "home_id": client.driver.controller.data["homeId"], "nodes": list(client.driver.controller.nodes), }, } connection.send_result( msg[ID], data, )
async def websocket_playlist( hass: HomeAssistant, connection: ActiveConnection, msg: dict, mass: MusicAssistant, ) -> None: """Return track.""" item = await mass.music.playlists.get( msg[ITEM_ID], msg[PROVIDER], lazy=msg[LAZY], force_refresh=msg[REFRESH] ) if item is None: connection.send_error( msg[ID], ERR_NOT_FOUND, f"playlist not found: {msg[PROVIDER]}/{msg[ITEM_ID]}", ) return connection.send_result( msg[ID], item.to_dict(), )
async def websocket_stats( hass: HomeAssistant, connection: ActiveConnection, msg: dict, mass: MusicAssistant, ) -> None: """Return some statistics and generic info.""" result = { "providers": {x.id: x.to_dict() for x in mass.music.providers}, "count": { "artists": await mass.music.artists.count(), "albums": await mass.music.albums.count(), "tracks": await mass.music.tracks.count(), "playlists": await mass.music.playlists.count(), "radios": await mass.music.radio.count(), }, } connection.send_result( msg[ID], result, )
async def websocket_subscribe_controller_statistics( hass: HomeAssistant, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Subsribe to the statistics updates for a controller.""" @callback def async_cleanup() -> None: """Remove signal listeners.""" for unsub in unsubs: unsub() @callback def forward_stats(event: dict) -> None: statistics: ControllerStatistics = event["statistics_updated"] connection.send_message( websocket_api.event_message( msg[ID], { "event": event["event"], "source": "controller", **_get_controller_statistics_dict(statistics), }, ) ) controller = client.driver.controller msg[DATA_UNSUBSCRIBE] = unsubs = [ controller.on("statistics updated", forward_stats) ] connection.subscriptions[msg["id"]] = async_cleanup connection.send_result( msg[ID], _get_controller_statistics_dict(controller.statistics) )
async def websocket_refresh_node_info( hass: HomeAssistant, connection: ActiveConnection, msg: dict, node: Node, ) -> None: """Re-interview a node.""" @callback def async_cleanup() -> None: """Remove signal listeners.""" for unsub in unsubs: unsub() @callback def forward_event(event: dict) -> None: connection.send_message( websocket_api.event_message(msg[ID], {"event": event["event"]}) ) @callback def forward_stage(event: dict) -> None: connection.send_message( websocket_api.event_message( msg[ID], {"event": event["event"], "stage": event["stageName"]} ) ) connection.subscriptions[msg["id"]] = async_cleanup msg[DATA_UNSUBSCRIBE] = unsubs = [ node.on("interview started", forward_event), node.on("interview completed", forward_event), node.on("interview stage completed", forward_stage), node.on("interview failed", forward_event), ] result = await node.async_refresh_info() connection.send_result(msg[ID], result)
async def websocket_remove_failed_node( hass: HomeAssistant, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Remove a failed node from the Z-Wave network.""" controller = client.driver.controller node_id = msg[NODE_ID] @callback def async_cleanup() -> None: """Remove signal listeners.""" unsub() @callback def node_removed(event: dict) -> None: node = event["node"] node_details = { "node_id": node.node_id, } connection.send_message( websocket_api.event_message(msg[ID], { "event": "node removed", "node": node_details })) connection.subscriptions[msg["id"]] = async_cleanup unsub = controller.on("node removed", node_removed) result = await controller.async_remove_failed_node(node_id) connection.send_result( msg[ID], result, )
async def websocket_set_config_parameter( hass: HomeAssistant, connection: ActiveConnection, msg: dict, node: Node, ) -> None: """Set a config parameter value for a Z-Wave node.""" property_ = msg[PROPERTY] property_key = msg.get(PROPERTY_KEY) value = msg[VALUE] try: zwave_value, cmd_status = await async_set_config_parameter( node, value, property_, property_key=property_key) except (InvalidNewValue, NotFoundError, NotImplementedError, SetValueFailed) as err: code = ERR_UNKNOWN_ERROR if isinstance(err, NotFoundError): code = ERR_NOT_FOUND elif isinstance(err, (InvalidNewValue, NotImplementedError)): code = ERR_NOT_SUPPORTED connection.send_error( msg[ID], code, str(err), ) return connection.send_result( msg[ID], { VALUE_ID: zwave_value.value_id, STATUS: cmd_status, }, )