Skip to content

dmbr0/ex_ttlock

Repository files navigation

TTlockClient

An Elixir client library for the TTLock Open Platform API with centralized OAuth 2.0 authentication management.

Features

  • Centralized Authentication: Single GenServer manages all OAuth token lifecycle
  • Automatic Token Refresh: Proactive token refresh before expiry
  • Thread-Safe: Safe concurrent access to authentication state
  • OTP Compliant: Proper supervision and fault tolerance
  • Zero Module Dependencies: No authentication logic scattered across modules

Installation

Add ex_ttlock to your list of dependencies in mix.exs:

def deps do
  [
    {:ex_ttlock, "~> 0.1.0"}
  ]
end

Quick Start

1. Configuration

Configure your TTLock application credentials:

# Option A: Direct configuration
TTlockClient.configure("your_client_id", "your_client_secret")

# Option B: Use environment variables (automatically loads from .env in dev/test)
TTlockClient.configure(
  System.get_env("TTLOCK_CLIENT_ID"), 
  System.get_env("TTLOCK_CLIENT_SECRET")
)

# Option C: One-liner with .env file
TTlockClient.start_with_env()  # Reads all vars from environment

2. Environment Setup (Recommended)

Create a .env file in your project root:

# .env
TTLOCK_CLIENT_ID=your_actual_client_id
TTLOCK_CLIENT_SECRET=your_actual_client_secret
TTLOCK_USERNAME=your_ttlock_username
TTLOCK_PASSWORD=your_ttlock_password

Important: Add .env to your .gitignore!

3. Authentication

# With environment variables
TTlockClient.authenticate(
  System.get_env("TTLOCK_USERNAME"), 
  System.get_env("TTLOCK_PASSWORD")
)

# Or use the all-in-one helper
TTlockClient.start_with_env()  # Configure + authenticate in one call

3. Making API Calls

Get valid tokens for your API requests:

case TTlockClient.get_valid_token() do
  {:ok, token} ->
    # Token is automatically refreshed if needed
    headers = [{"Authorization", "Bearer #{token}"}]
    # Make your TTLock API calls
    
  {:error, :not_authenticated} ->
    # Need to authenticate first
    TTlockClient.authenticate("username", "password")
    
  {:error, reason} ->
    # Handle authentication errors
    Logger.error("Auth error: #{inspect(reason)}")
end

Advanced Usage

All-in-One Setup

# Option 1: With .env file (recommended)
case TTlockClient.start_with_env() do
  :ok -> 
    {:ok, token} = TTlockClient.get_valid_token()
    # Ready to make API calls
    
  {:error, reason} ->
    # Handle setup error
end

# Option 2: Direct configuration
case TTlockClient.start("client_id", "client_secret", "username", "password") do
  :ok -> 
    {:ok, token} = TTlockClient.get_valid_token()
    # Ready to make API calls
    
  {:error, reason} ->
    # Handle setup error
end

Status Checking

case TTlockClient.status() do
  :not_configured -> 
    # Need to call TTlockClient.configure/2
  :configured -> 
    # Configured but need to authenticate
  :authenticated -> 
    # Ready for API calls
end

# Or use the convenience function
if TTlockClient.ready?() do
  # Make API calls
end

Manual Token Management

# Force token refresh (usually not needed)
TTlockClient.refresh_token()

# Get current user ID
{:ok, user_id} = TTlockClient.get_user_id()

# Reset all authentication state
TTlockClient.reset()

Configuration Options

Using .env Files (Recommended for Development)

The library automatically loads .env files in development and test environments:

# .env (in your project root)
TTLOCK_CLIENT_ID=your_client_id
TTLOCK_CLIENT_SECRET=your_client_secret
TTLOCK_USERNAME=your_username
TTLOCK_PASSWORD=your_password

Then use the simple setup:

# Reads all environment variables and sets up authentication
TTlockClient.start_with_env()

Application Configuration

# config/config.exs
config :ex_ttlock,
  client_id: System.get_env("TTLOCK_CLIENT_ID"),
  client_secret: System.get_env("TTLOCK_CLIENT_SECRET"),
  base_url: "https://euapi.ttlock.com"  # optional

Environment Variables (Production)

export TTLOCK_CLIENT_ID="your_client_id"
export TTLOCK_CLIENT_SECRET="your_client_secret"
export TTLOCK_USERNAME="your_username"
export TTLOCK_PASSWORD="your_password"

API Reference

Authentication

  • configure/2,3 - Set client credentials
  • authenticate/2 - Authenticate with username/password
  • get_valid_token/0 - Get current valid access token
  • get_user_id/0 - Get authenticated user ID
  • refresh_token/0 - Manually refresh token
  • status/0 - Get authentication status
  • ready?/0 - Check if ready for API calls
  • reset/0 - Clear all authentication state
  • start/4,5 - Configure and authenticate in one call
  • start_with_env/0 - Configure and authenticate using environment variables

Lock Management

  • get_locks/0,1,2,3,4 - Get paginated list of locks
  • get_lock/1 - Get detailed information about a specific lock
  • get_all_locks/0,1,2 - Get all locks (handles pagination automatically)

Passcode Management

  • add_permanent_passcode/2,3 - Add a permanent passcode via gateway
  • add_temporary_passcode/4,5 - Add a time-limited passcode via gateway
  • add_passcode/2,3,4,5,6,7,8 - Add passcode with full parameter control
  • change_passcode/2,3,4,5,6,7 - Change passcode name, value, or validity period
  • change_passcode_name/3 - Change only the passcode name
  • change_passcode_value/3 - Change only the passcode value
  • change_passcode_period/4 - Change only the passcode validity period
  • delete_passcode/2 - Delete a passcode via gateway
  • delete_passcode_via_gateway/2 - Delete a passcode via gateway (alias)
  • get_passcodes/1,2,3,4,5 - Get paginated list of passcodes for a lock
  • get_lock_passcodes/1,2 - Get all passcodes for a lock (convenience function)
  • search_passcodes/2 - Search passcodes by name or passcode value

Low-Level API

  • TTlockClient.Locks.get_lock_list/1 - Direct lock list API call
  • TTlockClient.Locks.get_lock_detail/1 - Direct lock detail API call
  • TTlockClient.Passcodes.add_passcode/1 - Direct passcode add API call
  • TTlockClient.Passcodes.change_passcode/1 - Direct passcode change API call
  • TTlockClient.Passcodes.delete_passcode/1 - Direct passcode delete API call
  • TTlockClient.Passcodes.get_passcode_list/1 - Direct passcode list API call
  • TTlockClient.Types.* - Type definitions and helper functions

Authentication States

  • :not_configured - No client credentials set
  • :configured - Client configured but not authenticated
  • :authenticated - Fully authenticated and ready

Passcode Management Examples

Adding Passcodes

# Add a permanent passcode
{:ok, %{keyboardPwdId: passcode_id}} = 
  TTlockClient.add_permanent_passcode(12345, 123456, "Guest Access")

# Add a temporary passcode (valid for 1 week)
start_time = DateTime.utc_now()
end_time = DateTime.add(start_time, 7, :day)
{:ok, result} = 
  TTlockClient.add_temporary_passcode(12345, 987654, start_time, end_time, "Week Access")

# Add with full control over parameters
{:ok, result} = 
  TTlockClient.add_passcode(12345, 555999, "Custom", 3, start_ms, end_ms, 2)

Changing Passcodes

# Change passcode name only
{:ok, %{errcode: 0, errmsg: "success"}} = 
  TTlockClient.change_passcode_name(12345, 67890, "Updated Guest Access")

# Change passcode value only
{:ok, result} = 
  TTlockClient.change_passcode_value(12345, 67890, 999888)

# Change validity period only
start_time = DateTime.utc_now()
end_time = DateTime.add(start_time, 30, :day)
{:ok, result} = 
  TTlockClient.change_passcode_period(12345, 67890, start_time, end_time)

# Change multiple properties at once
{:ok, result} = 
  TTlockClient.change_passcode(12345, 67890, "New Name", 888999, start_time, end_time)

Note: Passcode changes work via the cloud API for WiFi locks or locks connected to a gateway. At least one change parameter must be provided (name, passcode value, or validity period).

Deleting Passcodes

# Delete a passcode (works for WiFi locks or locks connected to a gateway)
{:ok, %{errcode: 0, errmsg: "success"}} = 
  TTlockClient.delete_passcode(12345, 67890)

# Alternative method (same functionality)
{:ok, result} = TTlockClient.delete_passcode_via_gateway(12345, 67890)

Note: Passcode deletion works via the cloud API for WiFi locks or locks connected to a gateway. The passcode will be removed from both the cloud and the physical lock.

Listing and Searching Passcodes

# Get all passcodes for a lock
{:ok, %{list: passcodes, total: count}} = TTlockClient.get_lock_passcodes(12345)

# Search for specific passcodes
{:ok, results} = TTlockClient.search_passcodes(12345, "Guest")

# Get paginated results with custom parameters
{:ok, response} = TTlockClient.get_passcodes(12345, nil, 1, 50, 1)

Architecture

The library uses a centralized authentication pattern with a GenServer that manages all OAuth state:

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Your App      │───▶│  TTlockClient   │───▶│ TTlockClient    │
│                 │    │ .API            │    │ .AuthManager    │
└─────────────────┘    └─────────────────┘    └─────────────────┘
                                                       │
                                               ┌───────▼────────┐
                                               │ TTlockClient   │
                                               │ .OAuthClient   │
                                               └────────────────┘

Key Benefits

  1. No Module Dependencies: Each module gets tokens without knowing about OAuth
  2. Automatic Refresh: Tokens refreshed 5 minutes before expiry
  3. Thread Safety: Safe concurrent access from multiple processes
  4. Fault Tolerance: Proper OTP supervision and error recovery
  5. Centralized Logic: All authentication logic in one place

Error Handling

The library provides detailed error information:

case TTlockClient.authenticate("username", "password") do
  :ok -> 
    # Success
    
  {:error, %{error_code: 10001, description: "Invalid credentials"}} ->
    # TTLock API error
    
  {:error, :not_configured} ->
    # Need to configure client first
    
  {:error, {:transport_error, reason}} ->
    # Network error
    
  {:error, reason} ->
    # Other errors
end

Common Error Codes

  • 10001 - Invalid credentials
  • 10004 - Token expired (handled automatically)
  • 10005 - Invalid client credentials

Testing

# Run tests
mix test

# Run with coverage
mix test.coverage

# Run specific test file
mix test test/ttlock_client_test.exs

# Watch mode for development
mix test.watch

Examples

The library includes several example scripts:

# Basic authentication example
elixir example.exs

# Simple setup with .env
elixir example.exs simple

# Real-time token monitoring
elixir example.exs monitor

# Lock management examples
elixir locks_example.exs

# Advanced lock operations
elixir locks_example.exs detail

# Passcode management examples
elixir passcodes_example.exs

# Advanced passcode operations
elixir passcodes_example.exs advanced

# Passcode deletion examples
elixir passcodes_example.exs delete

# Passcode change examples
elixir passcodes_example.exs change

# Passcode time helper examples
elixir passcodes_example.exs helpers

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Follow the style guides:
  4. Add tests for your changes
  5. Ensure all tests pass (mix test)
  6. Run code analysis (mix credo)
  7. Commit your changes (git commit -m 'Add amazing feature')
  8. Push to the branch (git push origin feature/amazing-feature)
  9. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

TTLock API Documentation

For complete TTLock API documentation, visit:

Support

  • Create an issue for bug reports or feature requests
  • Check existing issues before creating new ones
  • Provide clear reproduction steps for bugs

About

TTLock API Client written in Elixir

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages