Rename integration for consistent branding: - User-facing name: "Spotify Voice Assistant" - Integration name: "Spotify Voice Assistant Search" - Domain: spotify_voice_assistant Changes: - Rename custom_components/spotify_search/ to spotify_voice_assistant/ - Update domain in manifest.json and __init__.py - Update all service references in examples - Update all documentation references
11 KiB
Development Guide
Development Setup
This repository is set up for active development while being used in a running Home Assistant instance.
Recommended Setup: Docker Bind Mount
For Docker-based Home Assistant installations, use a bind mount to link this repository directly into your container. This is the cleanest approach for active development.
Add this volume to your Docker run command or docker-compose.yml:
# Docker run example
docker run ... \
-v /path/to/spotify-voice-assistant/custom_components/spotify_voice_assistant:/config/custom_components/spotify_voice_assistant:ro \
...
# docker-compose.yml example
services:
homeassistant:
volumes:
- /path/to/spotify-voice-assistant/custom_components/spotify_voice_assistant:/config/custom_components/spotify_voice_assistant:ro
Benefits:
- ✅ Edit files in your development repository
- ✅ Changes immediately reflected (after restart/reload)
- ✅ Develop, test, and commit from one location
- ✅ No file duplication or sync issues
- ✅ Read-only mount (
:ro) protects your source files
Note: You'll need to recreate/restart your container after adding the bind mount.
Alternative: Symlink Development (Non-Docker or Host Config)
If you have direct filesystem access to your Home Assistant config directory:
# Remove existing installation (if using HACS version)
rm -rf /path/to/homeassistant/config/custom_components/spotify_voice_assistant
# Create symlink to your development repo
ln -s /path/to/your/spotify-voice-assistant/custom_components/spotify_voice_assistant \
/path/to/homeassistant/config/custom_components/spotify_voice_assistant
Important for Docker: If using symlinks with Docker, ensure the symlink target path is accessible from inside the container (either mounted or within the config volume).
When to Restart vs Reload
Understanding when you need to restart Home Assistant vs just reloading the integration:
Full Restart Required:
- Python code changes (
__init__.py) - Manifest changes (
manifest.json) - Adding/removing the integration
- First-time setup
Reload Integration Only:
- Service definition changes (
services.yaml) - Go to Developer Tools > YAML > Reload "Core Configuration"
No Action Required:
- Documentation changes (README.md, DEVELOPMENT.md)
- Git operations (commit, push, tag)
Development Workflow
1. Make Changes
cd /path/to/your/spotify-voice-assistant
# Edit files in custom_components/spotify_voice_assistant/
2. Test Changes
# For Python code changes - full restart
docker restart homeassistant
# For YAML changes - reload only (faster)
# Use Developer Tools > YAML > Core Configuration
# Watch logs in real-time
docker logs -f homeassistant 2>&1 | grep spotify_voice_assistant
3. Commit Changes
git add .
git commit -m "Description of changes"
git push
4. Create Release (when ready)
- Update version in
manifest.json - Push changes
- Create new GitHub release (e.g., v1.1.0)
- HACS will auto-detect and notify users
Switching Between Development and HACS
Switch to HACS Version
When you want to use the stable HACS version instead of development:
For bind mount setup:
# Remove the bind mount from your Docker configuration
# Recreate the container without the bind mount
docker restart homeassistant
# Install via HACS
# HACS > Integrations > Spotify Voice Assistant > Install
# Restart Home Assistant
For symlink setup:
# Remove symlink
rm /path/to/homeassistant/config/custom_components/spotify_voice_assistant
# Install via HACS
# HACS > Integrations > Spotify Voice Assistant > Install
# Restart Home Assistant
Switch Back to Development
For bind mount setup:
# Add the bind mount back to your Docker configuration
# Recreate/restart the container
docker restart homeassistant
For symlink setup:
# Remove HACS version
rm -rf /path/to/homeassistant/config/custom_components/spotify_voice_assistant
# Recreate symlink
ln -s /path/to/your/spotify-voice-assistant/custom_components/spotify_voice_assistant \
/path/to/homeassistant/config/custom_components/spotify_voice_assistant
# Restart Home Assistant
Testing Checklist
Before each release:
- Test artist search with exact match (e.g., "Coldplay")
- Test artist search with first result fallback
- Test album search
- Test track search
- Test playlist search (user playlists and public)
- Test queue functionality (play vs queue modes)
- Test shuffle controls (on/off)
- Test via voice commands through Extended OpenAI Conversation
- Test via Developer Tools service call
- Test clear_cache service
- Check debug logs for errors
- Verify caching is working (check debug logs for "Using cached Spotify client")
- Verify version in manifest.json is updated
- Update README if features changed
File Structure
spotify-voice-assistant/
├── custom_components/
│ └── spotify_voice_assistant/
│ ├── __init__.py # Main integration code
│ ├── manifest.json # Version and metadata
│ └── services.yaml # Service definitions
├── examples/ # User examples
├── README.md # Main documentation
├── LICENSE # MIT License
├── .gitignore # Git ignore patterns
├── DEVELOPMENT.md # This file
└── GITHUB-SETUP.md # GitHub setup instructions
Debugging
Enable Debug Logging
In Home Assistant configuration.yaml:
logger:
default: info
logs:
custom_components.spotify_voice_assistant: debug
After adding debug logging, restart Home Assistant to apply.
Common Debug Commands
# Watch integration logs in real-time (Docker example)
docker logs -f homeassistant 2>&1 | grep spotify_voice_assistant
# Check if integration loaded successfully
docker logs homeassistant 2>&1 | grep "Setting up spotify_voice_assistant"
# Verify bind mount or symlink
# For bind mount:
docker inspect homeassistant | grep spotify_voice_assistant
# For symlink:
ls -la /path/to/homeassistant/config/custom_components/spotify_voice_assistant
# Check manifest version
cat custom_components/spotify_voice_assistant/manifest.json | grep version
# Check Python syntax without running
python3 -m py_compile custom_components/spotify_voice_assistant/__init__.py
Debug Logging Output
When working correctly, you should see:
INFO Setting up spotify_voice_assistant
INFO Setup of domain spotify_voice_assistant took 0.00 seconds
DEBUG Cache miss, performing Spotify entity lookup
DEBUG Found Spotify entity: media_player.spotify_xxx
DEBUG Spotify client type: SpotifyClient
INFO Cached Spotify client for entity: media_player.spotify_xxx
DEBUG Searching for artist: Coldplay
INFO Found Spotify artist: Coldplay (spotify:artist:4gzpq5DPGxSnKTe4SA8HAU) - exact match
Subsequent searches should show:
DEBUG Using cached Spotify client # <-- Caching is working!
Test Service Call
Via Developer Tools > Services:
service: spotify_voice_assistant.search
data:
query: "Coldplay"
type: "artist"
Expected response:
{
"uri": "spotify:artist:4gzpq5DPGxSnKTe4SA8HAU",
"name": "Coldplay",
"type": "artist"
}
Test Clear Cache Service
service: spotify_voice_assistant.clear_cache
Expected response:
{
"success": true,
"message": "Cache cleared"
}
Performance Notes
The v1.0.0 release includes client caching that provides 15-50x performance improvement:
- Without cache: ~150-500ms per search (full entity lookup)
- With cache: ~3-10ms per search (cached client reuse)
Cache automatically invalidates if the Spotify integration is reloaded or removed.
Version Numbering
Follow semantic versioning:
- v1.0.x - Bug fixes only
- v1.x.0 - New features (config options, additional search types, etc.)
- v2.0.0 - Breaking changes (API changes, config changes, etc.)
Release Process
When making improvements:
- Make changes and test thoroughly with checklist above
- Update version in
manifest.json - Commit with descriptive message (no AI attribution)
- Push to main branch
- Create GitHub release with tag matching manifest version
- HACS users will be notified automatically
Example:
# Update manifest.json version to 1.1.0
git add custom_components/spotify_voice_assistant/manifest.json
git commit -m "Add support for podcast search"
git push
# Create release on GitHub
gh release create v1.1.0 --title "v1.1.0 - Podcast Search Support" \
--notes "Added support for searching Spotify podcasts"
Backup Strategy
Before making major changes, create a backup:
# Backup your development repository
tar -czf spotify-voice-assistant-backup-$(date +%Y%m%d).tar.gz \
custom_components/
# Optionally backup your HA configuration
cp /path/to/homeassistant/config/configuration.yaml \
configuration-backup-$(date +%Y%m%d).yaml
Common Issues
Integration Not Loading
Symptom: No "Setting up spotify_voice_assistant" in logs
Check:
- Verify bind mount or symlink is correct
- Check
configuration.yamlhasspotify_voice_assistant:entry - Ensure files are readable by HA container user
- Look for Python syntax errors in logs
Docker bind mount check:
docker inspect homeassistant | grep -A2 spotify_voice_assistant
Symlink Not Working in Docker
Symptom: Integration loads but shows errors about missing files
Solution: Symlinks must point to paths accessible from inside the container. Use bind mounts instead (see setup section above).
Changes Not Reflecting
Symptom: Made changes but HA still runs old code
Solution:
- Ensure you're editing the correct files (check bind mount/symlink target)
- Restart HA after Python code changes
- Check for cached
.pycfiles in__pycache__/and clear if needed - Verify bind mount is not read-only when testing (remove
:rotemporarily)
Code Quality
Before Committing
# Check Python syntax
python3 -m py_compile custom_components/spotify_voice_assistant/__init__.py
# Check for common issues
grep -n "TODO\|FIXME\|XXX" custom_components/spotify_voice_assistant/*.py
# Ensure no debug print statements
grep -n "print(" custom_components/spotify_voice_assistant/*.py
Code Standards
- Use lazy logging:
_LOGGER.debug("Text: %s", var)not_LOGGER.debug(f"Text: {var}") - Use defensive programming: check attributes with
hasattr()before accessing - Validate all user inputs
- Handle specific exceptions before generic ones
- Keep functions focused and under 50 lines when possible