== 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` {{{
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!
LogoutNo devices found.
{% endif %} {% endif %} {% if show_clients and clients is defined %}No clients found.
{% endif %} {% endif %} {% if not show_devices and not show_clients %}