This release is a pre-release and may not be stable for production use.
Pyecobee
A Python implementation of the ecobee API.
Pyecobee is a simple, elegant, and object oriented implementation of the ecobee API in Python. Requests and responses are Python objects: Pyecobee serializes them to and from the JSON the ecobee API expects, so you never have to build or parse JSON yourself.
Requires Python 3.12 or newer.
[!IMPORTANT] ecobee no longer accepts new developer registrations. Pyecobee can therefore only be used by people who already have a valid ecobee application key. Version 2.0.0b1 has extensive offline test coverage but has not been tested against the live ecobee API. Existing API users willing to test it are encouraged to report their results.
- Installation
- Quick start
- Requests and responses
- General usage
- Authorization and token requests
- Thermostat requests
- Report requests
- Group requests
- Hierarchy set requests
- Hierarchy user requests
- Hierarchy thermostat requests
- Utility requests
- Runtime report job requests
- Thermostat functions
- Credentials
- Token renewal
- Date and time handling
- Exception handling
- Development
[!WARNING] Earlier Pyecobee versions were tested with an ecobee Smart Si, but the version 2 modernization has not been tested against a live account. The following methods were also outside the historical live testing, though they should work. Please open an issue, or better still a pull request, if you hit a problem with any of them.
control_plug: requires an ecobee smart plugreset_preferences: wipes a thermostat's settingsset_occupied: requires an EMS thermostatunlink_voice_engine: requires a thermostat with voice controlupdate_sensor: requires an ecobee3 or ecobee4 thermostat- All hierarchy requests, accessible to EMS and Utility accounts only:
list_hierarchy_sets,list_hierarchy_users,add_hierarchy_set,remove_hierarchy_set,rename_hierarchy_set,move_hierarchy_set,add_hierarchy_users,remove_hierarchy_users,unregister_hierarchy_users,update_hierarchy_users,register_hierarchy_thermostats,unregister_hierarchy_thermostats,move_hierarchy_thermostats,assign_hierarchy_thermostats- All utility requests, accessible to Utility accounts only:
list_demand_responses,issue_demand_response,cancel_demand_response,issue_demand_managements- All runtime report job requests, accessible to Utility accounts only:
create_runtime_report_job,list_runtime_report_job_status,cancel_runtime_report_job
[!NOTE] ecobee currently returns 404 for the object definitions of
ECPDemandResponseandEquipmentUtilization. Responses stay forward compatible because unknown fields are ignored, andRuntime.equipment_utilizationis kept as an untyped value until ecobee publishes its shape.
Any ecobee API keys and authorization, access or refresh tokens in the examples below are fake.
Installation
pip install pyecobee
Quick start
from pyecobee import EcobeeService, Selection, SelectionType, Tokens
ecobee_service = EcobeeService(
"My Thermostat",
"jiNXJ2Q6dyeAPXxy4HsFGUp1nK94C9VF",
Tokens(), # or the credentials you stored last time
save_tokens, # called whenever new credentials are issued
)
# One-time authorization: display the PIN, register the app at ecobee.com, then request tokens.
authorize_response = ecobee_service.authorize()
print(f"Enter this PIN at ecobee.com => {authorize_response.ecobee_pin}")
input("Press Enter once the app is authorized...")
ecobee_service.request_tokens()
thermostat_response = ecobee_service.request_thermostats(
Selection(
selection_type=SelectionType.REGISTERED,
selection_match="",
include_runtime=True,
)
)
thermostat = thermostat_response.thermostat_list[0]
print(thermostat.name, thermostat.runtime.actual_temperature)
Pyecobee hands every new set of credentials to the callback and renews expired access tokens on its own. See Credentials.
Requests and responses
Building a request
Request models accept Python field names and serialize to ecobee's camelCase field names. Model construction rejects unknown fields, and enum fields require valid enum values.
from pyecobee import Selection, SelectionType
selection = Selection(
selection_type=SelectionType.THERMOSTATS,
selection_match="123456789012",
include_runtime=True,
)
assert selection.to_api_dict() == {
"selectionType": "thermostats",
"selectionMatch": "123456789012",
"includeRuntime": True,
}
Processing a response
Service methods deserialize responses into typed models. Access Python attributes, or use model_dump() when an
alias-based JSON-compatible mapping is needed.
response = ecobee_service.request_thermostats(selection)
response.status.code # 0 indicates success
thermostat = response.thermostat_list[0]
payload = response.model_dump(by_alias=True, exclude_none=True, mode="json")
Response deserialization ignores fields that ecobee adds after this release, including fields within nested objects.
Every model provides pretty_format() for alias-based diagnostic output, alongside the usual repr():
>>> repr(authorize_response)
"EcobeeAuthorizeResponse(ecobee_pin='bv29', code='...', scope='smartWrite', expires_in=9, interval=30)"
>>> authorize_response.pretty_format()
"EcobeeAuthorizeResponse({'code': '...', 'ecobeePin': 'bv29', ...})"
General usage
The EcobeeService class provides the ecobee API implementation. To use Pyecobee:
- Import the models you need.
- Instantiate an
EcobeeServiceobject. - Complete the authorization sequence if required (
authorizethenrequest_tokens). - Invoke the ecobee API requests and functions you need. Access tokens renew automatically.
Pyecobee ships with docstrings throughout. Use dir() and help() to explore:
>>> from pyecobee import EcobeeService
>>> dir(EcobeeService)
>>> help(EcobeeService)
Import the models
from pyecobee import EcobeeService, Selection, SelectionType, Thermostat, Tokens
# Import other models, enums, and exceptions by their explicit names.
Instantiate an EcobeeService object
ecobee_service = EcobeeService(
"My Thermostat", # a label of your choosing
"jiNXJ2Q6dyeAPXxy4HsFGUp1nK94C9VF", # your application key
Tokens(), # the credentials you hold, if any
save_tokens, # where to put the credentials ecobee issues
)
Every parameter is required. You pass the credentials you hold as tokens and get back every new set through
on_tokens_changed, because the ecobee API requires an application to store what it is issued and replaces the
refresh token every time it issues one.
Authorization and token requests
Authorize
authorize_response = ecobee_service.authorize()
print(f'Enter PIN "{authorize_response.ecobee_pin}" at ecobee.com')
A successful invocation of authorize() returns an EcobeeAuthorizeResponse instance.
Request tokens
token_response = ecobee_service.request_tokens()
A successful invocation of request_tokens() returns an EcobeeTokensResponse instance.
Refresh tokens
Renewal is automatic, so this is only needed to renew on your own schedule. See Token renewal.
token_response = ecobee_service.refresh_tokens()
A successful invocation of refresh_tokens() returns an EcobeeTokensResponse instance.
Thermostat requests
Request thermostat summary
thermostat_summary_response = ecobee_service.request_thermostats_summary(
selection=Selection(
selection_type=SelectionType.REGISTERED,
selection_match="",
include_equipment_status=True,
)
)
A successful invocation of request_thermostats_summary() returns an EcobeeThermostatsSummaryResponse instance.
Request thermostats
# Only set the include options you need to True. Most are set to True here for illustrative purposes only.
selection = Selection(
selection_type=SelectionType.REGISTERED,
selection_match="",
include_alerts=True,
include_audio=True,
include_capabilities=True,
include_device=True,
include_energy=True,
include_equipment_status=True,
include_events=True,
include_extended_runtime=True,
include_house_details=True,
include_location=True,
include_management=True,
include_notification_settings=True,
include_oem_cfg=False,
include_privacy=False,
include_program=True,
include_reminders=True,
include_runtime=True,
include_security_settings=False,
include_sensors=True,
include_settings=True,
include_technician=True,
include_utility=True,
include_version=True,
include_weather=True,
)
thermostat_response = ecobee_service.request_thermostats(selection)
assert thermostat_response.status.code == 0, (
f"Failure while executing request_thermostats:\n{thermostat_response.pretty_format()}"
)
A successful invocation of request_thermostats() returns an EcobeeThermostatResponse instance.
Update thermostat
from pyecobee import Function, Selection, SelectionType, Settings, Thermostat
update_thermostat_response = ecobee_service.update_thermostats(
selection=Selection(selection_type=SelectionType.REGISTERED, selection_match=""),
thermostat=Thermostat(
identifier="123456789012", settings=Settings(hvac_mode="off")
),
functions=[Function(type="deleteVacation", params={"name": "My vacation"})],
)
assert update_thermostat_response.status.code == 0, (
f"Failure while executing update_thermostats:\n{update_thermostat_response.pretty_format()}"
)
A successful invocation of update_thermostats() returns an EcobeeStatusResponse instance.
Report requests
Meter report
from datetime import datetime
from zoneinfo import ZoneInfo
eastern = ZoneInfo("America/New_York")
meter_reports_response = ecobee_service.request_meter_reports(
selection=Selection(
selection_type=SelectionType.THERMOSTATS,
selection_match="123456789012",
),
start_date_time=datetime(2013, 4, 4, 0, 0, 0, tzinfo=eastern),
end_date_time=datetime(2013, 4, 4, 23, 59, 0, tzinfo=eastern),
)
assert meter_reports_response.status.code == 0, (
f"Failure while executing request_meter_reports:\n{meter_reports_response.pretty_format()}"
)
A successful invocation of request_meter_reports() returns an EcobeeMeterReportsResponse instance.
Runtime report
eastern = ZoneInfo("America/New_York")
runtime_reports_response = ecobee_service.request_runtime_reports(
selection=Selection(
selection_type=SelectionType.THERMOSTATS,
selection_match="123456789012",
),
start_date_time=datetime(2010, 1, 1, 0, 0, 0, tzinfo=eastern),
end_date_time=datetime(2010, 1, 2, 0, 0, 0, tzinfo=eastern),
columns=(
"auxHeat1,auxHeat2,auxHeat3,compCool1,compCool2,compHeat1,compHeat2,dehumidifier,dmOffset,"
"economizer,fan,humidifier,hvacMode,outdoorHumidity,outdoorTemp,sky,ventilator,wind,zoneAveTemp,"
"zoneCalendarEvent,zoneClimate,zoneCoolTemp,zoneHeatTemp,zoneHumidity,zoneHumidityHigh,"
"zoneHumidityLow,zoneHvacMode,zoneOccupancy"
),
)
assert runtime_reports_response.status.code == 0, (
f"Failure while executing request_runtime_reports:\n{runtime_reports_response.pretty_format()}"
)
A successful invocation of request_runtime_reports() returns an EcobeeRuntimeReportsResponse instance.
Group requests
Request groups
group_response = ecobee_service.request_groups(
selection=Selection(selection_type=SelectionType.REGISTERED, selection_match="")
)
assert group_response.status.code == 0, (
f"Failure while executing request_groups:\n{group_response.pretty_format()}"
)
A successful invocation of request_groups() returns an EcobeeGroupsResponse instance.
Update groups
from pyecobee import Group
# Create groups
group_response = ecobee_service.update_groups(
selection=Selection(selection_type=SelectionType.REGISTERED, selection_match=""),
groups=[
Group(
group_ref="3d03a26fd80001",
group_name="ground_floor",
synchronize_alerts=True,
synchronize_vacation=True,
thermostats=["123456789101"],
),
Group(
group_ref="3bb5a91b180001",
group_name="first_floor",
synchronize_reset=True,
synchronize_vacation=True,
thermostats=["123456789102"],
),
],
)
# Update a group
group_response = ecobee_service.update_groups(
selection=Selection(selection_type=SelectionType.REGISTERED, selection_match=""),
groups=[
Group(
group_name="ground_floor",
group_ref="3d03a26fd80001",
synchronize_system_mode=True,
)
],
)
# Delete a group by setting its thermostats to an empty list
group_response = ecobee_service.update_groups(
selection=Selection(selection_type=SelectionType.REGISTERED, selection_match=""),
groups=[
Group(group_name="ground_floor", group_ref="3d03a26fd80001", thermostats=[])
],
)
assert group_response.status.code == 0, (
f"Failure while executing update_groups:\n{group_response.pretty_format()}"
)
A successful invocation of update_groups() returns an EcobeeGroupsResponse instance.
Hierarchy set requests
List hierarchy sets
list_hierarchy_sets_response = ecobee_service.list_hierarchy_sets(
set_path="/",
recursive=True,
include_privileges=True,
include_thermostats=True,
)
assert list_hierarchy_sets_response.status.code == 0, (
f"Failure while executing list_hierarchy_sets:\n{list_hierarchy_sets_response.pretty_format()}"
)
A successful invocation of list_hierarchy_sets() returns an EcobeeListHierarchySetsResponse instance.
Add hierarchy set
add_hierarchy_set_response = ecobee_service.add_hierarchy_set(
set_name="NewSet", parent_path="/"
)
Remove hierarchy set
remove_hierarchy_set_response = ecobee_service.remove_hierarchy_set(set_path="/NewSet")
Rename hierarchy set
rename_hierarchy_set_response = ecobee_service.rename_hierarchy_set(
set_path="/NewSet", new_name="ToRename"
)
Move hierarchy set
move_hierarchy_set_response = ecobee_service.move_hierarchy_set(
set_path="/ToMove", to_path="MainNode"
)
A successful invocation of add_hierarchy_set(), remove_hierarchy_set(), rename_hierarchy_set() or
move_hierarchy_set() returns an EcobeeStatusResponse instance.
Hierarchy user requests
List hierarchy users
list_hierarchy_users_response = ecobee_service.list_hierarchy_users(
set_path="/",
recursive=True,
include_privileges=True,
)
assert list_hierarchy_users_response.status.code == 0, (
f"Failure while executing list_hierarchy_users:\n{list_hierarchy_users_response.pretty_format()}"
)
A successful invocation of list_hierarchy_users() returns an EcobeeListHierarchyUsersResponse instance.
Add hierarchy users
from pyecobee import HierarchyPrivilege, HierarchyUser
add_hierarchy_users_response = ecobee_service.add_hierarchy_users(
users=[
HierarchyUser(user_name="new@user1.com", first_name="User", last_name="1"),
HierarchyUser(user_name="new@user2.com", first_name="User", last_name="2"),
],
privileges=[
HierarchyPrivilege(
set_path="/MainNode", user_name="new@user1.com", allow_view=True
),
HierarchyPrivilege(
set_path="/OtherNode", user_name="new@user1.com", allow_view=True
),
],
)
assert add_hierarchy_users_response.status.code == 0, (
f"Failure while executing add_hierarchy_users:\n{add_hierarchy_users_response.pretty_format()}"
)
Remove hierarchy users
remove_hierarchy_users_response = ecobee_service.remove_hierarchy_users(
set_path="/",
users=[
HierarchyUser(user_name="todelete@hierarchy.com"),
HierarchyUser(user_name="todelete2@hierarchy.com"),
],
)
Unregister hierarchy users
unregister_hierarchy_users_response = ecobee_service.unregister_hierarchy_users(
users=[
HierarchyUser(user_name="todelete@hierarchy.com"),
HierarchyUser(user_name="todelete2@hierarchy.com"),
]
)
Update hierarchy users
update_hierarchy_users_response = ecobee_service.update_hierarchy_users(
users=[
HierarchyUser(
user_name="user1@update.com",
first_name="Updated",
last_name="User",
phone="222-333-4444",
email_alerts=False,
)
],
privileges=[
HierarchyPrivilege(
set_path="/MainNode", user_name="user1@update.com", allow_view=True
),
HierarchyPrivilege(
set_path="/MainNode", user_name="user2@update.com", allow_view=True
),
HierarchyPrivilege(
set_path="/OtherNode", user_name="user2@update.com", allow_view=True
),
],
)
A successful invocation of add_hierarchy_users(), remove_hierarchy_users(), unregister_hierarchy_users() or
update_hierarchy_users() returns an EcobeeStatusResponse instance.
Hierarchy thermostat requests
Register thermostats
register_hierarchy_thermostats_response = ecobee_service.register_hierarchy_thermostats(
set_path="/OtherNode",
thermostats="123456789012,123456789013",
)
Unregister thermostats
unregister_hierarchy_thermostats_response = (
ecobee_service.unregister_hierarchy_thermostats(
thermostats="123456789012,123456789013"
)
)
Move thermostats
move_hierarchy_thermostats_response = ecobee_service.move_hierarchy_thermostats(
set_path="/MainNode",
to_path="/OtherNode",
thermostats="123456789012,123456789013",
)
Assign thermostats
assign_hierarchy_thermostats_response = ecobee_service.assign_hierarchy_thermostats(
set_path="/MainNode",
thermostats="123456789012,123456789013",
)
A successful invocation of any hierarchy thermostat request returns an EcobeeStatusResponse instance.
Utility requests
List demand responses
list_demand_responses_response = ecobee_service.list_demand_responses()
assert list_demand_responses_response.status.code == 0, (
f"Failure while executing list_demand_responses:\n{list_demand_responses_response.pretty_format()}"
)
A successful invocation of list_demand_responses() returns an EcobeeListDemandResponsesResponse instance.
Issue demand response
from pyecobee import DemandResponse, Event
issue_demand_response_response = ecobee_service.issue_demand_response(
selection=Selection(
selection_type=SelectionType.MANAGEMENT_SET, selection_match="/"
),
demand_response=DemandResponse(
name="myDR",
message="This is a DR!",
event=Event(
type="useEndTime",
name="apiDR",
start_date="2011-01-09",
start_time="11:37:18",
end_date="2011-01-10",
end_time="11:37:18",
cool_hold_temp=790,
heat_hold_temp=790,
is_temperature_absolute=True,
),
),
)
assert issue_demand_response_response.status.code == 0, (
f"Failure while executing issue_demand_response:\n{issue_demand_response_response.pretty_format()}"
)
A successful invocation of issue_demand_response() returns an EcobeeIssueDemandResponsesResponse instance.
Cancel demand response
cancel_demand_response_response = ecobee_service.cancel_demand_response(
demand_response_ref="c253a12e0b3c3c93800095"
)
A successful invocation of cancel_demand_response() returns an EcobeeStatusResponse instance.
Issue demand managements
from pyecobee import DemandManagement
issue_demand_managements_response = ecobee_service.issue_demand_managements(
selection=Selection(
selection_type=SelectionType.MANAGEMENT_SET, selection_match="/"
),
demand_managements=[
DemandManagement(
date="2012-01-01",
hour=5,
temp_offsets=[20, 20, 20, 0, 0, 0, 0, -20, -20, -20, 0, 0],
),
DemandManagement(
date="2012-01-01",
hour=6,
temp_offsets=[0, 0, 20, 20, 0, 0, 0, 0, 0, -20, -20, -20],
),
],
)
A successful invocation of issue_demand_managements() returns an EcobeeStatusResponse instance.
Runtime report job requests
Create runtime report job
from datetime import date
create_runtime_report_job_response = ecobee_service.create_runtime_report_job(
selection=Selection(
selection_type=SelectionType.THERMOSTATS, selection_match="123456789012"
),
start_date=date(2016, 7, 1),
end_date=date(2016, 10, 1),
columns="zoneCalendarEvent,zoneHvacMode,zoneHeatTemp,zoneCoolTemp,zoneAveTemp,dmOffset",
)
assert create_runtime_report_job_response.status.code == 0, (
f"Failure while executing create_runtime_report_job:\n{create_runtime_report_job_response.pretty_format()}"
)
A successful invocation of create_runtime_report_job() returns an EcobeeCreateRuntimeReportJobResponse instance.
List runtime report job status
list_runtime_report_job_status_response = ecobee_service.list_runtime_report_job_status(
job_id="123"
)
A successful invocation of list_runtime_report_job_status() returns an
EcobeeListRuntimeReportJobStatusResponse instance.
Cancel runtime report job
cancel_runtime_report_job_response = ecobee_service.cancel_runtime_report_job(
job_id="123"
)
A successful invocation of cancel_runtime_report_job() returns an EcobeeStatusResponse instance.
Thermostat functions
A successful invocation of any thermostat function returns an EcobeeStatusResponse instance.
Send message
update_thermostat_response = ecobee_service.send_message("Hello World")
assert update_thermostat_response.status.code == 0, (
f"Failure while executing send_message:\n{update_thermostat_response.pretty_format()}"
)
Acknowledge
from pyecobee import AckType
selection = Selection(
selection_type=SelectionType.REGISTERED,
selection_match="",
include_alerts=True,
)
thermostat_response = ecobee_service.request_thermostats(selection)
thermostat = thermostat_response.thermostat_list[0]
alerts = [alert for alert in thermostat.alerts if alert.text == message]
update_thermostat_response = ecobee_service.acknowledge(
thermostat_identifier=thermostat.identifier,
ack_ref=alerts[0].acknowledge_ref,
ack_type=AckType.ACCEPT,
)
Set hold
from pyecobee import HoldType
# Simplest form
update_thermostat_response = ecobee_service.set_hold(
hold_climate_ref="away",
hold_type=HoldType.NEXT_TRANSITION,
)
# Using a specific start and end date and time
eastern = ZoneInfo("America/New_York")
update_thermostat_response = ecobee_service.set_hold(
hold_climate_ref="away",
start_date_time=datetime(2017, 5, 10, 13, 0, 0, tzinfo=eastern),
end_date_time=datetime(2017, 5, 10, 14, 0, 0, tzinfo=eastern),
hold_type=HoldType.DATE_TIME,
)
# Using a duration
update_thermostat_response = ecobee_service.set_hold(
hold_climate_ref="away",
start_date_time=datetime(2017, 5, 10, 13, 0, 0, tzinfo=eastern),
hold_type=HoldType.HOLD_HOURS,
hold_hours=1,
)
# Specific heating and cooling temperatures, held indefinitely
update_thermostat_response = ecobee_service.set_hold(
cool_hold_temp=75,
heat_hold_temp=68,
hold_type=HoldType.INDEFINITE,
)
Resume program
update_thermostat_response = ecobee_service.resume_program(resume_all=False)
Create vacation
from pyecobee import FanMode
eastern = ZoneInfo("America/New_York")
update_thermostat_response = ecobee_service.create_vacation(
name="Christmas Vacation!",
cool_hold_temp=104,
heat_hold_temp=59,
start_date_time=datetime(2017, 12, 23, 10, 0, 0, tzinfo=eastern),
end_date_time=datetime(2017, 12, 28, 17, 0, 0, tzinfo=eastern),
fan_mode=FanMode.AUTO,
fan_min_on_time=0,
)
Delete vacation
update_thermostat_response = ecobee_service.delete_vacation(name="Christmas Vacation!")
Reset preferences
# Danger zone! This resets every user configurable setting to its factory default.
update_thermostat_response = ecobee_service.reset_preferences()
Credentials
The ecobee API requires that every credential it issues is stored by the application, and it replaces the refresh token each time it issues one. Pyecobee therefore takes the credentials you hold and a callback to store the ones it receives.
JsonFileTokenStore writes the credentials as JSON in a file only your user can read, which is all most applications
need. Its load and save are the two arguments the service asks for.
from pyecobee import EcobeeService, JsonFileTokenStore
store = JsonFileTokenStore("~/.config/pyecobee/tokens.json")
ecobee_service = EcobeeService(
"My Thermostat",
application_key,
store.load(),
store.save,
)
The file is written under a temporary name and renamed into place, so an interrupted save cannot leave a half-written file where your credentials used to be.
If the storage callback raises, Pyecobee does not suppress the exception. The newly issued credentials remain available
through ecobee_service.tokens while the process is still running.
[!WARNING] Catch storage callback exceptions and recover
ecobee_service.tokensbefore the process exits. By the time the callback runs, ecobee has already invalidated the previous refresh token. If the process exits before the new credentials are stored, they are lost and the application must authorize again.
Anywhere else you want to keep them, supply your own pair. Tokens is an immutable snapshot that converts to and from
a plain mapping through to_dict and from_dict, so a store is usually a few lines. For a desktop application,
consider keyring. For a server, take the application key and the initial
credentials from the environment or a secret manager.
Tokens omits the credentials from its representation, so logging one discloses only the expiries and the scope:
>>> ecobee_service.tokens
Tokens(held=access_token, refresh_token, access_token_expires_on=..., refresh_token_expires_on=..., scope=<Scope.SMART_WRITE: 'smartWrite'>)
First run
Authorization is only needed when no credentials are held. The callback stores whatever each step produces.
if ecobee_service.authorization_token is None:
authorize_response = ecobee_service.authorize()
input(
"Go to ecobee.com, enable My Apps in the settings tab, and install "
f'PIN "{authorize_response.ecobee_pin}". Press Enter here after ecobee '
"authorizes the app."
)
if ecobee_service.access_token is None:
ecobee_service.request_tokens()
Token renewal
An access token expires 3599 seconds (1 hour) after it is issued. A refresh token expires 30 days after it is issued, and the ecobee API does not report that expiry, so Pyecobee derives it.
Renewal is automatic. Before each request Pyecobee renews an access token that is within two minutes of expiring, and if ecobee answers that the token has already expired, it renews the credentials and sends the request once more. Each renewal reaches your callback.
# No token handling required. The request renews the credentials if it has to.
thermostat_summary_response = ecobee_service.request_thermostats_summary(
selection=Selection(
selection_type=SelectionType.REGISTERED,
selection_match="",
include_equipment_status=True,
)
)
Renewal needs a refresh token that is still valid. Once one has expired, ecobee raises
EcobeeAuthorizationException and the application must authorize again:
from pyecobee import EcobeeAuthorizationException
try:
thermostat_response = ecobee_service.request_thermostats(selection)
except EcobeeAuthorizationException:
authorize_again(ecobee_service)
refresh_tokens() remains available for an application that would rather renew on its own schedule, such as before
a long idle period.
Date and time handling
Some ecobee API requests expect a date and time in thermostat time, while others expect UTC.
Every EcobeeService method that accepts a datetime expects it in thermostat time, and the datetime must be
timezone aware.
from datetime import datetime
from zoneinfo import ZoneInfo
eastern = ZoneInfo("America/New_York")
start_date_time = datetime(
2017, 5, 1, 10, 0, 0, tzinfo=eastern
) # 2017/05/01 10:00:00 -0400
The method then either uses the datetime as is, or converts it to UTC, depending on what the ecobee API request
requires.
Exception handling
Your code should be prepared to handle the following exceptions:
EcobeeApiException: raised if a request results in an ecobee API error response. Exposesstatus_codeandstatus_message.EcobeeAuthorizationException: raised if a request results in a standard or extended OAuth error response. Exposeserror,error_descriptionanderror_uri.EcobeeRequestsException: raised if a request results in an exception from the underlyingrequestsmodule.EcobeeHttpException: raised if a request results in any other HTTP error.EcobeeDeserializationException: raised if a response cannot be converted into a model.
All of them derive from EcobeeException.
Development
Pyecobee is developed with uv and Ruff. To create the locked development environment:
uv sync --locked
Run the lint and formatting checks:
uv run ruff check .
uv run ruff format --check .
Run the offline regression suite, which enforces an 82% coverage minimum:
uv run pytest
tests/live_integration.py is not collected by pytest. It requires an existing valid ecobee application key,
contacts a real account, exercises only requests that read data, and stores its credentials in
~/.config/pyecobee/live_integration.json:
ECOBEE_APPLICATION_KEY=your_application_key uv run python tests/live_integration.py
Apply safe lint fixes, sort imports, and format the source tree:
uv run ruff check . --select I --fix
uv run ruff check . --fix
uv run ruff format .
See CHANGELOG.md for release notes and MIGRATION.md for the 1.x to 2.0 upgrade
path.
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyecobee-2.0.0b1.tar.gz.
File metadata
- Download URL: pyecobee-2.0.0b1.tar.gz
- Upload date:
- Size: 79.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.12.2 {"installer":{"name":"uv","version":"0.12.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e83ffb3597f0faea5a599c39573f25b6935bd0fe97efa8229546a837b3460db3
|
|
| MD5 |
718f0228bd4d02f5ae11a0017dea1368
|
|
| BLAKE2b-256 |
e6cbfe9534c7cd4fec99921f40879fa281e46dbb54f5f6b33f9dcab9dadbeff4
|
File details
Details for the file pyecobee-2.0.0b1-py3-none-any.whl.
File metadata
- Download URL: pyecobee-2.0.0b1-py3-none-any.whl
- Upload date:
- Size: 53.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.12.2 {"installer":{"name":"uv","version":"0.12.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ab745e0c99a828c18234bd2748eec5a6cdb510955e64c56d7e942a43badb187e
|
|
| MD5 |
02e21f8fa9c3c19aefc18e2c6bd6e590
|
|
| BLAKE2b-256 |
2c61cb5b25bef09b5c4838cc565f6d3577eb796678ac5a9a8a1600d286b56a4e
|