Skip to content

Commit 089d3a9

Browse files
committed
feat: add download and GPIO listing functionality to WokwiClient and WokwiClientSync
1 parent be09efa commit 089d3a9

File tree

5 files changed

+73
-2
lines changed

5 files changed

+73
-2
lines changed

src/wokwi_client/client.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
from .constants import DEFAULT_WS_URL
1111
from .control import set_control
1212
from .event_queue import EventQueue
13-
from .file_ops import upload, upload_file
14-
from .pins import pin_listen, pin_read
13+
from .file_ops import download, download_file, upload, upload_file
14+
from .pins import gpio_list, pin_listen, pin_read
1515
from .protocol_types import EventMessage, ResponseMessage
1616
from .serial import monitor_lines, write_serial
1717
from .simulation import pause, restart, resume, start
@@ -88,6 +88,28 @@ async def upload_file(
8888
"""
8989
return await upload_file(self._transport, filename, local_path)
9090

91+
async def download(self, name: str) -> ResponseMessage:
92+
"""
93+
Download a file from the simulator.
94+
95+
Args:
96+
name: The name of the file to download.
97+
98+
Returns:
99+
The response message from the server.
100+
"""
101+
return await download(self._transport, name)
102+
103+
async def download_file(self, name: str, local_path: Optional[Path] = None) -> None:
104+
"""
105+
Download a file from the simulator and save it to a local path.
106+
107+
Args:
108+
name: The name of the file to download.
109+
local_path: The local path to save the downloaded file. If not provided, uses the name as the path.
110+
"""
111+
await download_file(self._transport, name, local_path)
112+
91113
async def start_simulation(
92114
self,
93115
firmware: str,
@@ -214,6 +236,10 @@ async def listen_pin(self, part: str, pin: str, listen: bool = True) -> Response
214236
"""
215237
return await pin_listen(self._transport, part=part, pin=pin, listen=listen)
216238

239+
async def gpio_list(self) -> ResponseMessage:
240+
"""Get a list of all GPIO pins available in the simulation."""
241+
return await gpio_list(self._transport)
242+
217243
async def set_control(
218244
self, part: str, control: str, value: typing.Union[int, bool, float]
219245
) -> ResponseMessage:

src/wokwi_client/client_sync.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,18 @@ def upload_file(self, filename: str, local_path: Path | None = None) -> t.Any:
9393
assert self._client is not None
9494
return self._run_async(self._client.upload_file(filename, local_path))
9595

96+
def download(self, name: str) -> t.Any:
97+
if not self._connected:
98+
raise RuntimeError("Client not connected")
99+
assert self._client is not None
100+
return self._run_async(self._client.download(name))
101+
102+
def download_file(self, name: str, local_path: Path | None = None) -> t.Any:
103+
if not self._connected:
104+
raise RuntimeError("Client not connected")
105+
assert self._client is not None
106+
return self._run_async(self._client.download_file(name, local_path))
107+
96108
def start_simulation(
97109
self,
98110
firmware: str,
@@ -153,6 +165,12 @@ def listen_pin(self, part: str, pin: str, listen: bool = True) -> t.Any:
153165
assert self._client is not None
154166
return self._run_async(self._client.listen_pin(part, pin, listen))
155167

168+
def gpio_list(self) -> t.Any:
169+
if not self._connected:
170+
raise RuntimeError("Client not connected")
171+
assert self._client is not None
172+
return self._run_async(self._client.gpio_list())
173+
156174
def monitor_serial(self, callback: t.Callable[[bytes], None]) -> None:
157175
if not self._connected:
158176
raise RuntimeError("Client not connected")

src/wokwi_client/file_ops.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,16 @@ async def upload_file(
2222
async def upload(transport: Transport, name: str, content: bytes) -> ResponseMessage:
2323
params = UploadParams(name=name, binary=base64.b64encode(content).decode())
2424
return await transport.request("file:upload", params.model_dump())
25+
26+
27+
async def download(transport: Transport, name: str) -> ResponseMessage:
28+
return await transport.request("file:download", {"name": name})
29+
30+
31+
async def download_file(transport: Transport, name: str, local_path: Optional[Path] = None) -> None:
32+
if local_path is None:
33+
local_path = Path(name)
34+
35+
result = await download(transport, name)
36+
with open(local_path, "wb") as f:
37+
f.write(base64.b64decode(result["result"]["binary"]))

src/wokwi_client/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ class UploadParams(BaseModel):
1010
binary: str # base64
1111

1212

13+
class DownloadParams(BaseModel):
14+
binary: str # base64
15+
16+
1317
class SimulationParams(BaseModel):
1418
firmware: str
1519
elf: str

src/wokwi_client/pins.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,13 @@ async def pin_listen(
4343
"""
4444

4545
return await transport.request("pin:listen", {"part": part, "pin": pin, "listen": listen})
46+
47+
48+
async def gpio_list(transport: Transport) -> ResponseMessage:
49+
"""List all GPIO pins and their current states.
50+
51+
Args:
52+
transport: The active Transport instance.
53+
"""
54+
55+
return await transport.request("gpio:list", {})

0 commit comments

Comments
 (0)