Adaptations of this article are on Medium and Substack
Python Helper Scripts with Claude and InControl GraphQL API
A practical guide for using Claude to generate and maintain Python scripts that interact with the InControl GraphQL API for EV charging management.
Overview
The InControl platform by InCharge Energy provides a GraphQL API for managing EV charging infrastructure. This guide shows how to leverage Claude to create Python helper scripts for common tasks like monitoring charger status, managing charging sessions, and generating reports. Note: certain permission levels will not have API key access. If you do not see the API Keys option in Settings in the Management Portal, contact your organization's InContorl Admin or InCharge Energy account manager.
Prerequisites
Required Python Libraries
pip install requests python-dotenv gql graphql-core aiohttp
API Access Setup
- Get API Access: Log into your InControl portal and navigate to Settings → API Keys
 - Generate API Key: Create a new API key with appropriate role and account permissions
 - Note Your Endpoint: Your API endpoint will be 
https://your-app.inchargeus.net/api/graphql 
Environment Setup
Create a .env file in your project directory:
INCONTROL_CLIENT_ID=your_client_id_here
INCONTROL_CLIENT_SECRET=your_client_secret_here
INCONTROL_ENDPOINT=https://your-app.inchargeus.net/api/graphql
Base Python Template
Here's a foundational template for InControl API interactions:
import os
import requests
from dotenv import load_dotenv
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport
import asyncio
import base64
# Load environment variables
load_dotenv()
class InControlAPI:
    def __init__(self):
        self.client_id = os.getenv('INCONTROL_CLIENT_ID')
        self.client_secret = os.getenv('INCONTROL_CLIENT_SECRET')
        self.endpoint = os.getenv('INCONTROL_ENDPOINT')
        
        if not all([self.client_id, self.client_secret, self.endpoint]):
            raise ValueError("Missing required environment variables")
        
        self.headers = self._get_auth_headers()
        self.client = self._create_client()
    
    def _get_auth_headers(self):
        """Create authorization headers for API requests"""
        auth_string = f"{self.client_id}:{self.client_secret}"
        auth_bytes = auth_string.encode('ascii')
        auth_b64 = base64.b64encode(auth_bytes).decode('ascii')
        
        return {
            'Authorization': f'Bearer {auth_b64}',
            'Content-Type': 'application/json'
        }
    
    def _create_client(self):
        """Create GraphQL client with authentication"""
        transport = AIOHTTPTransport(
            url=self.endpoint,
            headers=self.headers
        )
        return Client(transport=transport, fetch_schema_from_transport=True)
    
    async def execute_query(self, query, variables=None):
        """Execute a GraphQL query"""
        try:
            result = await self.client.execute_async(query, variable_values=variables)
            return result
        except Exception as e:
            print(f"Query execution failed: {e}")
            return None
    
    def execute_query_sync(self, query_string, variables=None):
        """Synchronous query execution using requests"""
        payload = {
            'query': query_string,
            'variables': variables or {}
        }
        
        response = requests.post(
            self.endpoint,
            json=payload,
            headers=self.headers
        )
        
        if response.status_code == 200:
            return response.json()
        else:
            print(f"Request failed: {response.status_code} - {response.text}")
            return None
# Usage example
async def main():
    api = InControlAPI()
    
    # Example query to get chargers
    query = gql("""
        query GetChargers($first: Int, $orderBy: [ChargerOrderBy!]) {
            chargers(first: $first, orderBy: $orderBy) {
                nodes {
                    id
                    serialNumber
                    name
                    status
                    location {
                        name
                        address
                    }
                }
            }
        }
    """)
    
    variables = {
        "first": 10,
        "orderBy": [{"field": "SERIAL_NUMBER", "direction": "DESC"}]
    }
    
    result = await api.execute_query(query, variables)
    print(result)
if __name__ == "__main__":
    asyncio.run(main())
Working with Claude to Generate Scripts
Step 1: Schema Discovery
Ask Claude to help you explore the GraphQL schema:
Claude Prompt:
I'm working with the InControl GraphQL API for EV charging management. Can you help me create a Python script that uses introspection to discover the available types, queries, and mutations? I want to understand what data I can access and what operations I can perform.
Expected Claude Response Pattern: Claude will provide a script using GraphQL introspection queries to discover the schema structure, helping you understand available operations.
Step 2: Specific Task Scripts
Example Claude Prompts for Common Tasks:
Charger Status Monitoring
Using the InControl GraphQL API base template I provided, create a Python script that:
1. Queries all chargers and their current status
2. Filters for chargers that are offline or have errors
3. Sends an email alert if any issues are found
4. Logs the results to a file with timestamps
The charger query should include: id, serialNumber, name, status, lastSeen, and location information.
Usage Reporting
Create a Python script that generates a weekly usage report for EV chargers using the InControl GraphQL API. The script should:
1. Query charging sessions for the past 7 days
2. Calculate total energy delivered, session count, and average session duration
3. Group data by charger location
4. Export results to CSV format
5. Include error handling for missing data
Focus on the chargingSessions query with fields like startTime, endTime, energyDelivered, and charger details.
Automated Charger Configuration
I need a Python script to bulk update charger settings using the InControl GraphQL API. The script should:
1. Read charger IDs and new settings from a CSV file
2. Use GraphQL mutations to update each charger
3. Implement retry logic for failed updates
4. Log all changes and any errors
5. Provide a summary report of successful vs failed updates
Include proper error handling and validation of input data.
Step 3: Error Handling and Logging
Claude Prompt for Robust Error Handling:
Enhance my InControl GraphQL API Python scripts with comprehensive error handling that covers:
1. Network timeouts and connection errors
2. GraphQL query errors and validation failures
3. Authentication failures and token expiration
4. Rate limiting and API quotas
5. Data validation errors
Also add structured logging that includes:
- Timestamps for all operations
- Request/response logging for debugging
- Performance metrics (query execution time)
- Separate log levels for different types of events
Common GraphQL Queries and Mutations
Based on the InControl API documentation, here are common patterns:
Charger Management
# Get all chargers with status
GET_CHARGERS_QUERY = """
query GetChargers($first: Int, $after: String, $filter: ChargerFilter) {
    chargers(first: $first, after: $after, filter: $filter) {
        nodes {
            id
            serialNumber
            name
            status
            model
            manufacturer
            location {
                id
                name
                address
                latitude
                longitude
            }
            connectors {
                id
                type
                maxPower
                status
            }
            lastSeen
            firmwareVersion
        }
        pageInfo {
            hasNextPage
            endCursor
        }
        totalCount
    }
}
"""
# Update charger settings
UPDATE_CHARGER_MUTATION = """
mutation UpdateCharger($input: UpdateChargerInput!) {
    updateCharger(input: $input) {
        charger {
            id
            name
            status
        }
        errors {
            field
            message
        }
    }
}
"""
Session Management
# Get charging sessions
GET_SESSIONS_QUERY = """
query GetChargingSessions($first: Int, $filter: ChargingSessionFilter) {
    chargingSessions(first: $first, filter: $filter) {
        nodes {
            id
            startTime
            endTime
            energyDelivered
            status
            charger {
                id
                serialNumber
                location {
                    name
                }
            }
            vehicle {
                id
                vin
                make
                model
            }
            user {
                id
                email
            }
            cost
            duration
        }
    }
}
"""
Claude Collaboration Workflow
1. Initial Script Generation
Ask Claude to create the basic structure with your specific requirements.
2. Iterative Refinement
Use follow-up prompts to enhance the script:
- "Add better error handling to this script"
 - "Make this script more efficient for large datasets"
 - "Add configuration file support"
 - "Include progress bars for long operations"
 
3. Testing and Debugging
Share error messages with Claude:
- "I'm getting this GraphQL error: [paste error]. How should I fix the query?"
 - "The script is running slow with large datasets. How can I optimize it?"
 
4. Documentation Generation
Ask Claude to create documentation:
- "Generate comprehensive documentation for this script including usage examples"
 - "Create a README with installation and configuration instructions"
 
Best Practices for Claude Collaboration
1. Provide Context
Always include:
- Your current script or template
 - Specific error messages
 - Sample data structures
 - Your business requirements
 
2. Ask for Specific Improvements
Instead of "make this better," ask for:
- "Add pagination support for large result sets"
 - "Implement exponential backoff for retries"
 - "Add input validation with helpful error messages"
 
3. Request Different Approaches
Ask Claude to show multiple solutions:
- "Show me both synchronous and asynchronous versions"
 - "Compare using the gql library vs raw requests"
 - "What are the trade-offs between different error handling strategies?"
 
4. Validate GraphQL Syntax
Always ask Claude to verify:
- "Check this GraphQL query syntax for the InControl API"
 - "Validate these variable types match the schema"
 - "Ensure this mutation includes proper error handling"
 
Example: Complete Monitoring Script
Here's how you might work with Claude to build a comprehensive monitoring script:
Initial Prompt:
Create a comprehensive EV charger monitoring script using the InControl GraphQL API that:
1. Checks charger status every 5 minutes
2. Detects offline chargers, error states, and usage anomalies
3. Sends alerts via email and Slack
4. Maintains a SQLite database of historical status
5. Generates daily summary reports
6. Includes a web dashboard for real-time viewing
Use async/await for performance and include comprehensive logging.
Follow-up Refinements:
- "Add configuration file support for different alert thresholds"
 - "Include charger utilization metrics and trend analysis"
 - "Add support for multiple InControl tenants"
 - "Implement graceful shutdown and status persistence"
 
Troubleshooting Common Issues
Authentication Problems
- Verify Client ID and Secret format
 - Check API key permissions and scope
 - Ensure correct endpoint URL
 
Query Errors
- Use GraphiQL explorer to test queries
 - Validate field names against schema
 - Check required vs optional parameters
 
Rate Limiting
- Implement exponential backoff
 - Add request throttling
 - Monitor API usage patterns
 
Data Validation
- Validate input data before sending mutations
 - Handle partial failures in batch operations
 - Implement data consistency checks
 
Conclusion
By leveraging Claude's ability to understand GraphQL schemas and generate Python code, you can rapidly develop robust scripts for InControl API integration. The key is providing clear requirements, iterating on solutions, and building up a library of reusable components.
Remember to:
- Start with the base template and build incrementally
 - Use Claude for both initial development and ongoing maintenance
 - Document your scripts thoroughly for future reference
 - Test scripts against staging environments before production use
 
This approach allows you to focus on business logic while Claude handles the implementation details, making API integration much more efficient and maintainable.