# Introducing Fakebox: A POSbox/IoT Box Simulator for Odoo Developers

> Fakebox is a local HTTP server that speaks the same hw_proxy API as an Odoo IoT Box, so POS payment terminals, scales and receipt printers can be tested without hardware.

**Date:** 2026-05-26
**Source:** <https://trobz.com/insights/introducing-fakebox/>

---



We built [Fakebox](https://gitlab.trobz.com/services/fakebox) because we wanted to reproduce PosBox related issues without having to actually run a PosBox. It is a local HTTP server that speaks the same `hw_proxy` API as a real Odoo IoT Box or POSbox. Point your POS configuration at it and the Odoo browser client does not know the difference.

This article covers why the substitution works at the protocol level, what devices Fakebox simulates, how to control test outcomes at runtime, and how it fits into a local Odoo POS development workflow.

---

## Why the Substitution Works

Before getting into what Fakebox does, it is worth understanding why a software server can replace physical hardware at all.

### The Browser Security Wall

Odoo POS is a single-page web application. Browsers cannot access USB ports, serial ports, or any OS-level hardware interface directly. Every hardware interaction must go through HTTP. The IoT Box (or POSbox) exists precisely to bridge this gap: it is a local HTTP server running on the same network as the POS workstation, and the Odoo browser client calls it like any other API.

This design is what makes Fakebox possible. From Odoo's perspective, the IoT Box is nothing more than a set of HTTP endpoints under `/hw_proxy/`. Any server that responds correctly to those endpoints is a valid IoT Box. Fakebox is that server.

### The Protocol

Every device interaction follows the same pattern: the Odoo browser sends an HTTP POST to `/hw_proxy/<command>` with a JSON-RPC payload, and the IoT Box responds with a JSON result. The Odoo codebase has no hardware-specific logic on the browser side. It calls the proxy, reads the result, and updates the POS UI accordingly.

Fakebox implements all the endpoints the Odoo POS browser calls. It does not emulate hardware at a driver level. It speaks the protocol.

---

## Getting Started

Fakebox is licensed AGPL-3.0 and hosted at [gitlab.trobz.com/services/fakebox](https://gitlab.trobz.com/services/fakebox).

Install from source:

```bash
git clone https://gitlab.trobz.com/services/fakebox.git
cd fakebox
pip install -e .
```

Copy the sample configuration:

```bash
cp fakebox/config.sample.py fakebox/config.py
```

The defaults are `127.0.0.1:5000` for the HTTP server and `127.0.0.1:2121` for the FTP server (used by the Bizerba scale). Edit `config.py` to change them.

Start Fakebox:

```bash
fakebox
```

Verify it is running:

```bash
curl http://127.0.0.1:5000/hw_proxy/hello
```

Then set the IoT Box URL in your Odoo POS configuration to `http://127.0.0.1:5000`. The POS will handshake, query device status, and behave as if a real box is connected.

---

## Device Simulations

### Payment Terminal

The payment terminal simulation uses a deterministic amount rule by default: orders of 100 EUR or less result in a failed transaction; orders above 100 EUR succeed. This gives a reliable way to cover both outcomes in a test session without any configuration.

```
Amount <= 100 EUR  ->  transaction failed
Amount >  100 EUR  ->  transaction succeeded
```

For interactive control, set `PAYMENT_TERMINAL_DRIVER=mock` before starting Fakebox. In mock mode, the server prompts on stdin each time a transaction starts. Type `ok` for success or any other string to set a specific failure status. This is useful when you need to test a particular error message the POS displays.

### Cashlogy Automated Cash Drawer

The Cashlogy simulation implements the full transaction protocol that Odoo uses:

- `start_add_change`: called when the customer transaction modal opens
- `get_amount_accepted`: polled every 0.1 seconds to update the amount displayed in the modal
- `stop_acceptance`: called when the modal is confirmed, either manually or automatically
- `dispense`: called to release change to the customer

Opening and closing balance queries are also supported: `get_total_amount` returns the total cash in the machine, and `get_inventory` returns a coin and note breakdown. These are used by the opening and closing POS session flows when `pos.config.cash_control` is enabled.

All amounts are driven by `output/values.ini`:

```ini
[cashlogy]
amount_accepted = 50.00
amount_recycler = 200.00
amount_stacker = 50.00
```

`amount_recycler` and `amount_stacker` together represent the total cash in the machine (`get_total_amount` returns their sum). `amount_accepted` is what the customer has inserted so far.

### Scales

Fakebox supports two scale types that Odoo POS uses in different configurations.

**Toledo** (POS-connected scale): responds to HTTP requests directly from the POS session. Fakebox implements `scale_read`, `scale_price`, `scale_price_tare`, `scale_price_text`, `scale_price_tare_text`, and `reset_weight`. Weight and price values are read from `values.ini`.

**Bizerba** (self-service scale): uses FTP to push weighed item data to a scale server. Fakebox runs a `pyftpdlib` FTP server on port 2121 that accepts uploads from the scale. Received files land in `output/scale/` and are browsable at `http://127.0.0.1:5000/scale`. If your Bizerba model uses a non-UTF-8 encoding, configure it in `values.ini`:

```ini
[scale]
encoding = iso8859-1
```

### Receipt Printer and Other Peripherals

The receipt printer endpoints (`print_receipt`, `print_xml_receipt`) save each printed receipt as a file in `output/receipts/`. Browse them at `http://127.0.0.1:5000/receipt` to inspect exactly what the POS sends without a physical printer attached. This is useful for debugging receipt templates or verifying the XML structure of a custom receipt layout.

The customer display endpoints (`send_text_customer_display`, `send_currency_data_customer_display`) log their payloads. The barcode scanner endpoint (`/hw_proxy/scanner`) is a stub that returns a success response.

---

## Controlling Test Outcomes With values.ini

The `output/values.ini` file is read on every relevant request, so changes take effect immediately without restarting the server or the POS session. This is the main lever for controlling what Fakebox returns at runtime.

### Manual Testing

While a POS session is open, edit `values.ini` to change the state Fakebox reports. Change `amount_accepted` and the Cashlogy modal updates on its next poll (every 0.1 seconds). Change the scale weight and the next weigh request returns the new value. No page reload, no server restart.

### Scripted Testing

A test script writes specific values to `values.ini` before triggering a POS action, then asserts on the result. No mocking, no monkey-patching, no test doubles in application code.

Example: simulating a Cashlogy session where the customer inserts the exact order amount:

```bash
echo "[cashlogy]
amount_accepted = 150.00
amount_recycler = 0.00
amount_stacker = 150.00" > output/values.ini
```

The POS polls `get_amount_accepted`, sees 150.00, auto-confirms the transaction (`options.auto_accept = true`), and validates the order. The full happy-path flow runs without any manual intervention.

### CI Pipelines

Fakebox starts with `fakebox` and has no external dependencies beyond the Python packages installed at setup. A CI job can start it as a background process, run the POS test suite against it, and tear it down. The `values.ini` file can be pre-written by the test setup to produce deterministic outcomes for each test case.

This makes hardware-dependent POS flows testable in environments where no physical IoT Box will ever be connected.

---


## Conclusion

Fakebox works because the Odoo IoT Box is a protocol, not a device. Any HTTP server that responds correctly to `hw_proxy` endpoints is a valid substitute. Fakebox implements those endpoints, adds deterministic test controls via `values.ini`, and runs on a developer laptop with a single command.

Install it from [gitlab.trobz.com/services/fakebox](https://gitlab.trobz.com/services/fakebox) and point your POS configuration at `http://127.0.0.1:5000`.

