== PA-API OAuth2 Python Example The following is an example of accessing the Telldus PA-API using Python and OAuth 2.0. This example requires the following 3rd party Python packages: **Flask**, **requests** You can install them using pip: {{{ pip install -U flask requests }}} == The Example Project **Running the Project** You can run the project's main application using Python from the root of the project folder: {{{ python app.py }}} Then open your browser at `http://127.0.0.1:5000`. ### Project Directory You can organise your project directory as follows: {{{ telldus_oauth2_flask_app/ │ ├── app.py ├── config.py ├── requirements.txt └── templates/ └── index.html }}} == App & Configurations **Configuration** We define some commonly used constants here. `config.py` {{{ # config.py import os # Obtain the client ID and client secret from https://pa-api.telldus.com/keys/showToken CLIENT_ID = 'Your client ID (Public key)' CLIENT_SECRET = 'Your client secret (Private key)' # Default Telldus Live! account credentials USERNAME = 'your@email.com' PASSWORD = 'your_password' # OAuth2 and API endpoints TOKEN_URL = 'https://pa-api.telldus.com/oauth2/accessToken' API_BASE_URL = 'https://pa-api.telldus.com/oauth2' # Scope requested during token exchange SCOPE = 'live-app' # Flask secret key for session management SECRET_KEY = os.urandom(24) # User-Agent sent with all API requests USER_AGENT = 'MyTelldusApp/1.0' }}} `requirements.txt` {{{ Flask requests }}} **The Flask Application** The main application. `app.py` {{{ # app.py from flask import Flask, session, redirect, url_for, request, render_template, flash import requests import config import json app = Flask(__name__) app.config.from_object('config') # --------------------------------------------------------------------------- # OAuth2 helpers # --------------------------------------------------------------------------- def obtain_tokens(username, password): """Obtain OAuth2 access and refresh tokens using the password grant.""" payload = { 'grant_type': 'password', 'username': username, 'password': password, 'scope': config.SCOPE, 'client_id': config.CLIENT_ID, 'client_secret': config.CLIENT_SECRET } headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': config.USER_AGENT } response = requests.post(config.TOKEN_URL, data=payload, headers=headers) response.raise_for_status() data = response.json() access_token = data.get('access_token') refresh_token = data.get('refresh_token') if not access_token: raise ValueError(data.get('error_description', 'Failed to obtain access token.')) return access_token, refresh_token def refresh_tokens(refresh_token): """Obtain a new access token using a refresh token.""" payload = { 'grant_type': 'refresh_token', 'refresh_token': refresh_token, 'client_id': config.CLIENT_ID, 'client_secret': config.CLIENT_SECRET } headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': config.USER_AGENT } response = requests.post(config.TOKEN_URL, data=payload, headers=headers) response.raise_for_status() data = response.json() return data.get('access_token'), data.get('refresh_token') def api_get(path, access_token, params=None): """Perform an authenticated GET request to the Telldus OAuth2 API.""" headers = { 'Authorization': f'Bearer {access_token}', 'User-Agent': config.USER_AGENT } url = f'{config.API_BASE_URL}/{path}' response = requests.get(url, headers=headers, params=params or {}) response.raise_for_status() return response.json() def print_debug_message(message): print(f'[DEBUG] {message}') # --------------------------------------------------------------------------- # Routes # --------------------------------------------------------------------------- @app.route('/') def index(): access_token = session.get('access_token') print_debug_message('Accessing home page.') if not access_token: print_debug_message('No access token found. User not authenticated.') return render_template('index.html', authenticated=False) # Example API call: list devices params = { 'supportedMethods': 65535, 'format': 'json' } print_debug_message(f'Making API call to {config.API_BASE_URL}/devices/list') try: result = api_get('devices/list', access_token, params) devices = result.get('device', []) print_debug_message(f'Devices fetched: {json.dumps(devices, indent=2)}') except Exception as e: flash('An error occurred while fetching devices.', 'danger') print_debug_message(f'Exception during API call: {str(e)}') devices = [] return render_template('index.html', authenticated=True, devices=devices, access_token=access_token) @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form.get('username', '').strip() password = request.form.get('password', '').strip() if not username or not password: flash('Username and password are required.', 'warning') return redirect(url_for('login')) print_debug_message(f'Attempting OAuth2 login for user: {username}') try: access_token, refresh_token = obtain_tokens(username, password) print_debug_message('OAuth2 tokens obtained successfully.') except Exception as e: flash(f'Login failed: {e}', 'danger') print_debug_message(f'Login failed: {e}') return redirect(url_for('login')) session['access_token'] = access_token session['refresh_token'] = refresh_token flash('You have successfully logged in.', 'success') return redirect(url_for('index')) return render_template('index.html', authenticated=False, show_login_form=True) @app.route('/logout') def logout(): print_debug_message('Logging out user.') session.clear() flash('You have been logged out.', 'success') return redirect(url_for('index')) @app.route('/devices') def devices(): access_token = session.get('access_token') print_debug_message('Accessing devices route.') if not access_token: flash('You need to log in first.', 'warning') return redirect(url_for('index')) params = {'supportedMethods': 65535, 'format': 'json'} try: result = api_get('devices/list', access_token, params) devices = result.get('device', []) except Exception as e: flash('Failed to fetch devices.', 'danger') print_debug_message(f'Exception: {e}') devices = [] return render_template('index.html', authenticated=True, devices=devices, show_devices=True) @app.route('/clients') def clients(): access_token = session.get('access_token') print_debug_message('Accessing clients route.') if not access_token: flash('You need to log in first.', 'warning') return redirect(url_for('index')) params = {'format': 'json'} try: result = api_get('clients/list', access_token, params) clients = result.get('client', []) except Exception as e: flash('Failed to fetch clients.', 'danger') print_debug_message(f'Exception: {e}') clients = [] return render_template('index.html', authenticated=True, clients=clients, show_clients=True) if __name__ == '__main__': app.secret_key = config.SECRET_KEY app.run(debug=True) }}} **The HTML Template** The home page also serves as the login form and results view. `templates/index.html` {{{ Telldus OAuth2 Flask App
{% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} {% for category, message in messages %} {% endfor %} {% endif %} {% endwith %}

Telldus Live! OAuth2 Integration

{% if not authenticated %} {% if show_login_form %}

Enter your Telldus Live! credentials to connect.

{% else %}

You are not connected to Telldus Live!.

Connect with Telldus {% endif %} {% else %}

You are connected to Telldus Live!

Logout
{% if show_devices or devices is defined %}

Devices

{% if devices %} {% else %}

No devices found.

{% endif %} {% endif %} {% if show_clients and clients is defined %}

Clients

{% if clients %} {% else %}

No clients found.

{% endif %} {% endif %} {% if not show_devices and not show_clients %}

Quick Links

View Devices View Clients {% endif %} {% endif %}
}}} == Command-Line Alternative If you only need to obtain tokens without a web interface, you can use a standalone script. The repository includes `oauth2.py` for this purpose. Create a `config.ini` file: {{{ [oauth2] client_id = Your client ID client_secret = Your client secret username = your@email.com password = your_password [server] user_agent = MyTelldusApp/1.0 }}} Run the script: {{{ python oauth2.py }}} On success, the access token and refresh token are printed to the console. == Testing an API Call from the Command Line Once you have an access token, you can test an API call directly with curl: {{{ curl -X GET "https://pa-api.telldus.com/oauth2/devices/list?supportedMethods=65535&format=json" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "User-Agent: MyTelldusApp/1.0" }}} Replace `YOUR_ACCESS_TOKEN` with the token obtained from the login flow or the command-line script.