From c169fe9c57f2ad991bdbc6e65cf91c7ae752d7cd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Dec 2025 01:21:38 +0000 Subject: [PATCH 1/5] Initial plan From 2c01c68fe1fbe1819d0d84a8910577cd16456079 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Dec 2025 01:27:46 +0000 Subject: [PATCH 2/5] Remove proxy feature from CLI, GUI, server, and tests Co-authored-by: kjy5 <82800265+kjy5@users.noreply.github.com> --- docs/development/socketio_api.md | 14 ---- src/ephys_link/back_end/server.py | 57 ++++--------- src/ephys_link/front_end/cli.py | 15 ---- src/ephys_link/front_end/gui.py | 29 +------ src/ephys_link/utils/constants.py | 1 - tests/back_end/test_server.py | 128 +----------------------------- 6 files changed, 22 insertions(+), 222 deletions(-) diff --git a/docs/development/socketio_api.md b/docs/development/socketio_api.md index f325f03..e2a026f 100644 --- a/docs/development/socketio_api.md +++ b/docs/development/socketio_api.md @@ -37,20 +37,6 @@ Response: - `"2.0.0"` - `"2.0.0b2` -### Get Pinpoint ID - -| Event | Input | Response | -|-------------------|-------|----------| -| `get_pinpoint_id` | None | `string` | - -Proxy connection ID (first 8 characters of a UUID v4). - -__Example:__ - -Input: None - -Response: `"81f8de08"` - ### Get Platform Info | Event | Input | Return | diff --git a/src/ephys_link/back_end/server.py b/src/ephys_link/back_end/server.py index c19f6ee..48cfc41 100644 --- a/src/ephys_link/back_end/server.py +++ b/src/ephys_link/back_end/server.py @@ -12,22 +12,20 @@ ``` """ -from asyncio import new_event_loop, run +from asyncio import new_event_loop from collections.abc import Callable, Coroutine from json import JSONDecodeError, dumps, loads from typing import Any, TypeVar, final -from uuid import uuid4 from aiohttp.web import Application, run_app from pydantic import ValidationError -from socketio import AsyncClient, AsyncServer # pyright: ignore [reportMissingTypeStubs] +from socketio import AsyncServer # pyright: ignore [reportMissingTypeStubs] from vbl_aquarium.models.ephys_link import ( EphysLinkOptions, SetDepthRequest, SetInsideBrainRequest, SetPositionRequest, ) -from vbl_aquarium.models.proxy import PinpointIdResponse from vbl_aquarium.utils.vbl_base_model import VBLBaseModel from ephys_link.__about__ import __version__ @@ -36,7 +34,6 @@ from ephys_link.utils.constants import ( MALFORMED_REQUEST_ERROR, PORT, - PROXY_CLIENT_NOT_INITIALIZED_ERROR, SERVER_NOT_INITIALIZED_ERROR, UNKNOWN_EVENT_ERROR, cannot_connect_as_client_is_already_connected_error, @@ -64,35 +61,28 @@ def __init__(self, options: EphysLinkOptions, platform_handler: PlatformHandler, self._platform_handler = platform_handler self._console = console - # Initialize based on proxy usage. - self._sio: AsyncServer | AsyncClient = AsyncClient() if self._options.use_proxy else AsyncServer() - if not self._options.use_proxy: - # Exit if _sio is not a Server. - if not isinstance(self._sio, AsyncServer): - self._console.critical_print(SERVER_NOT_INITIALIZED_ERROR) - raise TypeError(SERVER_NOT_INITIALIZED_ERROR) + # Initialize server. + self._sio: AsyncServer = AsyncServer() + # Exit if _sio is not a Server. + if not isinstance(self._sio, AsyncServer): + self._console.critical_print(SERVER_NOT_INITIALIZED_ERROR) + raise TypeError(SERVER_NOT_INITIALIZED_ERROR) - self._app = Application() - self._sio.attach(self._app) # pyright: ignore [reportUnknownMemberType] + self._app = Application() + self._sio.attach(self._app) # pyright: ignore [reportUnknownMemberType] - # Bind connection events. - _ = self._sio.on("connect", self.connect) # pyright: ignore [reportUnknownMemberType, reportUnknownVariableType] - _ = self._sio.on("disconnect", self.disconnect) # pyright: ignore [reportUnknownMemberType, reportUnknownVariableType] + # Bind connection events. + _ = self._sio.on("connect", self.connect) # pyright: ignore [reportUnknownMemberType, reportUnknownVariableType] + _ = self._sio.on("disconnect", self.disconnect) # pyright: ignore [reportUnknownMemberType, reportUnknownVariableType] # Store connected client. self._client_sid: str = "" - # Generate Pinpoint ID for proxy usage. - self._pinpoint_id = str(uuid4())[:8] - # Bind events. _ = self._sio.on("*", self.platform_event_handler) # pyright: ignore [reportUnknownMemberType, reportUnknownVariableType] def launch(self) -> None: - """Launch the server. - - Based on the options, either connect to a proxy or launch the server locally. - """ + """Launch the server.""" # List platform and available manipulators. self._console.info_print("PLATFORM", self._platform_handler.get_display_name()) @@ -108,22 +98,7 @@ def launch(self) -> None: loop.close() # Launch server - if self._options.use_proxy: - self._console.info_print("PINPOINT ID", self._pinpoint_id) - - async def connect_proxy() -> None: - # Exit if _sio is not a proxy client. - if not isinstance(self._sio, AsyncClient): - self._console.critical_print(PROXY_CLIENT_NOT_INITIALIZED_ERROR) - raise TypeError(PROXY_CLIENT_NOT_INITIALIZED_ERROR) - - # noinspection HttpUrlsUsage - await self._sio.connect(f"http://{self._options.proxy_address}:{PORT}") # pyright: ignore [reportUnknownMemberType] - await self._sio.wait() - - run(connect_proxy()) - else: - run_app(self._app, port=PORT) + run_app(self._app, port=PORT) # Helper functions. def _malformed_request_response(self, request: str, data: tuple[tuple[Any], ...]) -> str: # pyright: ignore [reportExplicitAny] @@ -250,8 +225,6 @@ async def platform_event_handler(self, event: str, _: str, data: Any) -> str: # # Server metadata. case "get_version": return __version__ - case "get_pinpoint_id": - return PinpointIdResponse(pinpoint_id=self._pinpoint_id, is_requester=False).to_json_string() case "get_platform_info": return (await self._platform_handler.get_platform_info()).to_json_string() diff --git a/src/ephys_link/front_end/cli.py b/src/ephys_link/front_end/cli.py index 7fb46d0..2e2e06d 100644 --- a/src/ephys_link/front_end/cli.py +++ b/src/ephys_link/front_end/cli.py @@ -56,21 +56,6 @@ def __init__(self) -> None: action="store_true", help="Enable debug mode.", ) - _ = self._parser.add_argument( - "-p", - "--use-proxy", - dest="use_proxy", - action="store_true", - help="Enable proxy mode.", - ) - _ = self._parser.add_argument( - "-a", - "--proxy-address", - type=str, - default="proxy2.virtualbrainlab.org", - dest="proxy_address", - help="Proxy IP address.", - ) _ = self._parser.add_argument( "--mpm-port", type=int, diff --git a/src/ephys_link/front_end/gui.py b/src/ephys_link/front_end/gui.py index 06ad4c6..017750f 100644 --- a/src/ephys_link/front_end/gui.py +++ b/src/ephys_link/front_end/gui.py @@ -52,8 +52,6 @@ def __init__(self) -> None: self._ignore_updates = BooleanVar(value=options.ignore_updates) self._type = StringVar(value=options.type) self._debug = BooleanVar(value=options.debug) - self._use_proxy = BooleanVar(value=options.use_proxy) - self._proxy_address = StringVar(value=options.proxy_address) self._mpm_port = IntVar(value=options.mpm_port) self._serial = StringVar(value=options.serial) @@ -80,8 +78,6 @@ def get_options(self) -> EphysLinkOptions: ignore_updates=self._ignore_updates.get(), type=self._type.get(), debug=self._debug.get(), - use_proxy=self._use_proxy.get(), - proxy_address=self._proxy_address.get(), mpm_port=self._mpm_port.get(), serial=self._serial.get(), ) @@ -115,40 +111,23 @@ def _build_gui(self) -> None: ttk.Label(server_serving_settings, text="Local IP:", anchor=E, justify=RIGHT).grid(column=0, row=0, sticky="we") ttk.Label(server_serving_settings, text=gethostbyname(gethostname())).grid(column=1, row=0, sticky="we") - # Proxy. - ttk.Label(server_serving_settings, text="Use Proxy:", anchor=E, justify=RIGHT).grid( - column=0, row=1, sticky="we" - ) - ttk.Checkbutton( - server_serving_settings, - variable=self._use_proxy, - ).grid(column=1, row=1, sticky="we") - - # Proxy address. - ttk.Label(server_serving_settings, text="Proxy Address:", anchor=E, justify=RIGHT).grid( - column=0, row=2, sticky="we" - ) - ttk.Entry(server_serving_settings, textvariable=self._proxy_address, justify=CENTER).grid( - column=1, row=2, sticky="we" - ) - # Ignore updates. ttk.Label(server_serving_settings, text="Ignore Updates:", anchor=E, justify=RIGHT).grid( - column=0, row=4, sticky="we" + column=0, row=1, sticky="we" ) ttk.Checkbutton( server_serving_settings, variable=self._ignore_updates, - ).grid(column=1, row=4, sticky="we") + ).grid(column=1, row=1, sticky="we") # Debug mode. ttk.Label(server_serving_settings, text="Debug mode:", anchor=E, justify=RIGHT).grid( - column=0, row=5, sticky="we" + column=0, row=2, sticky="we" ) ttk.Checkbutton( server_serving_settings, variable=self._debug, - ).grid(column=1, row=5, sticky="we") + ).grid(column=1, row=2, sticky="we") # --- diff --git a/src/ephys_link/utils/constants.py b/src/ephys_link/utils/constants.py index ac86174..2754ea4 100644 --- a/src/ephys_link/utils/constants.py +++ b/src/ephys_link/utils/constants.py @@ -59,7 +59,6 @@ def did_not_reach_target_depth_error(request: SetDepthRequest, final_unified_dep EMERGENCY_STOP_MESSAGE = "Emergency Stopping All Manipulators..." SERVER_NOT_INITIALIZED_ERROR = "Server not initialized." -PROXY_CLIENT_NOT_INITIALIZED_ERROR = "Proxy client not initialized." def cannot_connect_as_client_is_already_connected_error(new_client_sid: str, current_client_sid: str) -> str: diff --git a/tests/back_end/test_server.py b/tests/back_end/test_server.py index 8010265..0299994 100644 --- a/tests/back_end/test_server.py +++ b/tests/back_end/test_server.py @@ -1,10 +1,9 @@ import asyncio -from collections.abc import Awaitable from json import dumps, loads import pytest from pytest_mock import MockerFixture -from socketio import AsyncClient, AsyncServer # pyright: ignore[reportMissingTypeStubs] +from socketio import AsyncServer # pyright: ignore[reportMissingTypeStubs] from vbl_aquarium.models.ephys_link import ( AngularResponse, BooleanStateResponse, @@ -18,7 +17,6 @@ SetPositionRequest, ShankCountResponse, ) -from vbl_aquarium.models.proxy import PinpointIdResponse import ephys_link.back_end.server from ephys_link.__about__ import __version__ @@ -27,7 +25,6 @@ from ephys_link.front_end.console import Console from ephys_link.utils.constants import ( MALFORMED_REQUEST_ERROR, - PROXY_CLIENT_NOT_INITIALIZED_ERROR, SERVER_NOT_INITIALIZED_ERROR, UNKNOWN_EVENT_ERROR, cannot_connect_as_client_is_already_connected_error, @@ -50,12 +47,7 @@ def console(self, mocker: MockerFixture) -> Console: @pytest.fixture def server(self, platform_handler: PlatformHandler, console: Console) -> Server: """Fixture for server.""" - return Server(EphysLinkOptions(use_proxy=False), platform_handler, console) - - @pytest.fixture - def proxy_client(self, platform_handler: PlatformHandler, console: Console) -> Server: - """Fixture for server as proxy client.""" - return Server(EphysLinkOptions(use_proxy=True), platform_handler, console) + return Server(EphysLinkOptions(), platform_handler, console) def test_failed_server_init( self, platform_handler: PlatformHandler, console: Console, mocker: MockerFixture @@ -66,7 +58,7 @@ def test_failed_server_init( # Act with pytest.raises(TypeError) as init_error: - _ = Server(EphysLinkOptions(use_proxy=False), platform_handler, console) + _ = Server(EphysLinkOptions(), platform_handler, console) # Assert patched_async_server.assert_called_once() @@ -107,98 +99,6 @@ def test_launch_server( spied_info_print.assert_any_call("MANIPULATORS", str(DUMMY_STRING_LIST)) mocked_run_app.assert_called_once() - def test_launch_proxy_client( - self, proxy_client: Server, platform_handler: PlatformHandler, console: Console, mocker: MockerFixture - ) -> None: - """Proxy client should print info then start.""" - # Add mocks and spies. - # noinspection DuplicatedCode - spied_info_print = mocker.spy(console, "info_print") - - patched_get_display_name = mocker.patch.object(platform_handler, "get_display_name", return_value=DUMMY_STRING) - - # Mock out get manipulators. - patched_get_manipulators = mocker.patch.object(platform_handler, "get_manipulators", new=mocker.Mock()) - asyncio_loop = mocker.Mock() - patched_run_until_complete = mocker.patch.object( - asyncio_loop, "run_until_complete", return_value=GetManipulatorsResponse(manipulators=DUMMY_STRING_LIST) - ) - patched_new_event_loop = mocker.patch.object( - ephys_link.back_end.server, "new_event_loop", return_value=asyncio_loop - ) - - # Mock out run. - def run_coroutine(coroutine: Awaitable[None]) -> None: - """Run the coroutine.""" - asyncio.new_event_loop().run_until_complete(coroutine) - - _ = mocker.patch.object(ephys_link.back_end.server, "run", new=run_coroutine) - patched_connect = mocker.patch.object(AsyncClient, "connect", new_callable=mocker.AsyncMock) - patched_wait = mocker.patch.object(AsyncClient, "wait", new_callable=mocker.AsyncMock) - - # Act. - proxy_client.launch() - - # Assert. - patched_get_display_name.assert_called_once() - patched_get_manipulators.assert_called_once() - patched_run_until_complete.assert_called_once() - patched_new_event_loop.assert_called_once() - spied_info_print.assert_any_call("PLATFORM", platform_handler.get_display_name()) - spied_info_print.assert_any_call("MANIPULATORS", str(DUMMY_STRING_LIST)) - spied_info_print.assert_any_call("PINPOINT ID", mocker.ANY) - patched_connect.assert_awaited_once() # pyright: ignore[reportUnusedCallResult] - patched_wait.assert_awaited_once() # pyright: ignore[reportUnusedCallResult] - - def test_launch_proxy_client_failed_init( - self, platform_handler: PlatformHandler, console: Console, mocker: MockerFixture - ) -> None: - """Proxy client should print info then start.""" - # Add mocks and spies. - # noinspection DuplicatedCode - spied_info_print = mocker.spy(console, "info_print") - - patched_get_display_name = mocker.patch.object(platform_handler, "get_display_name", return_value=DUMMY_STRING) - - # Mock out get manipulators. - patched_get_manipulators = mocker.patch.object(platform_handler, "get_manipulators", new=mocker.Mock()) - asyncio_loop = mocker.Mock() - patched_run_until_complete = mocker.patch.object( - asyncio_loop, "run_until_complete", return_value=GetManipulatorsResponse(manipulators=DUMMY_STRING_LIST) - ) - patched_new_event_loop = mocker.patch.object( - ephys_link.back_end.server, "new_event_loop", return_value=asyncio_loop - ) - - # Mock out run. - def run_coroutine(coroutine: Awaitable[None]) -> None: - """Run the coroutine.""" - asyncio.new_event_loop().run_until_complete(coroutine) - - _ = mocker.patch.object(ephys_link.back_end.server, "run", new=run_coroutine) - patched_connect = mocker.patch.object(AsyncClient, "connect", new_callable=mocker.AsyncMock) - patched_wait = mocker.patch.object(AsyncClient, "wait", new_callable=mocker.AsyncMock) - - # Mock out the AsyncServer init. - patched_async_server = mocker.patch.object(AsyncClient, "__new__") - - # Act - with pytest.raises(TypeError) as init_error: - Server(EphysLinkOptions(use_proxy=True), platform_handler, console).launch() - - # Assert. - patched_async_server.assert_called_once() - patched_get_display_name.assert_called_once() - patched_get_manipulators.assert_called_once() - patched_run_until_complete.assert_called_once() - patched_new_event_loop.assert_called_once() - spied_info_print.assert_any_call("PLATFORM", platform_handler.get_display_name()) - spied_info_print.assert_any_call("MANIPULATORS", str(DUMMY_STRING_LIST)) - spied_info_print.assert_any_call("PINPOINT ID", mocker.ANY) - assert init_error.value.args[0] == PROXY_CLIENT_NOT_INITIALIZED_ERROR - patched_connect.assert_not_awaited() # pyright: ignore[reportUnusedCallResult] - patched_wait.assert_not_awaited() # pyright: ignore[reportUnusedCallResult] - @pytest.mark.asyncio async def test_connect_success(self, server: Server, console: Console, mocker: MockerFixture) -> None: """Server should allow connection if there is no existing connection.""" @@ -283,28 +183,6 @@ async def test_platform_event_handler_get_version( spied_info_print.assert_called_once_with("EVENT", event_name) assert result == __version__ - @pytest.mark.asyncio - async def test_platform_event_handler_get_pinpoint_id( - self, server: Server, console: Console, mocker: MockerFixture - ) -> None: - """Server should return pinpoint ID.""" - # Spy console. - spied_info_print = mocker.spy(console, "debug_print") - - # Mock Pinpoint ID. - dummy_id = DUMMY_STRING[:8] - _ = mocker.patch.object(server, "_pinpoint_id", new=dummy_id) - - # Act. - event_name = "get_pinpoint_id" - result = await server.platform_event_handler(event_name, DUMMY_STRING, None) - parsed_result = PinpointIdResponse(**loads(result)) # pyright: ignore[reportAny] - - # Assert. - spied_info_print.assert_called_once_with("EVENT", event_name) - assert parsed_result.pinpoint_id == dummy_id - assert not parsed_result.is_requester - @pytest.mark.asyncio async def test_platform_event_handler_get_platform_info( self, platform_handler: PlatformHandler, server: Server, console: Console, mocker: MockerFixture From 733e2e9fb215fb056f9ee7f2bb1e3c3bfec2c47f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Dec 2025 01:29:44 +0000 Subject: [PATCH 3/5] Fix linting issues and remove unused code Co-authored-by: kjy5 <82800265+kjy5@users.noreply.github.com> --- src/ephys_link/back_end/server.py | 5 ----- src/ephys_link/utils/constants.py | 2 -- tests/back_end/test_server.py | 18 ------------------ 3 files changed, 25 deletions(-) diff --git a/src/ephys_link/back_end/server.py b/src/ephys_link/back_end/server.py index 48cfc41..bdcff1a 100644 --- a/src/ephys_link/back_end/server.py +++ b/src/ephys_link/back_end/server.py @@ -34,7 +34,6 @@ from ephys_link.utils.constants import ( MALFORMED_REQUEST_ERROR, PORT, - SERVER_NOT_INITIALIZED_ERROR, UNKNOWN_EVENT_ERROR, cannot_connect_as_client_is_already_connected_error, client_disconnected_without_being_connected_error, @@ -63,10 +62,6 @@ def __init__(self, options: EphysLinkOptions, platform_handler: PlatformHandler, # Initialize server. self._sio: AsyncServer = AsyncServer() - # Exit if _sio is not a Server. - if not isinstance(self._sio, AsyncServer): - self._console.critical_print(SERVER_NOT_INITIALIZED_ERROR) - raise TypeError(SERVER_NOT_INITIALIZED_ERROR) self._app = Application() self._sio.attach(self._app) # pyright: ignore [reportUnknownMemberType] diff --git a/src/ephys_link/utils/constants.py b/src/ephys_link/utils/constants.py index 2754ea4..9b34b1c 100644 --- a/src/ephys_link/utils/constants.py +++ b/src/ephys_link/utils/constants.py @@ -58,8 +58,6 @@ def did_not_reach_target_depth_error(request: SetDepthRequest, final_unified_dep EMERGENCY_STOP_MESSAGE = "Emergency Stopping All Manipulators..." -SERVER_NOT_INITIALIZED_ERROR = "Server not initialized." - def cannot_connect_as_client_is_already_connected_error(new_client_sid: str, current_client_sid: str) -> str: """Generate an error message for when the client is already connected. diff --git a/tests/back_end/test_server.py b/tests/back_end/test_server.py index 0299994..2d7bf23 100644 --- a/tests/back_end/test_server.py +++ b/tests/back_end/test_server.py @@ -1,9 +1,7 @@ -import asyncio from json import dumps, loads import pytest from pytest_mock import MockerFixture -from socketio import AsyncServer # pyright: ignore[reportMissingTypeStubs] from vbl_aquarium.models.ephys_link import ( AngularResponse, BooleanStateResponse, @@ -25,7 +23,6 @@ from ephys_link.front_end.console import Console from ephys_link.utils.constants import ( MALFORMED_REQUEST_ERROR, - SERVER_NOT_INITIALIZED_ERROR, UNKNOWN_EVENT_ERROR, cannot_connect_as_client_is_already_connected_error, client_disconnected_without_being_connected_error, @@ -49,21 +46,6 @@ def server(self, platform_handler: PlatformHandler, console: Console) -> Server: """Fixture for server.""" return Server(EphysLinkOptions(), platform_handler, console) - def test_failed_server_init( - self, platform_handler: PlatformHandler, console: Console, mocker: MockerFixture - ) -> None: - """Server should raise error if sio is not an AsyncServer.""" - # Mock out the AsyncServer init. - patched_async_server = mocker.patch.object(AsyncServer, "__new__") - - # Act - with pytest.raises(TypeError) as init_error: - _ = Server(EphysLinkOptions(), platform_handler, console) - - # Assert - patched_async_server.assert_called_once() - assert init_error.value.args[0] == SERVER_NOT_INITIALIZED_ERROR - def test_launch_server( self, server: Server, platform_handler: PlatformHandler, console: Console, mocker: MockerFixture ) -> None: From cbd506adff9ca5cadd3e037194eac526b0dbc56a Mon Sep 17 00:00:00 2001 From: Kenneth Yang Date: Tue, 23 Dec 2025 15:25:43 -0800 Subject: [PATCH 4/5] Fix doc generation warnings --- docs/usage/experiment_automation.md | 10 ---------- mkdocs.yml | 1 + 2 files changed, 1 insertion(+), 10 deletions(-) delete mode 100644 docs/usage/experiment_automation.md diff --git a/docs/usage/experiment_automation.md b/docs/usage/experiment_automation.md deleted file mode 100644 index 80731ec..0000000 --- a/docs/usage/experiment_automation.md +++ /dev/null @@ -1,10 +0,0 @@ -# Experiment Automation - -Pinpoint and Ephys Link can work together to automate manual procedures in electrophysiology experiments. Follow the -[instructions on Pinpoint's documentation](https://virtualbrainlab.org//pinpoint/tutorials/tutorial_ephys_copilot.html) -to use automation in your next experiment! - -!!! note - - Automation is still in early development. We recommend [contacting](https://virtualbrainlab.org/about/overview.html) - Dan Birman and Kenneth Yang if you would like to try it out! \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 58ea60c..cfc9c32 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -107,5 +107,6 @@ nav: - development/index.md - development/socketio_api.md - development/adding_a_manipulator.md + - development/jackhammer_mode.md - development/code_organization.md - Source Code Reference: reference/ \ No newline at end of file From aab903d8ad5b35883395a2805cce09d214de4e92 Mon Sep 17 00:00:00 2001 From: Kenneth Yang Date: Tue, 23 Dec 2025 16:57:05 -0800 Subject: [PATCH 5/5] Update dependencies --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 46dc426..552e74b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,11 +31,11 @@ dependencies = [ "packaging==25.0", "platformdirs==4.5.1", "pyserial==3.5", - "python-socketio[asyncio_client]==5.15.1", + "python-socketio==5.15.1", "requests==2.32.5", "sensapex==1.504.1", "rich==14.2.0", - "vbl-aquarium==1.1.0" + "vbl-aquarium==1.2.0" ] [project.urls]