async def websocket_get_config_parameters( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, node: Node ) -> None: """Get a list of configuration parameters for a Z-Wave node.""" 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_subscribe_heal_network_progress( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Subscribe to heal Z-Wave network status updates.""" controller = client.driver.controller @callback def async_cleanup() -> None: """Remove signal listeners.""" for unsub in unsubs: unsub() @callback def forward_event(key: str, event: dict) -> None: connection.send_message( websocket_api.event_message( msg[ID], {"event": event["event"], "heal_node_status": event[key]} ) ) connection.subscriptions[msg["id"]] = async_cleanup unsubs = [ controller.on("heal network progress", partial(forward_event, "progress")), controller.on("heal network done", partial(forward_event, "result")), ] connection.send_result(msg[ID])
async def websocket_abort_firmware_update( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, node: Node, ) -> None: """Abort a firmware update.""" await node.async_abort_firmware_update() connection.send_result(msg[ID])
async def websocket_refresh_node_values( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, node: Node, ) -> None: """Refresh node values.""" await node.async_refresh_values() connection.send_result(msg[ID])
async def websocket_install_config_update( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Check for config updates.""" success = await client.driver.async_install_config_update() connection.send_result(msg[ID], success)
async def websocket_update_log_config( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Update the driver log config.""" await client.driver.async_update_log_config(LogConfig(**msg[CONFIG])) connection.send_result( msg[ID], )
async def websocket_ping_node( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, node: Node, ) -> None: """Ping a Z-Wave JS node.""" result = await node.async_ping() connection.send_result( msg[ID], result, )
async def websocket_data_collection_status( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Return data collection preference and status.""" result = { OPTED_IN: entry.data.get(CONF_DATA_COLLECTION_OPTED_IN), ENABLED: await client.driver.async_is_statistics_enabled(), } connection.send_result(msg[ID], result)
async def websocket_get_log_config( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Get log configuration for the Z-Wave JS driver.""" result = await client.driver.async_get_log_config() connection.send_result( msg[ID], dataclasses.asdict(result), )
async def websocket_network_adapters_configure( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, ) -> None: """Update network config.""" network: Network = opp.data[DOMAIN] await network.async_reconfig(msg["config"]) connection.send_result( msg["id"], {ATTR_CONFIGURED_ADAPTERS: network.configured_adapters}, )
async def websocket_stop_exclusion( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Cancel removing a node from the Z-Wave network.""" controller = client.driver.controller result = await controller.async_stop_exclusion() connection.send_result( msg[ID], result, )
async def websocket_stop_healing_network( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Stop healing the Z-Wave network.""" controller = client.driver.controller result = await controller.async_stop_healing_network() connection.send_result( msg[ID], result, )
async def websocket_network_adapters( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, ) -> None: """Return network preferences.""" network: Network = opp.data[DOMAIN] connection.send_result( msg["id"], { ATTR_ADAPTERS: network.adapters, ATTR_CONFIGURED_ADAPTERS: network.configured_adapters, }, )
async def websocket_subscribe_firmware_update_status( opp: OpenPeerPower, 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_heal_node( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Heal a node on the Z-Wave network.""" controller = client.driver.controller node_id = msg[NODE_ID] result = await controller.async_heal_node(node_id) connection.send_result( msg[ID], result, )
async def websocket_remove_node( opp: OpenPeerPower, 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 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_check_for_config_updates( opp: OpenPeerPower, 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_node_status( opp: OpenPeerPower, 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_refresh_node_cc_values( opp: OpenPeerPower, 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_update_data_collection_preference( opp: OpenPeerPower, 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(opp, 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_network_status( opp: OpenPeerPower, 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_node_metadata( opp: OpenPeerPower, 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(opp: OpenPeerPower, connection: ActiveConnection, msg: dict): """Websocket handler to call Supervisor API.""" supervisor: OppIO = opp.data[DOMAIN] try: result = await supervisor.send_command( msg[ATTR_ENDPOINT], method=msg[ATTR_METHOD], timeout=msg.get(ATTR_TIMEOUT, 10), payload=msg.get(ATTR_DATA, {}), ) if result.get(ATTR_RESULT) == "error": raise opp.components.oppio.OppioAPIError(result.get("message")) except opp.components.oppio.OppioAPIError 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, {}))
async def websocket_refresh_node_info( opp: OpenPeerPower, 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 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( opp: OpenPeerPower, 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_subscribe_logs( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Subscribe to log message events from the server.""" driver = client.driver @callback def async_cleanup() -> None: """Remove signal listeners.""" opp.async_create_task(driver.async_stop_listening_logs()) unsub() @callback def forward_event(event: dict) -> None: log_msg: LogMessage = event["log_message"] connection.send_message( websocket_api.event_message( msg[ID], { "timestamp": log_msg.timestamp, "level": log_msg.level, "primary_tags": log_msg.primary_tags, "message": log_msg.formatted_message, }, ) ) unsub = driver.on("logging", forward_event) connection.subscriptions[msg["id"]] = async_cleanup await driver.async_start_listening_logs() connection.send_result(msg[ID])
async def websocket_set_config_parameter( opp: OpenPeerPower, 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, }, )
async def websocket_supervisor_event(opp: OpenPeerPower, connection: ActiveConnection, msg: dict): """Publish events from the Supervisor.""" async_dispatcher_send(opp, EVENT_SUPERVISOR_EVENT, msg[ATTR_DATA]) connection.send_result(msg[WS_ID])
async def websocket_replace_failed_node( opp: OpenPeerPower, connection: ActiveConnection, msg: dict, entry: ConfigEntry, client: Client, ) -> None: """Replace a failed node with a new node.""" controller = client.driver.controller include_non_secure = not msg[SECURE] node_id = msg[NODE_ID] @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"]} ) ) @callback def node_added(event: dict) -> None: node = event["node"] interview_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), ] unsubs.extend(interview_unsubs) node_details = { "node_id": node.node_id, "status": node.status, "ready": node.ready, } connection.send_message( websocket_api.event_message( msg[ID], {"event": "node added", "node": node_details} ) ) @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} ) ) @callback def device_registered(device: DeviceEntry) -> None: device_details = { "name": device.name, "id": device.id, "manufacturer": device.manufacturer, "model": device.model, } connection.send_message( websocket_api.event_message( msg[ID], {"event": "device registered", "device": device_details} ) ) connection.subscriptions[msg["id"]] = async_cleanup unsubs = [ controller.on("inclusion started", forward_event), controller.on("inclusion failed", forward_event), controller.on("inclusion stopped", forward_event), controller.on("node removed", node_removed), controller.on("node added", node_added), async_dispatcher_connect( opp, EVENT_DEVICE_ADDED_TO_REGISTRY, device_registered ), ] result = await controller.async_replace_failed_node(node_id, include_non_secure) connection.send_result( msg[ID], result, )