Small Python CLI and LLM skill for reading and controlling a Daikin ONECTA air conditioner via the official Daikin cloud API. It is designed for simple terminal automation and Codex/LLM agent workflows.
This is an independent open source project. It is not affiliated with, endorsed by, or supported by Daikin or the AC manufacturer.
No third-party Python packages are required.
- OAuth setup against the Daikin ONECTA cloud API.
- Local refresh-token storage outside the repository.
- AC status summaries for agent and voice workflows.
- Cooling controls for power, setpoint, fixed fan level, and Daikin
powerfulMode/power mode. - Automatic single-AC selection and explicit
DAIKIN_DEVICE_IDsupport for accounts with multiple AC devices. - Bundled Codex/LLM skill for natural-language AC control.
- Python 3.11 or newer.
- A Daikin account with an AC visible in the ONECTA app.
- A Daikin Developer Portal application with the
openid,onecta:basic.integration, andoffline_accessscopes. - A redirect URL that you control and have registered with the Daikin Developer Portal.
Clone the repository and create a local config file:
git clone https://github.com/randomsnowflake/daikin-ac-control.git
cd daikin-ac-control
cp .env-example .envEdit .env with your Daikin client ID, client secret, and redirect URI. Then
create an authorization URL:
python3 -m daikin_ac_control auth-urlOpen the URL, authorize with Daikin, copy the code from your callback handler, and exchange it for a local token file:
python3 -m daikin_ac_control exchange-code "CODE_FROM_CALLBACK"Verify access:
python3 -m daikin_ac_control statusRun without parameters to see the available commands:
python3 -m daikin_ac_controlThe CLI loads .env from the current directory automatically, so you do not
need to source it before each command when running from the repository root.
Commands that operate on a device default to the single AC in the account.
Cooling is the assumed mode for control commands. Turning the AC on defaults to
the lowest supported cooling setpoint and fixed fan level 3/5.
Plain-text status output is a compact, labeled line suitable for agents and
voice summaries.
python3 -m daikin_ac_control status
python3 -m daikin_ac_control on
python3 -m daikin_ac_control off
python3 -m daikin_ac_control set --temperature 18 --fan-level 3
python3 -m daikin_ac_control fan-level 5
python3 -m daikin_ac_control power-mode onExample status output:
AC: on | mode: cooling | setpoint: 18 C | fan: 3/5 | flaps: vertical stop, horizontal swing | room: 22 C | outside: 29 C | powerful: off
If installed as a package, the console script is also available:
daikin-ac-control statusIf your Daikin account has exactly one AC gateway device, no device
configuration is needed. Leave DAIKIN_DEVICE_ID unset or empty, and the CLI
will use that single AC automatically.
If the account has multiple AC gateway devices, commands that operate on an AC
need DAIKIN_DEVICE_ID so the CLI knows which unit to control. Find the
available IDs with:
python3 -m daikin_ac_control devices --allThen set the selected device ID in .env or your shell:
export DAIKIN_DEVICE_ID="your-gateway-device-id"If multiple AC devices are returned and DAIKIN_DEVICE_ID is not set, the CLI
fails with a message listing the available IDs instead of guessing.
Register a redirect URL that you control in the Daikin developer portal, then
use the same URL as DAIKIN_REDIRECT_URI.
For example:
https://example.com/daikin/callback
The callback handler does not need to call this CLI or store anything. It only needs to:
- Receive the OAuth redirect from Daikin.
- Read the
codequery parameter from the request URL. - Show that code to the user so they can paste it into
python3 -m daikin_ac_control exchange-code "CODE_FROM_CALLBACK".
You may implement this handler as a tiny web page, server route, serverless
function, or local callback server, as long as the exact URL is registered in
the developer portal and configured in .env.
Here is a minimal dependency-free Python example for a local callback handler:
from html import escape
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import parse_qs, urlparse
class CallbackHandler(BaseHTTPRequestHandler):
def do_GET(self):
query = parse_qs(urlparse(self.path).query)
code = query.get("code", [""])[0]
error = query.get("error", [""])[0]
if code:
body = (
"<h1>Daikin authorization code</h1>"
"<p>Copy this code into the CLI:</p>"
f"<pre>{escape(code)}</pre>"
)
else:
body = (
"<h1>No authorization code found</h1>"
f"<p>Error: {escape(error or 'missing code')}</p>"
)
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.end_headers()
self.wfile.write(body.encode("utf-8"))
HTTPServer(("127.0.0.1", 8765), CallbackHandler).serve_forever()If you use that local example, register and configure this exact redirect URI:
http://127.0.0.1:8765/callback
For a hosted callback, use the same handler idea behind your own HTTPS URL and register that exact URL instead.
Use environment variables for credentials. Do not commit them.
export DAIKIN_CLIENT_ID="..."
export DAIKIN_CLIENT_SECRET="..."
export DAIKIN_REDIRECT_URI="https://example.com/daikin/callback"
export DAIKIN_SCOPE="openid onecta:basic.integration offline_access"
# Optional when the account has exactly one AC. Required for multiple ACs.
export DAIKIN_DEVICE_ID=""Create an authorization URL:
python3 -m daikin_ac_control auth-urlOpen the URL, authorize with Daikin, then copy the code from the callback page:
python3 -m daikin_ac_control exchange-code "CODE_FROM_CALLBACK"Read the AC status:
python3 -m daikin_ac_control statusTurn the AC on with the default cooling settings:
python3 -m daikin_ac_control onThe on command always sets cooling mode, the lowest supported cooling
temperature unless --temperature is passed, fixed fan level 3/5 unless
--fan or --fan-level is passed, and Daikin power mode off.
Set cooling controls:
python3 -m daikin_ac_control set --temperature 18
python3 -m daikin_ac_control set --fan-level 5
python3 -m daikin_ac_control fan-level togglefan-level toggle switches the fixed fan level between 3/5 and 5/5.
Daikin's literal app "power mode" switch is the powerfulMode boost and can be
controlled with:
python3 -m daikin_ac_control power-mode on
python3 -m daikin_ac_control power-mode off
python3 -m daikin_ac_control power-mode togglePower mode and fixed fan level are separate controls. Do not treat "power mode",
"boost", or "turbo" as fan level 5. The CLI also avoids redundant
powerfulMode=off writes when it is already off, because Daikin can rate-limit
unnecessary control calls.
Turn the AC off:
python3 -m daikin_ac_control offGet raw JSON for debugging:
python3 -m daikin_ac_control devices --rawList every gateway device instead of the default AC:
python3 -m daikin_ac_control devices --allTokens are stored at:
~/.config/daikin-ac-control/tokens.json
Override with:
export DAIKIN_TOKEN_FILE="/secure/path/tokens.json"This repository includes a Codex/LLM skill at
skills/daikin-ac-control/SKILL.md. Install it by copying or symlinking the
skill directory into your Codex skills directory:
mkdir -p ~/.codex/skills
ln -s "$(pwd)/skills/daikin-ac-control" ~/.codex/skills/daikin-ac-controlIf you prefer a copy instead of a symlink:
mkdir -p ~/.codex/skills
cp -R skills/daikin-ac-control ~/.codex/skills/Start a new Codex session after installing the skill so it is loaded. The skill
expects commands to run from this repository root, where .env can be loaded
automatically. Before using the skill, complete the setup above and verify:
python3 -m daikin_ac_control statusAfter that, you can ask your assistant for common AC tasks in natural language:
What is the AC status?
Turn the AC on.
Set the AC to 18 degrees.
Set the fan level to 5.
Enable power mode.
Turn the AC off.
If OAuth has not been completed yet, ask the assistant to create the Daikin auth
URL. Open it yourself, authorize with Daikin, then give the assistant the
callback code so it can run exchange-code.
Run the dependency-free unit suite before deploying:
python3 -m unittest discover -vThe tests mock the Daikin API and cover config loading, OAuth token handling, HTTP error handling, device selection, status formatting, CLI routing, write payloads, validation failures, and polling for confirmed state.
- This project uses the Daikin cloud API, so it requires internet access and a working Daikin cloud service.
- Control commands are intentionally narrow and cooling-focused.
- Supported devices are the AC gateway devices exposed through the tested ONECTA API shape. Other Daikin device classes may need additional mapping.
- The bundled LLM skill shells out to this CLI. Complete OAuth setup and verify
statusbefore expecting agent workflows to work.
MIT. See LICENSE.
On the live server, clone or update this repository, create a local .env from
.env-example, copy or bootstrap ~/.config/daikin-ac-control/tokens.json, and
run:
python3 -m unittest discover -v
python3 -m daikin_ac_control statusIf status succeeds, the agent can use the command map above without extra
setup. Keep .env and tokens.json out of git and readable only by the service
user.
This uses the official ONECTA endpoints:
https://idp.onecta.daikineurope.com/v1/oidc/authorize
https://idp.onecta.daikineurope.com/v1/oidc/token
https://api.onecta.daikineurope.com/v1/gateway-devices
PATCH https://api.onecta.daikineurope.com/v1/gateway-devices/{id}/management-points/{embeddedId}/characteristics/{characteristic}