Metadata-Version: 2.4
Name: ozobot-actors
Version: 0.3.2
Summary: Ozobot actor API package providing an alternative way for controling multiple bots
Project-URL: Homepage, https://ozobot.com
Project-URL: Github, https://github.com/ozobot/python-libraries
Author-email: Ondrej Novak <ondrej.novak@ozobot.com>
Requires-Python: >=3.13
Requires-Dist: ozobot-common<0.4.0,>=0.3.0
Requires-Dist: ozobot-linefollower<0.4.0,>=0.3.0
Provides-Extra: ari
Requires-Dist: ozobot-ari<0.4.0,>=0.3.0; extra == 'ari'
Provides-Extra: evo
Requires-Dist: ozobot-evo<0.4.0,>=0.3.0; extra == 'evo'
Provides-Extra: ora
Requires-Dist: ozobot-ora~=1.0.6; extra == 'ora'
Description-Content-Type: text/markdown

# ozobot-actors

Library that enables to control multiple Ozobot device using a globally defined functions. It mirrors the interface
of libraries implementing object oriented control of the individual robots. The library is primarily intended to be used by the Python code
generated from Blockly programs, as its semantics follows the actor based use by the [Ozobot Editor](https://editor.ozobot.com),
but can be used even in projects written from scratch.

See the [monorepo](https://github.com/ozobot/python-libraries) for more details. 

## Installation
The library requires you to list all the robots to be supported, so for example to install Evo support, run
`pip install ozobot-actors[evo]`, to install both Ari and Evo support, run `pip install ozobot-actors[ari,evo]`.

The library currently supports:
 - [`ozobot-ari`](/ozobot-ari)
 - [`ozobot-evo`](/ozobot-evo)


## Usage
The library defines global functions that can control any robot supporting that specific functionality. Targeting a specific robot
can be done by using a context manager. When an API function is called, the context is searched for available robot and the function is
executed with the first matching robot. The robot selection follows a few simple rules:

 - Actors need to be registered in the dispatcher with `dispatcher.add()`
 - Actors are put onto the stack as they are registered
 - Selecting an actor with `dispatcher.actor()` puts it on top of the current stack
 - Masking an actor with `dispatcher.mask()` removes it from the current stack
 - Stack is searched from the top
 - First robot having the required functionality is used, leaving the stack untouched


```python
import asyncio

from ozobot import actors
from ozobot.actors.linefollower import move
from ozobot.actors.userio import user_io_alert
from ozobot.ari import AriHandle
from ozobot.evo import EvoHandle

dispatcher = actors.new_actor_dispatcher()

async def main() -> None:
    async with EvoHandle(name="OzoEvo-1234abcd") as e1, EvoHandle(name="Ari-ABCD") as a1:
        dispatcher.add("Evo_1", e1)
        dispatcher.add("Ari_1", a1)

        with dispatcher.actor("Ari_1"):  # this puts Ari onto the default stack
            await move(100, 50)  # moves Ari
            await user_io_alert("Hello from Ari!")  # displays message on Ari

            with dispatcher.actor("Evo_1"):  # this puts Evo on top of Ari
                await move(100, 50)  # moves Evo
                await user_io_alert("Hello again from Ari!")  # Ari shows the message, because Evo does not support `user_io_alert`
            # stepping out of the context manager block removes Evo from the top of the stack
            
            await move(-100, 50)  # moves Ari again


if __name__ == "__main__":
    asyncio.run(main())
```
