Skip to main content

GitHub Release GitHub Activity Coverage License

Project Maintenance BuyMeCoffee

PyPI

carrier_api

Async Python client for Carrier Infinity and Bryant Evolution HVAC systems.

This package wraps the Carrier Infinity GraphQL API, exposes typed model objects for system profile, status, configuration, and energy data, and includes helper methods for common thermostat updates. It is intended for integrations and local automation code that need a lightweight API client rather than a full application.

The client is unofficial and depends on Carrier's private web and mobile API behavior. Carrier can change that API without notice.

Requirements

  • Python 3.14 or newer
  • A Carrier or Bryant account that works in the official app
  • Network access to Carrier's cloud API

Installation

Install the published package in a virtual environment:

python3.14 -m venv .venv
.venv/bin/python -m pip install carrier_api

Live Smoke Test

The live smoke test connects to a real Carrier account, prints loaded system state, starts websocket listeners, and sends one sample manual activity update to the first available zone.

scripts/live_smoke_test

Use it only when you want to exercise the live Carrier API. It can change thermostat settings.

To run non-interactively, put credentials in a local env file and pass it with --credentials-file. Supported keys are CARRIER_USERNAME/CARRIER_PASSWORD.

scripts/live_smoke_test --credentials-file scripts/carrier_api.env --output-file /private/tmp/carrier_api.txt --schema-output-file /private/tmp/carrier_api_schema.json --read-only

Use --output-file when you want a full transcript for later API debugging or fixture updates. Use --schema-output-file to also capture the authenticated GraphQL introspection schema without temporarily editing the client. Use --read-only to skip the sample thermostat mutation while still loading systems, capturing schema, and listening for websocket messages.

Basic Usage

import asyncio

from carrier_api import ApiConnectionGraphql


async def main() -> None:
    api = ApiConnectionGraphql(
        username="your-account@example.com",
        password="your-password",
    )
    try:
        systems = await api.load_data()
        for system in systems:
            print(system.profile.name)
            print(system.status.mode)
            print(system.as_dict())
    finally:
        await api.cleanup()


asyncio.run(main())

load_data() authenticates when needed and returns a list of System objects. Each System includes:

  • profile: system identity and location metadata
  • status: current mode, temperature, humidity, zone, and equipment state
  • config: configured zones, activities, schedules, and holds
  • energy: reported energy usage data

Model objects provide as_dict() for structured serialization. Their string and repr forms are intended for readable debugging output.

System also exposes HVAC capability helpers:

  • supports_heat()
  • supports_cool()
  • supports_fan()
  • supported_hvac_capabilities()

These helpers prefer Carrier's energy configuration flags, then use known equipment hints from Carrier's indoor and outdoor unit types to supplement.

For zone-level profile resolution, call ConfigZone.current_status_activity(status_zone) with matching zones from the same system. This uses Carrier's reported current activity type from live status data. Use ConfigZone.current_scheduled_activity() when you want the activity implied by schedule and hold configuration instead.

Deprecated compatibility aliases:

  • StatusZone.current_activity is an alias for StatusZone.current_status_activity_type. It returns Carrier's live status-derived activity type/name.
  • ConfigZone.current_activity() is an alias for ConfigZone.current_scheduled_activity(). It returns the schedule/configuration-derived activity.

To get both activity sources together, use ConfigZone.as_dict(status_zone)["current_activity"] or System.as_dict()["config"]["zones"][...]["current_activity"]. That object has from_schedule and from_status keys. System.as_dict() performs the status/config pairing for the aggregate system.

System.as_dict() includes supported_hvac_capabilities and passes status-zone context into config serialization so each zone dictionary includes current_activity.from_schedule and current_activity.from_status.

Updating Thermostat Settings

Mutation helpers send Carrier configuration updates and then request websocket reconciliation when a websocket manager is available.

import asyncio

from carrier_api import ActivityTypes, ApiConnectionGraphql, FanModes, SystemModes


async def main() -> None:
    api = ApiConnectionGraphql(
        username="your-account@example.com",
        password="your-password",
    )
    try:
        systems = await api.load_data()
        system = systems[0]
        zone = system.config.zones[0]

        await api.set_config_mode(system.profile.serial, SystemModes.AUTO)
        await api.update_fan(
            system_serial=system.profile.serial,
            zone_id=zone.api_id,
            activity_type=ActivityTypes.HOME,
            fan_mode=FanModes.LOW,
        )
        await api.set_config_manual_activity(
            system_serial=system.profile.serial,
            zone_id=zone.api_id,
            heat_set_point="68",
            cool_set_point="74",
            fan_mode=FanModes.LOW,
        )
    finally:
        await api.cleanup()


asyncio.run(main())

Available update helpers include:

  • set_config_mode(...)
  • set_config_heat_humidity(...)
  • set_heat_source(...)
  • set_humidifier(...)
  • update_fan(...)
  • set_config_hold(...)
  • resume_schedule(...)
  • set_config_manual_activity(...)

These methods can change real HVAC settings. Validate the selected system, zone, mode, and set points before calling them from automation.

Realtime Updates

ApiWebsocket manages Carrier realtime messages. WebsocketDataUpdater can merge incoming websocket messages into the System objects returned by load_data().

import asyncio

from carrier_api import ApiConnectionGraphql, WebsocketDataUpdater


async def main() -> None:
    api = ApiConnectionGraphql(
        username="your-account@example.com",
        password="your-password",
    )
    try:
        systems = await api.load_data()
        updater = WebsocketDataUpdater(systems)

        if api.api_websocket is None:
            raise RuntimeError("websocket manager was not initialized")

        api.api_websocket.callback_add(updater.message_handler)
        await api.api_websocket.create_task_listener()
    finally:
        await api.cleanup()


asyncio.run(main())

Callbacks receive the raw websocket message text. Register WebsocketDataUpdater.message_handler first when later callbacks need to read the updated in-memory system state.

Development

Clone this repository and install the development environment:

git clone https://github.com/dahlb/carrier_api.git
cd carrier_api
scripts/setup

Use the repository scripts so commands run through the local .venv.

scripts/setup
scripts/test
scripts/lint

The underlying commands are:

./.venv/bin/pytest
./.venv/bin/prek run --all-files
./.venv/bin/mypy .

Add or update deterministic pytest coverage for behavior changes. Fixture data for GraphQL and websocket responses lives under tests/graphql and tests/messages.

Updating the Captured Schema

To refresh captured GraphQL schema data, run the live smoke test with --schema-output-file:

scripts/live_smoke_test --credentials-file scripts/carrier_api.env --schema-output-file /private/tmp/carrier_api_schema.json --read-only

The command authenticates with Carrier, runs the GraphQL introspection query, and writes the schema JSON to the path you provide. Add --output-file /private/tmp/carrier_api.txt when you also want the full live smoke-test transcript.

Contributing

See CONTRIBUTING.md for contribution guidance. Please open bugs and feature requests in the issue tracker.

Metadata

Release files for carrier-api 3.6.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for carrier-api 3.6.1
File Size Uploaded
carrier_api-3.6.1.tar.gz 67.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for carrier-api 3.6.1
File Interpreter ABI Platform
carrier_api-3.6.1-py3-none-any.whl Python 3 none any Details

Total release size: 109.0 kB

Release files / carrier_api-3.6.1.tar.gz

Download URL carrier_api-3.6.1.tar.gz
Size 67.5 kB
Tags Source
SHA-256 checksum
How to use checksums
2606fe3cd38268ca4c29df223886892f2d97e60534219ce0e9a0b53f7f10ff66
BLAKE2b-256 checksum
How to use checksums
236a475e6b1f8e609fefda512c482839bfd5e06181a8268ee79839da4768b242
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / carrier_api-3.6.1-py3-none-any.whl

Download URL carrier_api-3.6.1-py3-none-any.whl
Size 41.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ccf3956d7608dacc95cf28ba972e0807e06a3b82fc3f39b739f82f5b29ea727d
BLAKE2b-256 checksum
How to use checksums
5bcc4b0eae55772fc295c640c0c37e89baf6111a28a19c81c8213c2286fe7771
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

3.6.2

2 release files

This release

3.6.1 This release

2 release files

3.6.0

2 release files

3.5.0

2 release files

3.4.2

2 release files

3.4.1

2 release files

3.4.0

2 release files

3.3.0

2 release files

3.2.0

2 release files

3.1.0

2 release files

3.0.0

2 release files

2.12.0

2 release files

2.11.4

2 release files

2.11.2

2 release files

2.11.1

2 release files

2.11.0

2 release files

2.10.0

2 release files

2.9.0

2 release files

2.8.13

2 release files

2.8.12

2 release files

2.8.9

2 release files

2.8.8

2 release files

2.8.7

2 release files

2.8.6

2 release files

2.8.5

2 release files

2.8.4

2 release files

2.8.3

2 release files

2.8.2

2 release files

2.8.1

2 release files

2.8.0

2 release files

2.7.9

2 release files

2.7.8

2 release files

2.7.7

2 release files

2.7.6

2 release files

2.7.5

2 release files

2.7.4

2 release files

2.7.3

2 release files

2.7.2

2 release files

2.7.1

2 release files

2.7.0

2 release files

2.6.2

2 release files

2.6.1

2 release files

2.6.0

2 release files

2.5.1

2 release files

2.5.0

2 release files

2.4.3

2 release files

2.4.2

2 release files

2.4.1

2 release files

2.4.0

2 release files

2.3.3

2 release files

2.3.2

2 release files

2.3.1

2 release files

2.3.0

2 release files

2.2.1

2 release files

2.2.0

2 release files

2.1.6

2 release files

2.1.5

2 release files

2.1.4

2 release files

2.1.3

2 release files

2.1.2

2 release files

2.1.1

2 release files

2.1.0

2 release files

1.17.1

2 release files

1.17.0

2 release files

1.16.2

2 release files

1.16.1

2 release files

1.16.0

2 release files

1.15.0

2 release files

1.14.2

2 release files

1.14.1

2 release files

1.14.0

2 release files

1.13.0

2 release files

1.12.1

2 release files

1.12.0

2 release files

1.11.0

2 release files

1.10.1

2 release files

1.10.0

2 release files

1.9.3

2 release files

1.9.2

2 release files

1.9.0

2 release files

1.8.0

2 release files

1.7.0

2 release files

1.6.0

2 release files

1.5.9

2 release files

1.5.8

2 release files

1.5.7

2 release files

1.5.6

2 release files

1.5.5

2 release files

1.5.4

2 release files

1.5.3

2 release files

1.5.2

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.1

2 release files

1.0.0

3 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page