curb_energy¶
A Python library to interact with the Curb API built on top of asyncio and aiohttp.
Documentation: http://curb-energy.readthedocs.io/en/latest/
Disclaimer¶
This project is not affiliated with Curb Inc.. Curb maintains a github repository of various projects and documents their API, which is built upon HAL.
I wanted something more pythonic than using HAL-tools to consume the API, and it was also a good opportunity for experimenting with using asyncio and aiohttp for handling streaming data.
Requirements¶
curb_energy requires Python 3.5 or later, mostly due to the async and type hint syntax used in the library.
Installation¶
curb_energy can be installed using pip
, easy_install
or setup.py
pip install curb_energy
You may want to install the library in a virtual environment to test things out.
License¶
curb_energy is offered under the Apache License 2.0.
Getting Started¶
To connect to the Curb REST API, you can instantiate a
curb_energy.client.RestApiClient
object passing in the required
credentials.
OAuth Tokens¶
Before you can interact with the Curb REST API, you’ll need to get a client
token and secret for your specific application. For that, you will need to
reach out to the Curb support team asking them for developer access to their
API. The defaults have been set to CHANGE_ME
explicitly for this reason
– you will be unable to proceed until getting these.
Once you have been given a client ID and token for your app, you can pass
this to the curb_energy.client.RestApiClient
along with the username
and password (or existing OAuth2 access/refresh token) to interact with the API.
Warning
Just to be clear: the Client Token and Client Secret are DIFFERENT from the username, password, or access and refresh tokens. The Client Token/Secret is used to identify the application interacting with the REST API, and the access and refresh tokens are used to identify the user.
The library will automatically fetch an access token when authenticating with a username and password. Subsequent requests to the REST API will use the access token. As well, the refresh token is automatically used to request a new access token when the previous access token has expired.
Runtime¶
The client can be used as a context manager to automatically handle logging in and cleaning-up:
import asyncio
from curb_energy.client import RestApiClient
async def main():
async with RestApiClient(username='user',
password='pass',
client_id='APP_CLIENT_ID',
client_token='APP_CLIENT_TOKEN') as client:
profiles = await client.profiles()
devices = await client.devices()
for profile in profiles:
print(profile)
for device in devices:
print(device)
asyncio.get_event_loop().run_until_complete(main())
Or in a more traditional way:
import asyncio
from curb_energy.client import RestApiClient
async def main():
client = RestApiClient(username='user', password='pass',
client_id='APP_CLIENT_ID',
client_token='APP_CLIENT_TOKEN')
try:
await client.authenticate()
profiles = await client.profiles()
devices = await client.devices()
for profile in profiles:
print(profile)
for device in devices:
print(device)
finally:
# Clean-up
await client.session.close()
asyncio.get_event_loop().run_until_complete(main())
API Documentation¶
curb_energy.client
¶
Client module for interacting with the Curb REST and Real-Time APIs
-
class
curb_energy.client.
AuthToken
(access_token=None, refresh_token=None, expires_in=0, user_id=0, token_type='bearer')[source]¶ Curb API OAuth2 Token. For more information, refer to: <https://oauth.net/articles/authentication/>
-
__init__
(access_token=None, refresh_token=None, expires_in=0, user_id=0, token_type='bearer')[source]¶ Create an
AuthToken
instance.Parameters:
-
expiry
¶ The expiration date of the token (in UTC)
Return type: datetime
Returns: Expiry date (in UTC) Return type: datetime.datetime
-
static
from_json
()[source]¶ Creates an AuthToken object from the given JSON payload.
Parameters: data ( str
) – Token dataReturn type: AuthToken
Returns: Creates an AuthToken instance from the given payload Raises: ValueError
-
-
class
curb_energy.client.
RestApiClient
(loop=None, username=None, password=None, auth_token=None, api_url='https://app.energycurb.com', client_token='CHANGE_ME', client_secret='CHANGE_ME', ssl_context=None)[source]¶ A client for the Curb REST API
-
__init__
(loop=None, username=None, password=None, auth_token=None, api_url='https://app.energycurb.com', client_token='CHANGE_ME', client_secret='CHANGE_ME', ssl_context=None)[source]¶ Initialize the REST API client.
The Curb API uses Oauth2 authentication. An access token can be fetched by supplying a valid username and password as credentials. Subsequent authentication with the API will be done using the Oauth2 token in the form of
AuthToken
.You can also pass an existing token instead of a username/password.
Parameters: - username (
Optional
[str
]) – Username - password (
Optional
[str
]) – Password - auth_token (
Optional
[AuthToken
]) – Oauth2 client token - api_url (
str
) – The URL to the Curb REST API - client_token (
str
) – The application client token (app identifier) - client_secret (
str
) – The application client secret (app password) - ssl_context (
Optional
[SSLContext
]) – Optional SSL
Warning
As a client to the Curb REST API, you MUST provide your own client_token and client_secret which identifies the APPLICATION you are developing. This is separate from the username/password or access token that identifies the USER accessing their data.
See <http://docs.energycurb.com/authentication.html> for more info. You’ll need to contact the Curb Support Team at <http://energycurb.com/support/> for assistance.
Example:
async with RestApiClient(username=user, password=pass, client_token='CHANGE_ME', client_secret='CHANGE_ME') as client: profiles = await client.profiles() devices = await client.devices() for profile in profiles: print(profile) for device in devices: print(device)
Or more traditionally:
client = RestApiClient(username=user, password=pass, client_token='CHANGE_ME', client_secret='CHANGE_ME') try: # Fetch and set the access token await client.authenticate() # code goes here finally: await client.session.close()
- username (
-
auth_token
¶ The AuthToken associated with the REST API session
Return type: AuthToken
Returns: The AuthToken associated with the session
-
authenticate
()[source]¶ Authenticates with the REST API by fetching an access token, raising an exception on failure. The access token is stored as a property. This method is automatically called when the client is used as a context manager.
Return type: AuthToken
Returns: The authentication token Raises: CurbBaseException
-
devices
()[source]¶ Return a list of devices associated with the authenticated user
Return type: List
[Device
]Returns: A list of devices
-
entry_point
()[source]¶ Return the resources the authenticated user has access to, namely Profiles and Devices. This is automatically called when the client is used as a context manager.
Return type: Dict
[~KT, ~VT]Returns: a dict of links to the Profiles and Devices
-
fetch_access_token
(client_token=None, client_secret=None, username=None, password=None)[source]¶ Fetches an access token using the given credentials. The supplied parameters override the original credentials passed to instance of the REST API client.
Parameters: Return type: Returns: Returns the access token after authentication
-
historical_data
(profile_id=0, granularity='1H', unit='$/hr', since=0, until=None)[source]¶ Return all recorded measurements for the given profile.
Parameters: - profile_id (
int
) – The profile configuration - granularity (
str
) – Per Minute, Per Hour (default), or Per Day - unit (
str
) – Dollars Per Hour, or Watts (default) - since (
int
) – Start time of measurements (in epoch format). Use 0 to indicate the beginning, which is the default. - until (
Optional
[int
]) – End time of measurements (in epoch format)
Return type: - profile_id (
-
profiles
()[source]¶ Return a list of profiles associated with the authenticated user.
Return type: List
[Profile
]Returns: A list of profiles
-
refresh_access_token
()[source]¶ Get a new access token using an existing refresh token and associated user ID. All other access tokens are immediately invalidated. When calling this method,
auth_token
is automatically set to the new token.Return type: AuthToken
Returns: a new access token Raises: CurbBaseException
whenauth_token
is not set
-
-
class
curb_energy.client.
RealTimeClient
(config, driver=<class 'hbmqtt.client.MQTTClient'>)[source]¶ A client to the Curb Energy Real-Time Streaming API
Todo
Refactor to support different access mechanisms. For now, we’re limited to using MQTT over WebSockets
-
__init__
(config, driver=<class 'hbmqtt.client.MQTTClient'>)[source]¶ Create an instance of
RealTimeClient
.Parameters: - config (
RealTimeConfig
) – Real Time Config - driver (
Callable
) – Real Time client driver
Example:
client = RealTimeClient(config) await client.connect() while condition: data = await client.stream() await client.disconnect()
Used as a context manager:
async with RealTimeClient(config) as client: while condition: data = await client.stream()
- config (
-
config
¶ Returns the configuration parameters for the client.
Return type: RealTimeConfig
-
is_connected
¶ Returns True if the real-time client has successfully established a connection with the Real-Time API, False otherwise.
Return type: bool
-
read
()[source]¶ Returns a single stream from the real-time API, or None when an error occurs. This may raise a
ValueError
when the returned data is invalid JSON.Return type: RealTimeMessage
Returns: Returns measurements Raises: ValueError
-
curb_energy.errors
¶
Errors and Exceptions module
curb_energy.models
¶
Classes for representing the Curb API resources
-
class
curb_energy.models.
Billing
(profile_id=-1, billing_model=None, day_of_month=1, zip_code=None, dollar_per_kwh=None, **kwargs)[source]¶ Billing describes how and when the customer is billed and is associated with a
BillingModel
instance.-
__init__
(profile_id=-1, billing_model=None, day_of_month=1, zip_code=None, dollar_per_kwh=None, **kwargs)[source]¶ The billing configuration for the customer
Parameters: - profile_id (
int
) – The Curb configuration profile - billing_model (
Optional
[BillingModel
]) – Billing model information - day_of_month (
int
) – The start day of the billing period - zip_code (
Optional
[int
]) – The zip code of the dwelling being monitored - dollar_per_kwh (
Optional
[float
]) – The price per kilowatt-hour
- profile_id (
-
-
class
curb_energy.models.
BillingModel
(sector='Residential', label=None, utility=None, name=None, **kwargs)[source]¶ The Billing Model describes the utility and billing tier for a given customer.
-
class
curb_energy.models.
Device
(id=-1, building_type=None, name=None, timezone=None, sensor_groups=None, **kwargs)[source]¶ A logical grouping of Sensor Groups. A “device” can be thought of as a unit representing a location being measured, such as a home.
Todo
Clarify with Curb what they really intend by this.
-
class
curb_energy.models.
Measurement
(granularity, since, until, unit, headers, data)¶ -
data
¶ Alias for field number 5
-
granularity
¶ Alias for field number 0
-
headers
¶ Alias for field number 4
-
since
¶ Alias for field number 1
-
unit
¶ Alias for field number 3
-
until
¶ Alias for field number 2
-
-
class
curb_energy.models.
Profile
(id=-1, display_name=None, real_time=None, register_groups=None, registers=None, widgets=None, billing=None, **kwargs)[source]¶ A profile defines how to interpret data, access real time data, and various other configuration options.
-
__init__
(id=-1, display_name=None, real_time=None, register_groups=None, registers=None, widgets=None, billing=None, **kwargs)[source]¶ Create an instance of a configuration profile
Parameters: - id (
int
) – The unique ID of the profile - display_name (
Optional
[str
]) – The friendly name of this profile/configuration - real_time (
Optional
[RealTimeConfig
]) – The configuration for the real-time API - register_groups (
Optional
[List
[RegisterGroup
]]) – The register groups associated with this config - registers (
Optional
[List
[Register
]]) – The list of registers associated with this config - widgets (
Optional
[List
[type
]]) – The list of widgets - billing (
Optional
[Billing
]) – The billing configuration
- id (
-
-
class
curb_energy.models.
RealTimeConfig
(topic=None, format='curb', prefix=None, ws_url=None, **kwargs)[source]¶ Configuration for the Real-Time client
-
class
curb_energy.models.
Register
(id='', multiplier=1, flip_domain=False, label=None, **kwargs)[source]¶ A source of power measurement data.
-
class
curb_energy.models.
RegisterGroup
(grid, normals, solar, use)[source]¶ A logical grouping of registers according to classification
-
class
curb_energy.models.
Sensor
(id=-1, name=None, arbitrary_name=None, **kwargs)[source]¶ An energy monitoring device (in this case, the Curb Hub)
curb_energy.schema
¶
The schema module helps convert the Curb API REST resources into Python-friendly objects.
-
class
curb_energy.schema.
BillingModelSchema
(extra=None, only=(), exclude=(), prefix='', strict=None, many=False, context=None, load_only=(), dump_only=(), partial=False)[source]¶ Billing Model: Utility/Provider information
-
class
curb_energy.schema.
BillingSchema
(extra=None, only=(), exclude=(), prefix='', strict=None, many=False, context=None, load_only=(), dump_only=(), partial=False)[source]¶ Billing Information for the monitored location
-
class
curb_energy.schema.
DeviceSchema
(extra=None, only=(), exclude=(), prefix='', strict=None, many=False, context=None, load_only=(), dump_only=(), partial=False)[source]¶ A monitored “location”, such as a home/building.
Todo
Why does Curb API call it “Device”?
-
class
curb_energy.schema.
ProfileSchema
(extra=None, only=(), exclude=(), prefix='', strict=None, many=False, context=None, load_only=(), dump_only=(), partial=False)[source]¶ Profiles define how to interpret data, access real time data, and various other configuration options.
-
class
curb_energy.schema.
RealTimeSchema
(extra=None, only=(), exclude=(), prefix='', strict=None, many=False, context=None, load_only=(), dump_only=(), partial=False)[source]¶ Source for Real-time data
-
class
curb_energy.schema.
RegisterGroupsSchema
(extra=None, only=(), exclude=(), prefix='', strict=None, many=False, context=None, load_only=(), dump_only=(), partial=False)[source]¶ A logical grouping of Registers.
-
class
curb_energy.schema.
RegisterSchema
(extra=None, only=(), exclude=(), prefix='', strict=None, many=False, context=None, load_only=(), dump_only=(), partial=False)[source]¶ Source for a single stream of power data. They can correspond to a physical circuit breaker.
-
class
curb_energy.schema.
RegistersSchema
(extra=None, only=(), exclude=(), prefix='', strict=None, many=False, context=None, load_only=(), dump_only=(), partial=False)[source]¶ A Collection of Registers
ChangeLog¶
0.0.2 (2017-04-15)¶
Minor Enhancements + Doc Updates
Changes¶
- Documentation updated to emphasize the need for supplying a client token and secret when using the library (in addition to the username and password credentials for interacting on behalf of a user)
Features¶
- Access tokens are automatically refreshed when they expire
Glossary¶
- auth token
- An OAuth2 token used to authenticate with the Curb REST API. The token may be either an access token or refresh token. Tokens should be treated like passwords – keep them safe and secure!
- profile
- A set of configuration options that collectively defines how to interpret data, and access real time data.
- register
- A source of power measurement data, such as a circuit breaker.
- register groups
- A collection of registers, logically grouped for a specific purpose, such as “use”, “mains”, “solar”, or “grid”.
- sensor
- An energy monitoring device, which in this case is the Curb Hub. A sensor will have one or more registers associated with it.
- sensor group
- A collection of sensor (Curb) devices, grouped in a logical manner, such as “Main Panel”. A given sensor will usually have at most 18 registers, and multiple sensors may be needed to cover numerous circuit breakers/electric panels in a given location.