# Listener / Streaming Mode

RouterOS supports real-time event streams via the `/listen` variant of
many commands.  The library wraps these in a `ListenReply` object backed
by a `queue.Queue`.

---

## Starting a listener

```python
import routeros

with routeros.dial("192.168.1.1:8728", "admin", "") as c:
    listener = c.listen("/ip/firewall/address-list/listen")

    for sentence in listener:           # blocks; yields one Sentence per event
        action = sentence.map.get("action", "?")
        entry  = sentence.map.get("dst-address", "?")
        print(f"[{action}] {entry}")
```

The loop runs until you cancel the listener or the connection closes.

---

## Cancelling a listener

```python
import threading, time, routeros

with routeros.dial("192.168.1.1:8728", "admin", "") as c:
    listener = c.listen("/ip/address/listen")

    # Cancel after 5 seconds
    threading.Timer(5.0, listener.cancel).start()

    for sentence in listener:
        print(sentence.map)

    print("stream ended, done =", listener.done)
```

`cancel()` sends `/cancel =tag=<tag>` to the device and waits for
the stream to close before returning.

---

## Queue-based access

For more control, use the underlying `Queue` directly:

```python
import queue as q_module
import routeros

with routeros.dial("192.168.1.1:8728", "admin", "") as c:
    listener = c.listen("/interface/listen", queue_size=100)

    # Process events with a timeout
    while True:
        try:
            sentence = listener.chan().get(timeout=1.0)
            if sentence is None:          # end-of-stream sentinel
                break
            print(sentence.map["name"])
        except q_module.Empty:
            print("no event in the last second")
```

`queue_size=0` means unbounded (default).

---

## Common listener commands

| Command | What it streams |
|---|---|
| `/ip/address/listen` | IP address add/remove events |
| `/ip/route/listen` | Route table changes |
| `/ip/firewall/address-list/listen` | Firewall address list events |
| `/interface/listen` | Interface state changes |
| `/log/listen` | System log messages |
| `/ip/dhcp-server/lease/listen` | DHCP lease events |

---

## Checking for errors

```python
listener = c.listen("/ip/address/listen")
for sentence in listener:
    ...

if listener.err:
    print("Stream ended with error:", listener.err)
else:
    print("Stream ended cleanly, done =", listener.done)
```

---

## Multiple listeners

Each `listen()` call returns an independent `ListenReply` with its own tag
and queue.  You can run multiple listeners concurrently:

```python
import threading, routeros

with routeros.dial("192.168.1.1:8728", "admin", "") as c:
    l1 = c.listen("/ip/address/listen")
    l2 = c.listen("/interface/listen")

    def drain(listener, label):
        for s in listener:
            print(f"[{label}]", s.map)

    t1 = threading.Thread(target=drain, args=(l1, "ip"))
    t2 = threading.Thread(target=drain, args=(l2, "iface"))
    t1.start(); t2.start()
    # ... cancel when done
    l1.cancel(); l2.cancel()
    t1.join(); t2.join()
```
