783 lines
32 KiB
Python
783 lines
32 KiB
Python
import json
|
|
import asyncio
|
|
import os
|
|
import signal
|
|
from dataclasses import dataclass
|
|
from typing import Any, Awaitable, Callable
|
|
|
|
from fastapi.encoders import jsonable_encoder
|
|
|
|
from .json_formatter import logger
|
|
from .models import LatLng
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class SocketIOEventHandlers:
|
|
context: Any
|
|
tunneld_core: Any
|
|
get_status: Callable[[], dict]
|
|
get_simulation_status: Callable[[], dict]
|
|
device_shutdown: Callable[[Any], Awaitable[Any]]
|
|
device_reboot: Callable[[Any], Awaitable[Any]]
|
|
advance_simulation_queue: Callable[[], Awaitable[Any]]
|
|
toggle_test_mode: Callable[[], dict]
|
|
add_location_to_simulation_queue: Callable[[Any], Awaitable[dict]]
|
|
delete_location_from_simulation_queue: Callable[[Any], Awaitable[dict]]
|
|
clear_future_items: Callable[[], Any]
|
|
reset_queue: Callable[[], Any]
|
|
pause_simulation_queue: Callable[[], Awaitable[Any]]
|
|
resume_simulation_queue: Callable[[], Awaitable[Any]]
|
|
end_simulation_queue: Callable[[], Awaitable[Any]]
|
|
start_simulation_queue: Callable[[], Awaitable[dict]]
|
|
restart_simulation_queue: Callable[[], Awaitable[dict]]
|
|
toggle_gps_noise: Callable[[], dict]
|
|
update_queue_times: Callable[[], Awaitable[Any]]
|
|
start_icloud_monitor: Callable[[], Awaitable[Any]]
|
|
end_icloud_monitor: Callable[[], Awaitable[Any]]
|
|
is_icloud_monitor_running: Callable[[], bool]
|
|
refresh_icloud_location: Callable[[], Awaitable[dict]]
|
|
start_tunnel_watcher: Callable[[], Awaitable[Any]]
|
|
end_tunnel_watcher: Callable[[], Awaitable[Any]]
|
|
get_reverse_geocode: Callable[[Any], Awaitable[Any]]
|
|
set_favorite: Callable[[Any], Awaitable[tuple[str, str]]]
|
|
clear_favorite: Callable[[Any], Awaitable[tuple[str, str]]]
|
|
get_favorites: Callable[[], Awaitable[Any]]
|
|
get_categories: Callable[[], Awaitable[Any]]
|
|
add_category: Callable[[Any], Awaitable[dict]]
|
|
delete_category: Callable[[Any], Awaitable[tuple[str, str]]]
|
|
|
|
def register(self) -> None:
|
|
sio = self.context.sio
|
|
|
|
async def emit_test_prompt(test_prompt_data):
|
|
prompt_name = (
|
|
test_prompt_data.get("prompt_name")
|
|
if isinstance(test_prompt_data, dict)
|
|
else getattr(test_prompt_data, "prompt_name", None)
|
|
)
|
|
client_id = (
|
|
test_prompt_data.get("client_id")
|
|
if isinstance(test_prompt_data, dict)
|
|
else getattr(test_prompt_data, "client_id", None)
|
|
)
|
|
logger.info(
|
|
"Sending test prompt %s sent to client %s", prompt_name, client_id
|
|
)
|
|
try:
|
|
response = await sio.call(
|
|
"test_prompt",
|
|
{"prompt_name": prompt_name},
|
|
namespace="/",
|
|
to=client_id,
|
|
timeout=60,
|
|
)
|
|
if response and response.get("confirmed"):
|
|
await do_positive_action(prompt_name, client_id)
|
|
else:
|
|
await do_negative_action(prompt_name, client_id)
|
|
except asyncio.TimeoutError:
|
|
print(f"Client {client_id} failed to respond in time.")
|
|
|
|
async def do_positive_action(prompt_name, client_id):
|
|
logger.info(
|
|
"Confirmation received from client %s fpr prompt %s",
|
|
prompt_name,
|
|
client_id,
|
|
)
|
|
|
|
async def do_negative_action(prompt_name, client_id):
|
|
logger.info(
|
|
"Client %s denied confirmation for prompt %s", client_id, prompt_name
|
|
)
|
|
|
|
async def sio_send_status(sid):
|
|
"""Send Current Status"""
|
|
if sid in self.context.connected_clients:
|
|
logger.info("Sending status to: %s", sid)
|
|
await sio.emit(
|
|
"status", jsonable_encoder(self.get_status()), namespace="/", to=sid
|
|
)
|
|
else:
|
|
logger.info("Sending status to all clients, not only: %s", sid)
|
|
await sio.emit(
|
|
"status", jsonable_encoder(self.get_status()), namespace="/"
|
|
)
|
|
|
|
def json_fallback(obj):
|
|
if hasattr(obj, "__dict__"):
|
|
return obj.__dict__
|
|
if hasattr(obj, "as_dict"):
|
|
return obj.as_dict()
|
|
if hasattr(obj, "to_dict"):
|
|
return obj.to_dict()
|
|
return str(obj)
|
|
|
|
@sio.event
|
|
async def connect(sid, environ):
|
|
"""Client connection event handler."""
|
|
self.context.connected_clients.add(sid)
|
|
logger.info("Client connected: %s", sid)
|
|
await sio_send_status(sid)
|
|
return "%s connected" % sid
|
|
|
|
@sio.event
|
|
async def disconnect(sid):
|
|
"""Client disconnection event handler."""
|
|
self.context.connected_clients.discard(sid)
|
|
logger.info("Client disconnected: %s", sid)
|
|
|
|
@sio.event
|
|
async def send_test_prompt(sid, data):
|
|
"""Send test prompt"""
|
|
prompt_name = (
|
|
data.get("prompt_name")
|
|
if isinstance(data, dict)
|
|
else getattr(data, "prompt_name", None)
|
|
)
|
|
logger.info(
|
|
"Received request from %s to send test prompt %s", sid, prompt_name
|
|
)
|
|
test_prompt_data = {"prompt_name": prompt_name, "client_id": sid}
|
|
sio.start_background_task(emit_test_prompt, test_prompt_data)
|
|
return {
|
|
"prompt_name": prompt_name,
|
|
"command_status": "OK",
|
|
"message": "Test prompt requested.",
|
|
}
|
|
|
|
@sio.event
|
|
async def request_update(sid):
|
|
status_update = jsonable_encoder(self.get_status())
|
|
logger.info("Update request from %s sending %s", sid, status_update)
|
|
return status_update
|
|
|
|
@sio.event
|
|
async def message(sid, data):
|
|
logger.info("Received message from %s: %s", sid, data)
|
|
return True, "Message received"
|
|
|
|
@sio.event
|
|
async def device_control(sid, data):
|
|
"""Device Control"""
|
|
command = (
|
|
data.get("command")
|
|
if isinstance(data, dict)
|
|
else getattr(data, "command", None)
|
|
)
|
|
delay = (
|
|
data.get("delay")
|
|
if isinstance(data, dict)
|
|
else getattr(data, "delay", None)
|
|
)
|
|
if delay is None:
|
|
delay = 5
|
|
match command:
|
|
case "shutdown":
|
|
logger.info(
|
|
"Shutdown command received from %s with delay %s", sid, delay
|
|
)
|
|
await self.device_shutdown(delay)
|
|
return {
|
|
"command_status": "OK",
|
|
"command_class": "device_control",
|
|
"command": "shutdown",
|
|
"message": f"Device shutdown initiated with {delay} seconds delay",
|
|
}
|
|
|
|
case "reboot":
|
|
logger.info(
|
|
"Reboot command received from %s with delay %s", sid, delay
|
|
)
|
|
await self.device_reboot(delay)
|
|
return {
|
|
"command_status": "OK",
|
|
"command_class": "device_control",
|
|
"command": "reboot",
|
|
"message": f"Device reboot initiated with {delay} seconds delay",
|
|
}
|
|
|
|
case _:
|
|
return {
|
|
"command_status": "ERROR",
|
|
"command_class": "device_control",
|
|
"command": command,
|
|
"message": f"Invalid command: {command}",
|
|
}
|
|
|
|
@sio.event
|
|
async def simulation_control(sid, data):
|
|
"""Simulation Control"""
|
|
command = (
|
|
data.get("command")
|
|
if isinstance(data, dict)
|
|
else getattr(data, "command", None)
|
|
)
|
|
logger.info(
|
|
"Simulation Control command: %s requested from %s", command, sid
|
|
)
|
|
try:
|
|
match command:
|
|
case "next":
|
|
await self.advance_simulation_queue()
|
|
return {
|
|
"command": command,
|
|
"command_class": "simulation_control",
|
|
"command_status": "OK",
|
|
"message": "Advanced simulation queue",
|
|
"data": {
|
|
"simulation": self.get_simulation_status(),
|
|
},
|
|
}
|
|
|
|
case "test-mode":
|
|
self.toggle_test_mode()
|
|
return {
|
|
"command": command,
|
|
"command_class": "simulation_control",
|
|
"command_status": "OK",
|
|
"message": "test-mode toggled",
|
|
"data": {
|
|
"simulation": self.get_simulation_status(),
|
|
},
|
|
}
|
|
|
|
case "add":
|
|
return await self.add_location_to_simulation_queue(data)
|
|
|
|
case "delete":
|
|
return await self.delete_location_from_simulation_queue(data)
|
|
|
|
case "clear":
|
|
self.clear_future_items()
|
|
return {
|
|
"command": command,
|
|
"command_class": "simulation_control",
|
|
"command_status": "OK",
|
|
"message": "Simulation cleared",
|
|
"data": {
|
|
"simulation": self.get_simulation_status(),
|
|
},
|
|
}
|
|
|
|
case "reset":
|
|
self.reset_queue()
|
|
return {
|
|
"command": command,
|
|
"command_class": "simulation_control",
|
|
"command_status": "OK",
|
|
"message": "Simulation queue reset",
|
|
"data": {
|
|
"simulation": self.get_simulation_status(),
|
|
},
|
|
}
|
|
|
|
case "pause":
|
|
await self.pause_simulation_queue()
|
|
return {
|
|
"command": command,
|
|
"command_class": "simulation_control",
|
|
"command_status": "OK",
|
|
"message": "Simulation paused",
|
|
"data": {
|
|
"simulation": self.get_simulation_status(),
|
|
},
|
|
}
|
|
|
|
case "resume":
|
|
await self.resume_simulation_queue()
|
|
return {
|
|
"command": command,
|
|
"command_status": "OK",
|
|
"command_class": "simulation_control",
|
|
"message": "Simulation resumed",
|
|
"data": {
|
|
"simulation": self.get_simulation_status(),
|
|
},
|
|
}
|
|
|
|
case "end":
|
|
logger.info("End location simulation request from %s", sid)
|
|
cstat = "OK" if await self.end_simulation_queue() else "ERROR"
|
|
return {
|
|
"command_status": cstat,
|
|
"command_class": "simulation_control",
|
|
"command": command,
|
|
"message": "Simulation ended",
|
|
"data": {
|
|
"simulation": self.get_simulation_status(),
|
|
},
|
|
}
|
|
|
|
case "start":
|
|
logger.info("Start location simulation request from %s", sid)
|
|
return await self.start_simulation_queue()
|
|
|
|
case "gps-noise":
|
|
before_toggle = self.context.simulation_noise
|
|
self.toggle_gps_noise()
|
|
return {
|
|
"command_status": "OK",
|
|
"command_class": "simulation_control",
|
|
"command": command,
|
|
"message": f"GPS noise toggled from {before_toggle} to {self.context.simulation_noise}",
|
|
"data": {
|
|
"simulation": self.get_simulation_status(),
|
|
},
|
|
}
|
|
case "restart":
|
|
logger.info("Restart location simulation request from %s", sid)
|
|
return await self.restart_simulation_queue()
|
|
|
|
case _:
|
|
logger.warning(
|
|
"Invalid command received from %s: %s", sid, command
|
|
)
|
|
return {"command_status": "ERROR", "message": "Invalid command"}
|
|
finally:
|
|
logger.info(
|
|
"Simulation Control command: %s completed, sending status update...",
|
|
command,
|
|
)
|
|
await sio_send_status(sid)
|
|
|
|
@sio.event
|
|
async def location_item_update(sid, data):
|
|
"""Location Item Control"""
|
|
loc_id = (
|
|
data.get("loc_id")
|
|
if isinstance(data, dict)
|
|
else getattr(data, "loc_id", None)
|
|
)
|
|
key = (
|
|
data.get("key")
|
|
if isinstance(data, dict)
|
|
else getattr(data, "key", None)
|
|
)
|
|
value = (
|
|
data.get("value")
|
|
if isinstance(data, dict)
|
|
else getattr(data, "value", None)
|
|
)
|
|
error_data = {
|
|
"command_status": "ERROR",
|
|
"command": "update",
|
|
"command_class": "location_item",
|
|
"message": "Invalid request, Location Item Unchanged",
|
|
"data": self.context.simulation_queue_data[loc_id],
|
|
}
|
|
|
|
if value is not None:
|
|
if loc_id and key:
|
|
old_val = self.context.simulation_queue_data[loc_id][key]
|
|
self.context.simulation_queue_data[loc_id][key] = value
|
|
if key == "delay":
|
|
await self.update_queue_times()
|
|
logger.info(
|
|
"Location Item Update: %s: %s changed from %s to %s",
|
|
loc_id,
|
|
key,
|
|
old_val,
|
|
value,
|
|
)
|
|
return {
|
|
"command_status": "OK",
|
|
"command": "update",
|
|
"command_class": "location_item",
|
|
"message": "Location Item Updated",
|
|
"data": {
|
|
"simulation": {
|
|
"data": {
|
|
loc_id: self.context.simulation_queue_data[loc_id],
|
|
},
|
|
},
|
|
},
|
|
}
|
|
else:
|
|
return error_data
|
|
else:
|
|
return error_data
|
|
|
|
@sio.event
|
|
async def queue_order_update(sid, data):
|
|
logger.info("Queue order update received: %s", data)
|
|
new_simulation_queue_order = []
|
|
new_order = (
|
|
data.get("newOrder")
|
|
if isinstance(data, dict)
|
|
else getattr(data, "newOrder", None)
|
|
)
|
|
if new_order:
|
|
for item in new_order:
|
|
if item in self.context.simulation_queue_data:
|
|
new_simulation_queue_order.append(item)
|
|
self.context.simulation_queue_order = new_simulation_queue_order
|
|
return {
|
|
"command_status": "OK",
|
|
"command": "queue_order_update",
|
|
"command_class": "queue_order",
|
|
"message": "Queue Order Updated",
|
|
"data": {
|
|
"simulation": {
|
|
"order" : self.context.simulation_queue_order,
|
|
},
|
|
},
|
|
}
|
|
return {
|
|
"command_status": "ERROR",
|
|
"command": "queue_order_update",
|
|
"command_class": "queue_order",
|
|
"message": "Invalid request, Queue Order Unchanged",
|
|
"data": {
|
|
"simulation": {
|
|
"order": self.context.simulation_queue_order,
|
|
},
|
|
},
|
|
}
|
|
|
|
@sio.event
|
|
async def icloud_monitor_control(sid, data):
|
|
command = (
|
|
data.get("command")
|
|
if isinstance(data, dict)
|
|
else getattr(data, "command", None)
|
|
)
|
|
logger.info(
|
|
"iCloud Monitor control command: %s requested from %s", command, sid
|
|
)
|
|
try:
|
|
match command:
|
|
case "start":
|
|
await self.start_icloud_monitor()
|
|
return {
|
|
"command": command,
|
|
"command_class": "icloud_monitor_control",
|
|
"command_status": "OK",
|
|
"data": {
|
|
"icloud": {
|
|
"icloud_monitor_enabled": self.context.icloud_monitor_enabled,
|
|
"icloud_monitor_running": self.is_icloud_monitor_running(),
|
|
},
|
|
},
|
|
}
|
|
case "stop":
|
|
await self.end_icloud_monitor()
|
|
return {
|
|
"command": command,
|
|
"command_class": "icloud_monitor_control",
|
|
"command_status": "OK",
|
|
"data": {
|
|
"icloud": {
|
|
"icloud_monitor_enabled": self.context.icloud_monitor_enabled,
|
|
"icloud_monitor_running": self.is_icloud_monitor_running(),
|
|
},
|
|
},
|
|
}
|
|
case "status":
|
|
return {
|
|
"command": command,
|
|
"command_class": "icloud_monitor_control",
|
|
"command_status": "OK",
|
|
"data": {
|
|
"icloud": {
|
|
"icloud_monitor_enabled": self.context.icloud_monitor_enabled,
|
|
"icloud_monitor_running": self.is_icloud_monitor_running(),
|
|
},
|
|
},
|
|
}
|
|
case "refresh":
|
|
resp = {
|
|
"command": command,
|
|
"command_class": "icloud_monitor_control",
|
|
"command_status": "OK",
|
|
"data": {
|
|
"icloud": {
|
|
"icloud_monitor_enabled": self.context.icloud_monitor_enabled,
|
|
"icloud_monitor_running": self.is_icloud_monitor_running(),
|
|
"fmf_location": json.dumps(
|
|
await self.refresh_icloud_location(),
|
|
default=json_fallback,
|
|
),
|
|
},
|
|
},
|
|
}
|
|
return resp
|
|
case _:
|
|
return {
|
|
"command": command,
|
|
"command_class": "icloud_monitor_control",
|
|
"command_status": "ERROR",
|
|
"message": "Invalid command",
|
|
}
|
|
finally:
|
|
await sio_send_status(sid)
|
|
|
|
@sio.event
|
|
async def tunneld_control(sid, data):
|
|
command = (
|
|
data.get("command")
|
|
if isinstance(data, dict)
|
|
else getattr(data, "command", None)
|
|
)
|
|
logger.info("Tunneld Control command: %s requested from %s", command, sid)
|
|
match command:
|
|
case "start":
|
|
logger.info("Start tunneld request from %s: %s", sid, data)
|
|
try:
|
|
self.tunneld_core.start()
|
|
logger.info("Tunneld started successfully")
|
|
return {
|
|
"command_status": "OK",
|
|
"command_class": "tunneld_control",
|
|
"command": command,
|
|
"message": "Tunneld started successfully",
|
|
}
|
|
except Exception as e:
|
|
logger.error("Error starting tunneld: %s", e)
|
|
return {
|
|
"command": command,
|
|
"command_class": "tunneld_control",
|
|
"command_status": "ERROR",
|
|
"message": f"Error starting tunneld: {e}",
|
|
}
|
|
case "restart":
|
|
logger.info("Restart tunneld request from %s: %s", sid, data)
|
|
try:
|
|
self.tunneld_core.clear()
|
|
await asyncio.sleep(2)
|
|
self.tunneld_core.start()
|
|
return {
|
|
"command_status": "OK",
|
|
"command_class": "tunneld_control",
|
|
"command": command,
|
|
"message": "Tunneld started successfully",
|
|
}
|
|
except Exception as e:
|
|
logger.error("Error restarting tunneld: %s", e)
|
|
return {
|
|
"command_status": "ERROR",
|
|
"command_class": "tunneld_control",
|
|
"command": command,
|
|
"message": f"Error starting tunneld: {e}",
|
|
}
|
|
|
|
case "start-watcher":
|
|
logger.info("Start tunneld watcher request from %s: %s", sid, data)
|
|
await self.start_tunnel_watcher()
|
|
return {
|
|
"command_status": "OK",
|
|
"command_class": "tunneld_control",
|
|
"command": command,
|
|
"message": "Tunneld watcher started successfully",
|
|
}
|
|
|
|
case "end-watcher":
|
|
logger.info("End tunneld watcher request from %s: %s", sid, data)
|
|
await self.end_tunnel_watcher()
|
|
return {
|
|
"command_status": "OK",
|
|
"command_class": "tunneld_control",
|
|
"command": command,
|
|
"message": "Tunneld watcher stopped successfully",
|
|
}
|
|
|
|
case "shutdown":
|
|
logger.info("Shutdown tunneld request from %s: %s", sid, data)
|
|
try:
|
|
os.kill(os.getpid(), signal.SIGINT)
|
|
return {
|
|
"command_status": "OK",
|
|
"command_class": "tunneld_control",
|
|
"command": command,
|
|
"message": "Server shutting down...",
|
|
}
|
|
except Exception as e:
|
|
logger.error("Error shutting down tunneld: %s", e)
|
|
return {
|
|
"command": command,
|
|
"command_class": "tunneld_control",
|
|
"command_status": "ERROR",
|
|
"message": f"Error shutting down tunneld: {e}",
|
|
}
|
|
|
|
case "clear":
|
|
logger.info("Clearing tunnels...")
|
|
try:
|
|
self.tunneld_core.clear()
|
|
return {
|
|
"command": command,
|
|
"command_class": "tunneld_control",
|
|
"command_status": "OK",
|
|
"message": "Cleared tunnels...",
|
|
}
|
|
except Exception as e:
|
|
logger.error("Error clearing tunnels: %s", e)
|
|
return {
|
|
"command": command,
|
|
"command_class": "tunneld_control",
|
|
"command_status": "ERROR",
|
|
"message": f"Error clearing tunnels: {e}",
|
|
}
|
|
|
|
case "cancel":
|
|
logger.info("Canceling tunnel request from %s: %s", sid, data)
|
|
try:
|
|
udid = (
|
|
data.get("udid")
|
|
if isinstance(data, dict)
|
|
else getattr(data, "udid", self.context.udid)
|
|
)
|
|
if udid is None:
|
|
udid = self.context.udid
|
|
self.tunneld_core.cancel(udid=udid)
|
|
return {
|
|
"command": command,
|
|
"command_class": "tunneld_control",
|
|
"command_status": "OK",
|
|
"udid": udid,
|
|
"message": f"tunnel {udid} Canceled ...",
|
|
}
|
|
except Exception as e:
|
|
logger.error("Error canceling tunnel: %s", e)
|
|
return {
|
|
"command": command,
|
|
"command_class": "tunneld_control",
|
|
"command_status": "ERROR",
|
|
"message": f"Error canceling tunnel: {e}",
|
|
}
|
|
|
|
case _:
|
|
return {
|
|
"command": command,
|
|
"command_class": "tunneld_control",
|
|
"command_status": "ERROR",
|
|
"message": f"Unknown operation: {command}",
|
|
}
|
|
|
|
@sio.event
|
|
async def reverse_geocode(sid, data: LatLng):
|
|
logger.info("OSM Proxy Request from %s, data: %s", sid, data)
|
|
return await self.get_reverse_geocode(data)
|
|
|
|
@sio.event
|
|
async def favorite_control(sid, data: dict):
|
|
command = (
|
|
data.get("command")
|
|
if isinstance(data, dict)
|
|
else getattr(data, "command", None)
|
|
)
|
|
logger.info("Favorite Control command: %s requested from %s", command, sid)
|
|
match command:
|
|
case "set":
|
|
cstat, cmessage = await self.set_favorite(data)
|
|
if cstat == "ERROR":
|
|
logger.error("Favorite set failed: %s", cmessage)
|
|
return {
|
|
"command": command,
|
|
"command_class": "favorite_control",
|
|
"command_status": cstat,
|
|
"message": cmessage,
|
|
"data": {
|
|
"favorites": await self.get_favorites(),
|
|
},
|
|
}
|
|
|
|
case "clear":
|
|
cstat, cmessage = await self.clear_favorite(data)
|
|
return {
|
|
"command": command,
|
|
"command_class": "favorite_control",
|
|
"command_status": cstat,
|
|
"message": cmessage,
|
|
"data": {
|
|
"favorites": await self.get_favorites(),
|
|
},
|
|
}
|
|
|
|
case "get":
|
|
favs = await self.get_favorites()
|
|
if favs:
|
|
cstat = "OK"
|
|
cmessage = "Favorites retrieved successfully"
|
|
else:
|
|
cstat = "ERROR"
|
|
cmessage = "No favorites found"
|
|
return {
|
|
"command": command,
|
|
"command_class": "favorite_control",
|
|
"command_status": cstat,
|
|
"message": cmessage,
|
|
"data": {
|
|
"favorites": favs,
|
|
},
|
|
}
|
|
|
|
case _:
|
|
return {
|
|
"command": command,
|
|
"command_class": "favorite_control",
|
|
"command_status": "ERROR",
|
|
"message": f"Invalid command: {command}",
|
|
}
|
|
|
|
@sio.event
|
|
async def category_control(sid, data: dict):
|
|
command = (
|
|
data.get("command")
|
|
if isinstance(data, dict)
|
|
else getattr(data, "command", None)
|
|
)
|
|
logger.info("Category Control command: %s requested from %s", command, sid)
|
|
match command:
|
|
case "add":
|
|
cstat, cmessage = await self.add_category(data)
|
|
if cstat == "ERROR":
|
|
logger.error("Category set failed: %s", cmessage)
|
|
return {
|
|
"command": command,
|
|
"command_class": "category_control",
|
|
"command_status": cstat,
|
|
"message": cmessage,
|
|
"data": {
|
|
"categories": await self.get_categories(),
|
|
},
|
|
}
|
|
|
|
case "delete":
|
|
cstat, cmessage = await self.delete_category(data)
|
|
return {
|
|
"command": command,
|
|
"command_class": "category_control",
|
|
"command_status": cstat,
|
|
"message": cmessage,
|
|
"data": {
|
|
"categories": await self.get_categories(),
|
|
},
|
|
}
|
|
|
|
case "get":
|
|
cats = await self.get_categories()
|
|
if cats:
|
|
cstat = "OK"
|
|
cmessage = "Categories retrieved successfully"
|
|
else:
|
|
cstat = "ERROR"
|
|
cmessage = "No categories found"
|
|
logger.info("Categories: %s", cats)
|
|
return {
|
|
"command": command,
|
|
"command_class": "category_control",
|
|
"command_status": cstat,
|
|
"message": cmessage,
|
|
"data": {
|
|
"categories": cats,
|
|
},
|
|
}
|
|
|
|
case _:
|
|
return {
|
|
"command": command,
|
|
"command_class": "category_control",
|
|
"command_status": "ERROR",
|
|
"message": f"Invalid command: {command}",
|
|
}
|