> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.ocpplab.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.ocpplab.io/_mcp/server.

# Plug, charge and unplug

You need a running virtual charger (see the [Quickstart](/get-started/quickstart)) and an id tag your CSMS accepts.

```bash
export OCPPLAB_TOKEN="<token>"
export CHARGER_IDENTITY="DEMO-CP-001"   # the identity you deployed it with
export ID_TAG="<accepted-id-tag>"
```

## The sequence

| Step | SDK call                   | What the charger does                                  |
| ---- | -------------------------- | ------------------------------------------------------ |
| 1    | `commands.plugConnector`   | Reports an EV cable plugged into the connector         |
| 2    | `commands.startSession`    | Authorizes the id tag and starts a transaction         |
| 3    | `commands.stopSession`     | Stops the transaction and sends the final meter values |
| 4    | `commands.unplugConnector` | Reports the cable unplugged                            |

Each call targets chargers with `target`: one charger by its OCPP `identity` (the one you chose when you deployed it) or its `deployment_id`, or every charger of a `location_id`, optionally narrowed to `charger_ids`. `payload` picks the connector; without it, EVSE `1` connector `1` is used.

## Full example

Every command returns `202` with one `results[].command.id` per targeted charger. The example polls each id until the charger reports a final status: `applied`, `rejected`, `expired` or `failed`. Until the charger has received the command, polling returns `404`.

```typescript title={"plug_charge_unplug.ts"}
// Plug a deployed virtual charger in, charge, stop and unplug it.
//
//     export OCPPLAB_TOKEN="<api-key>"
//     export CHARGER_IDENTITY="DEMO-CP-001"   # the identity you deployed it with
//     export ID_TAG="<accepted-id-tag>"
//     npx tsx examples/plug_charge_unplug.ts
//
// Every command returns 202 once it is queued. Each results[].command.id is
// polled with commands.getStatus until the charger reports a final status.
import { OcpplabSDK, OcpplabSDKTypes } from "ocpplab";

const FINAL = new Set(["applied", "rejected", "expired", "failed"]);
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

async function wait(
    client: OcpplabSDK,
    queued: Promise<OcpplabSDKTypes.OcppLocationCommandResponse>,
): Promise<void> {
    for (const result of (await queued).results) {
        while (true) {
            try {
                const status = await client.evcharger.commands.getStatus({ command_id: result.command.id });
                if (FINAL.has(status.status)) {
                    console.log(result.identity, status.event_type, status.status);
                    break;
                }
            } catch (error) {
                // 404 until the charger has received the command.
                if (!(error instanceof OcpplabSDKTypes.NotFoundError)) throw error;
            }
            await sleep(2_000);
        }
    }
}

async function main(): Promise<void> {
    const client = new OcpplabSDK({ token: process.env.OCPPLAB_TOKEN! });
    const target = { identity: process.env.CHARGER_IDENTITY! };
    const connector = { evse_id: 1, connector_id: 1 };

    await wait(client, client.evcharger.commands.plugConnector({ target, payload: connector }));
    await wait(
        client,
        client.evcharger.commands.startSession({
            target,
            payload: { id_tag: process.env.ID_TAG!, connector_id: 1 },
        }),
    );
    await sleep(30_000); // let it charge for a while
    await wait(client, client.evcharger.commands.stopSession({ target }));
    await wait(client, client.evcharger.commands.unplugConnector({ target, payload: connector }));
}

void main();

```

```python title={"plug_charge_unplug.py"}
"""Plug a deployed virtual charger in, charge, stop and unplug it.

    export OCPPLAB_TOKEN="<api-key>"
    export CHARGER_IDENTITY="DEMO-CP-001"   # the identity you deployed it with
    export ID_TAG="<accepted-id-tag>"
    uv run python examples/plug_charge_unplug.py

Every command returns 202 once it is queued. Each ``results[].command.id`` is
polled with ``commands.get_status`` until the charger reports a final status.
"""

from __future__ import annotations

import os
import time

from ocpplab import NotFoundError, OcpplabSDK
from ocpplab.types import (
    CommandTarget,
    ConnectorPayload,
    OcppLocationCommandResponse,
    StartSessionPayload,
)

FINAL = {"applied", "rejected", "expired", "failed"}


def wait(client: OcpplabSDK, queued: OcppLocationCommandResponse) -> None:
    """Poll each queued command until the charger answers."""
    for result in queued.results:
        while True:
            try:
                status = client.evcharger.commands.get_status(result.command.id)
            except NotFoundError:  # the charger has not received it yet
                status = None
            if status and status.status in FINAL:
                print(result.identity, status.event_type, status.status)
                break
            time.sleep(2)


def main() -> None:
    client = OcpplabSDK(token=os.environ["OCPPLAB_TOKEN"])
    target = CommandTarget(identity=os.environ["CHARGER_IDENTITY"])
    connector = ConnectorPayload(evse_id=1, connector_id=1)

    wait(
        client,
        client.evcharger.commands.plug_connector(target=target, payload=connector),
    )
    wait(
        client,
        client.evcharger.commands.start_session(
            target=target,
            payload=StartSessionPayload(id_tag=os.environ["ID_TAG"], connector_id=1),
        ),
    )
    time.sleep(30)  # let it charge for a while
    wait(client, client.evcharger.commands.stop_session(target=target))
    wait(
        client,
        client.evcharger.commands.unplug_connector(target=target, payload=connector),
    )


if __name__ == "__main__":
    main()

```

> **Tip**
>
> `startSession` already plugs the cable in before it authorizes. Call `plugConnector` first only when you want to test how your CSMS handles a plugged-in, idle charger.

## Other commands

The same pattern works for every `evcharger.commands.*` call, including `authorizeIdTag`, `changeConfiguration`, `injectFault`, `clearFault`, `sendLocalList` and `powerUpdate`. See the [API Reference](/api-reference).