# SmartThingsMCP Client Caching
The SmartThingsMCP Client includes comprehensive caching to reduce API calls and improve performance.
## Features
- **Automatic caching** of read-only operations (GET requests)
- **TTL-based expiration** (default: 5 minutes)
- **LRU eviction** when cache is full (default: 1000 entries)
- **Smart invalidation** on write operations
- **Cache statistics** tracking (hits, misses, hit rate)
- **Manual cache control** (enable/disable, clear, configure)
- **Visual feedback** - cache hits displayed in green in console
## How It Works
### Cached Operations
The following read-only operations are automatically cached:
- `list_locations` - List all locations
- `get_location` - Get specific location
- `list_devices` - List devices
- `get_device` - Get specific device
- `get_location_rooms` - Get rooms in location
- `get_room` - Get specific room
- `list_modes` - List available modes
- `get_current_mode` - Get current mode
- `list_scenes` - List scenes
- `get_scene` - Get specific scene
- `list_rules` - List rules
- `get_rule` - Get specific rule
- `get_device_components` - Get device components
- `get_device_capabilities` - Get device capabilities
- `get_device_health` - Get device health
### Not Cached
The following operations are **NOT** cached (frequently changing data):
- `get_device_status` - Device status changes frequently
- Any write operations (execute_command, create/update/delete operations)
### Cache Invalidation
Write operations automatically invalidate related cached data:
| Write Operation | Invalidates |
|----------------|-------------|
| `execute_command` | `get_device_status`, `get_device` |
| `update_device` | `list_devices`, `get_device` |
| `delete_device` | `list_devices` |
| `create_location` | `list_locations` |
| `update_location` | `list_locations`, `get_location` |
| `delete_location` | `list_locations` |
| `create_room` | `get_location_rooms` |
| `update_room` | `get_location_rooms`, `get_room` |
| `delete_room` | `get_location_rooms` |
| `set_mode` | `get_current_mode` |
## Visual Feedback
Cache hits are displayed in the console with a **green checkmark**:
```
✓ Cache hit: list_locations
✓ Cache hit: list_devices
```
This provides immediate visual confirmation that data is being served from cache rather than making an API call.
## Usage
### Basic Usage (Default Settings)
```python
from SmartThingsMCPClient import SmartThingsMCPClient
# Caching is enabled by default
client = SmartThingsMCPClient(
host="localhost",
port=8000,
transport="http"
)
# First call - cache miss (slower)
locations = await client.list_locations()
# Second call - cache hit (much faster!)
locations = await client.list_locations()
```
### Custom Cache Settings
```python
client = SmartThingsMCPClient(
host="localhost",
port=8000,
transport="http",
enable_cache=True, # Enable caching
cache_ttl=600, # 10 minutes TTL
max_cache_size=5000 # Max 5000 cached entries
)
```
### Disable Caching
```python
# Disable at initialization
client = SmartThingsMCPClient(
host="localhost",
port=8000,
transport="http",
enable_cache=False
)
# Or disable at runtime
client.set_cache_enabled(False)
```
### Cache Statistics
```python
# Get cache statistics
stats = client.get_cache_stats()
print(f"Cache enabled: {stats['enabled']}")
print(f"Cache size: {stats['size']}/{stats['max_size']}")
print(f"TTL: {stats['ttl_seconds']} seconds")
print(f"Total requests: {stats['total_requests']}")
print(f"Cache hits: {stats['hits']}")
print(f"Cache misses: {stats['misses']}")
print(f"Hit rate: {stats['hit_rate_percent']}%")
```
### Manual Cache Control
```python
# Clear all cached entries
client.clear_cache()
# Change cache TTL
client.set_cache_ttl(300) # 5 minutes
# Enable/disable caching
client.set_cache_enabled(True)
client.set_cache_enabled(False)
```
## Performance Benefits
### Expected Improvements
With typical usage patterns, caching provides:
- **60-80% reduction** in API calls
- **5-10x faster** response times for cached data
- **Lower rate limit usage**
- **Better user experience** with instant responses
### Example Benchmark
```
Operation: list_locations
First call (cache miss): 0.234s
Second call (cache hit): 0.021s (11x faster!)
Operation: list_devices
First call (cache miss): 0.456s
Second call (cache hit): 0.019s (24x faster!)
```
## Cache Keys
Cache keys are generated from:
1. Tool name (e.g., "list_devices")
2. Parameters (sorted for consistency)
3. MD5 hash of parameters (for shorter keys)
Example: `list_devices:a3f2b1c8`
## Best Practices
### 1. Use Default Settings for Most Cases
```python
# Simple and effective
client = SmartThingsMCPClient(host="localhost", port=8000)
```
### 2. Adjust TTL Based on Data Stability
```python
# For stable data (locations, rooms)
client = SmartThingsMCPClient(cache_ttl=3600) # 1 hour
# For dynamic environments
client = SmartThingsMCPClient(cache_ttl=60) # 1 minute
```
### 3. Monitor Cache Performance
```python
# Periodically check hit rate
stats = client.get_cache_stats()
if stats['hit_rate_percent'] < 50:
print("Low hit rate - consider increasing TTL")
```
### 4. Clear Cache After Bulk Operations
```python
# After making many changes
for device in devices:
await client.update_device(device['id'], new_label)
# Clear cache to get fresh data
client.clear_cache()
```
### 5. Disable Cache for Testing
```python
# During development/testing
client = SmartThingsMCPClient(enable_cache=False)
```
## Testing
Run the caching test script:
```bash
cd SmartThingsMCP
python test_caching.py
```
This will verify:
- Cache hits and misses
- Performance improvements
- Cache invalidation
- Manual cache control
- Statistics tracking
## Technical Details
### Implementation
- **Storage**: OrderedDict (LRU eviction)
- **Thread-safe**: No (async single-threaded)
- **Memory**: ~1KB per cached entry (typical)
- **Overhead**: <1ms per cache lookup
### Cache Keys
MD5 hash ensures:
- Consistent keys for same parameters
- Short keys (~40 chars)
- Fast lookups (O(1) dictionary access)
### LRU Eviction
When cache reaches max_cache_size:
- Oldest (least recently used) entry is removed
- New entry is added
- Maintains max_cache_size limit
## Troubleshooting
### Cache Not Working
```python
# Check if caching is enabled
stats = client.get_cache_stats()
print(f"Enabled: {stats['enabled']}")
# Check operation is cacheable
# Only read operations are cached (see list above)
```
### Low Hit Rate
Possible causes:
- TTL too short (data expires before reuse)
- Parameters vary too much (different cache keys)
- Max cache size too small (entries evicted)
Solutions:
- Increase TTL: `client.set_cache_ttl(600)`
- Increase cache size: use `max_cache_size=5000` at init
### Stale Data
```python
# If you need fresh data immediately
client.clear_cache()
# Or disable caching temporarily
client.set_cache_enabled(False)
result = await client.list_devices()
client.set_cache_enabled(True)
```
## Future Enhancements
Potential future improvements:
- Per-operation TTL configuration
- Cache warming strategies
- Persistent cache (Redis/disk)
- Cross-client cache sharing
- Cache compression
- Advanced invalidation patterns