diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..198404b --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,30 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/python +{ + "name": "Python 3", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bookworm", + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance", + "ms-python.black-formatter", + "ms-python.isort", + "ms-python.flake8", + "eamodio.gitlens" + ] + } + }, + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "pip3 install --user -r requirements.txt", + "postCreateCommand": "virtualenv venv && ./venv/bin/pip install -r requirements-dev.txt && ./venv/bin/pre-commit install" + // Configure tool-specific properties. + // "customizations": {}, + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.flake8 b/.flake8 index d68af71..d373378 100644 --- a/.flake8 +++ b/.flake8 @@ -19,6 +19,7 @@ exclude = docker-compose.yaml, frontend, *.md, + */*.md, *.txt extend-select = diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c8e920d..079900f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,6 +34,7 @@ repos: - id: check-yaml - id: check-json + exclude: ^.devcontainer/devcontainer.json$ - repo: https://github.com/igorshubovych/markdownlint-cli rev: v0.39.0 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..14137f2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,91 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug SunPower Integration", + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/testing/debug_runner.py", + "console": "integratedTerminal", + "envFile": "${workspaceFolder}/.env", + "env": { + "PYTHONPATH": "${workspaceFolder}:${workspaceFolder}/custom_components" + }, + "cwd": "${workspaceFolder}", + "justMyCode": false, + "stopOnEntry": false + }, + { + "name": "Test with Sample Data", + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/testing/test_with_sample_data.py", + "console": "integratedTerminal", + "env": { + "PYTHONPATH": "${workspaceFolder}:${workspaceFolder}/custom_components" + }, + "cwd": "${workspaceFolder}", + "justMyCode": false, + "stopOnEntry": false + }, + { + "name": "Debug HA Integration", + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/testing/test_ha_integration.py", + "console": "integratedTerminal", + "envFile": "${workspaceFolder}/.env", + "env": { + "PYTHONPATH": "${workspaceFolder}:${workspaceFolder}/custom_components" + }, + "cwd": "${workspaceFolder}", + "justMyCode": false, + "stopOnEntry": false + }, + { + "name": "Debug SunPower API Client", + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/testing/test_api.py", + "console": "integratedTerminal", + "envFile": "${workspaceFolder}/.env", + "env": { + "PYTHONPATH": "${workspaceFolder}:${workspaceFolder}/custom_components" + }, + "cwd": "${workspaceFolder}", + "justMyCode": false, + "stopOnEntry": false + }, + { + "name": "Debug Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "envFile": "${workspaceFolder}/.env", + "env": { + "PYTHONPATH": "${workspaceFolder}:${workspaceFolder}/custom_components" + }, + "cwd": "${workspaceFolder}", + "justMyCode": false + }, + { + "name": "Run Tests", + "type": "debugpy", + "request": "launch", + "module": "pytest", + "args": [ + "tests/", + "-v", + "--tb=short" + ], + "console": "integratedTerminal", + "envFile": "${workspaceFolder}/.env", + "env": { + "PYTHONPATH": "${workspaceFolder}:${workspaceFolder}/custom_components" + }, + "cwd": "${workspaceFolder}", + "justMyCode": false + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ed72745 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,47 @@ +{ + "python.defaultInterpreterPath": "./venv/bin/python", + "python.terminal.activateEnvironment": true, + "python.linting.enabled": true, + "python.linting.flake8Enabled": true, + "python.linting.pylintEnabled": false, + "python.formatting.provider": "black", + "python.formatting.blackArgs": [ + "--line-length", + "99" + ], + "python.sortImports.args": [ + "--profile", + "black" + ], + "editor.insertSpaces": true, + "editor.tabSize": 2, + "editor.formatOnSave": true, + "editor.detectIndentation": false, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + }, + "files.exclude": { + "**/__pycache__": true, + "**/*.pyc": true, + ".pytest_cache": true, + ".coverage": true, + "htmlcov": true + }, + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + } + }, + "python.testing.pytestEnabled": true, + "python.testing.unittestEnabled": false, + "python.testing.pytestArgs": [ + "tests" + ], + "python.analysis.typeCheckingMode": "basic", + "python.analysis.autoImportCompletions": true, + "python.analysis.extraPaths": [ + "./custom_components" + ] +} diff --git a/DEVELOPMENT_SETUP.md b/DEVELOPMENT_SETUP.md new file mode 100644 index 0000000..61a802c --- /dev/null +++ b/DEVELOPMENT_SETUP.md @@ -0,0 +1,381 @@ +# SunPower Integration Development Setup + +This guide will help you set up a complete development environment +for the SunPower Home Assistant integration, including debugging capabilities and import stepping. + +## ๐Ÿ—๏ธ **Project Overview** + +This is a **Home Assistant custom integration** for monitoring SunPower +solar systems via local PVS (Photovoltaic Supervisor) interface. The +integration provides real-time data for: + +- Solar panel production (per-panel data) +- Power consumption and grid interaction +- Battery storage systems (SunVault) +- System health and diagnostics + +## ๐Ÿ“‹ **Prerequisites** + +- **Python 3.12+** (tested with 3.12.11, 3.13.5) +- **VS Code** (recommended) or PyCharm +- **Git** for version control +- Access to a **SunPower PVS system** (for testing) + +## ๐Ÿš€ **Quick Setup** + +### 1. **Clone and Enter Project** + +```bash +cd /path/to/your/projects +git clone https://github.com/krbaker/hass-sunpower.git +cd hass-sunpower +``` + +### 3. **Configure Environment** + +```bash +cp env.example .env +# Edit .env with your PVS IP address +``` + +## ๐Ÿ”ง **VS Code Setup** + +The project includes pre-configured VS Code settings for optimal development. +The devcontainer should setup all of the following: + +### **Python Virtual Environment (DEVCONTAINER DOES THIS FOR YOU)** + +```bash +python3 -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate +``` + +### 3. **Install Dependencies (DEVCONTAINER DOES THIS FOR YOU)** + +```bash +pip install --upgrade pip +pip install -r requirements-dev.txt +``` + +### 2. **Setup Pre-commit Hooks (DEVCONTAINER DOES THIS FOR YOU)** + +```bash +pre-commit install +``` + +### **Extensions:** + +- Python (ms-python.python) +- Pylance (ms-python.vscode-pylance) +- Black Formatter (ms-python.black-formatter) +- isort (ms-python.isort) +- flake8 (ms-python.flake8) +- GitLens (eamodio.gitlens) + +### **Key Features Configured:** + +- โœ… **Auto-formatting** with Black (line length 99) +- โœ… **Import sorting** with isort +- โœ… **Linting** with Flake8 +- โœ… **Type checking** with Pylance +- โœ… **Auto-save formatting** +- โœ… **Debugging configurations** + +## ๐Ÿ› **Debugging Setup** + +### **Debug Configurations Available:** + +1. **Debug SunPower Integration** - Test core functionality (no HA required) +2. **Test with Sample Data** - Test integration logic with sample data +3. **Debug HA Integration** - Test Home Assistant coordinator setup +4. **Debug SunPower API Client** - Test just the API client +5. **Debug Current File** - Debug any Python file +6. **Run Tests** - Debug test cases + +### **Setting Breakpoints:** + +1. **Open any Python file** in the integration +2. **Click in the gutter** (left of line numbers) to set breakpoints +3. **Press F5** or use Debug menu to start debugging +4. **Step through code** with F10 (step over), F11 (step into) + +### **Debug Scripts:** + +#### **Test API Connection:** + +```bash +# Set your PVS IP +export PVS_HOST=192.168.1.100 # Replace with your PVS IP +python test_api.py +``` + +Example Output + +```bash +โžœ hass-sunpower git:(main) โœ— ./venv/bin/python3 test_api.py +Testing SunPower API connection to young-ave.dynamic-dns.net:8081 +-------------------------------------------------- +1. Testing network status... +โœ… Network status successful +{'networkstatus': {'interfaces': [{'interface': 'wan', + 'internet': 'down', + 'ipaddr': '', + 'link': 'disconnected', + 'mode': 'wan', + 'sms': 'unreachable', + 'state': 'down'}, + {'interface': 'plc', + 'internet': 'down', + 'ipaddr': '', + 'link': 'disconnected', + 'pairing': 'unpaired', + 'sms': 'unreachable', + 'speed': 0, + 'state': 'down'}, + {'interface': 'sta0', + 'internet': 'up', + 'ipaddr': '192.168.1.25', + 'signal': '-80', + 'sms': 'reachable', + 'ssid': "XYZ's Network", + 'status': 'connected'}, + {'interface': 'cell', + 'internet': 'down', + 'ipaddr': '', + 'is_alwayson': False, + 'is_primary': False, + 'link': 'disconnected', + 'modem': 'MODEM_OK', + 'provider': 'UNKNOWN', + 'signal': 0, + 'sim': 'SIM_READY', + 'sms': 'unreachable', + 'state': 'DOWN', + 'status': 'NOT_REGISTERED'}], + 'system': {'interface': 'sta0', + 'interface_name': 'sta0', + 'internet': 'up', + 'sms': 'reachable'}, + 'ts': '1756569304'}, + 'result': 'succeed'} + +2. Testing device list... +โœ… Device list successful +Found 30 devices +Device breakdown: + - PVS: 1 + - Power Meter: 2 + - Inverter: 27 + +3. Testing energy storage system status... +โœ… ESS status successful +{'result': 'Make sure you have run discovery to successful completion'} +``` + +#### **Test with Sample Data (No PVS Required):** + +```bash +python test_with_sample_data.py +``` + +#### **Debug Integration Logic:** + +```bash +export PVS_HOST=192.168.1.100 +python debug_runner.py +``` + +#### **Test Home Assistant Integration:** + +```bash +export PVS_HOST=192.168.1.100 +python test_ha_integration.py +``` + +## ๐Ÿงช **Testing** + +### **Run All Tests:** + +```bash +pytest tests/ -v +``` + +### **Run Specific Test:** + +```bash +pytest tests/test_sunpower_api.py::TestSunPowerMonitor::test_init -v +``` + +### **Run Tests with Coverage:** + +```bash +pytest tests/ --cov=custom_components/sunpower --cov-report=html +``` + +## ๐Ÿ” **Code Quality Tools** + +### **Manual Code Checks:** + +```bash +# Format code +black custom_components/ tests/ *.py + +# Sort imports +isort custom_components/ tests/ *.py + +# Lint code +flake8 custom_components/ tests/ *.py + +# Type checking +mypy custom_components/sunpower/ +``` + +### **Pre-commit (Automatic):** + +Code quality checks run automatically on commit. To run manually: + +```bash +pre-commit run --all-files +``` + +## ๐Ÿ“ **Key Files for Development** + +### **Core Integration Files:** + +- `custom_components/sunpower/__init__.py` - Integration entry point +- `custom_components/sunpower/sunpower.py` - API client +- `custom_components/sunpower/const.py` - Constants and sensor definitions +- `custom_components/sunpower/config_flow.py` - UI configuration +- `custom_components/sunpower/sensor.py` - Sensor entities +- `custom_components/sunpower/entity.py` - Base entity class + +### **Development Files:** + +- `test_api.py` - Test PVS API connection +- `debug_runner.py` - Debug full integration +- `requirements-dev.txt` - Development dependencies +- `.vscode/launch.json` - Debug configurations +- `tests/` - Test suite + +## ๐ŸŒ **Network Setup for Testing** + +The integration connects to the PVS management interface: + +### **Common PVS IP Addresses:** + +- **NAT setup**: `172.27.153.1` (most common) +- **Direct connection**: Your PVS's actual IP +- **Router assignment**: Check your router's DHCP clients + +### **Testing Connectivity:** + +```bash +# Test if PVS is reachable +curl "http://172.27.153.1/cgi-bin/dl_cgi?Command=Get_Comm" + +# Or use our test script +export PVS_HOST=172.27.153.1 +python test_api.py +``` + +## ๐Ÿšจ **Important Notes** + +### **PVS Management Interface:** + +- โš ๏ธ **DO NOT** plug the PVS management port directly into your LAN +- It runs its own DHCP server and will cause network issues +- Use a separate network interface or NAT setup + +### **Development Safety:** + +- Use longer polling intervals (120s+) to avoid overwhelming the PVS +- The PVS API is slow and can timeout - be patient +- Monitor PVS logs for any issues during development + +## ๐Ÿ”ง **Stepping Through Imports** + +### **Debug Import Issues:** + +1. **Set breakpoint** in `__init__.py` at the import statements +2. **Start debug session** "Debug SunPower Integration" +3. **Step through** (F11) each import to see what's loaded +4. **Check sys.path** in debug console: `sys.path` +5. **Verify module loading** in debug console: `import custom_components.sunpower.const` + +### **Import Path Configuration:** + +```python +# VS Code settings already include: +"python.analysis.extraPaths": ["./custom_components"] + +# Debug scripts add: +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'custom_components')) +``` + +## ๐Ÿ“ **Common Development Tasks** + +### **Adding a New Sensor:** + +1. **Add sensor definition** in `const.py` +2. **Test data availability** in sample JSON +3. **Add unit tests** in `tests/` +4. **Test with real PVS** using debug scripts + +### **Debugging Connection Issues:** + +1. **Check network connectivity**: `python test_api.py` +2. **Verify PVS responds**: `curl http:///cgi-bin/dl_cgi?Command=Get_Comm` +3. **Debug data parsing**: Set breakpoints in `sunpower_fetch()` +4. **Check coordinator updates**: Debug in `async_update_data()` + +### **Adding Multi-Account Support:** + +The integration supports multiple PVS systems: + +1. **Use unique names** in config flow +2. **Test entry_id isolation** in debug scripts +3. **Verify device/entity separation** in Home Assistant + +## ๐Ÿ†˜ **Troubleshooting** + +### **Import Errors:** + +```bash +# Check Python path +python -c "import sys; print('\n'.join(sys.path))" + +# Verify module structure +find custom_components/ -name "*.py" | head -10 +``` + +### **VS Code Issues:** + +- **Restart Python interpreter**: Cmd+Shift+P โ†’ "Python: Restart Language Server" +- **Check interpreter**: Cmd+Shift+P โ†’ "Python: Select Interpreter" โ†’ Choose `./venv/bin/python` +- **Reload window**: Cmd+Shift+P โ†’ "Developer: Reload Window" + +### **Debug Not Working:** + +1. **Check .env file** exists with PVS_HOST +2. **Verify PVS connectivity** with `test_api.py` +3. **Check Python interpreter** points to `./venv/bin/python` +4. **Look at debug console** for error messages + +## ๐ŸŽฏ **Next Steps** + +1. **Test with your PVS**: Update `.env` with your PVS IP +2. **Run debug script**: `python debug_runner.py` +3. **Set breakpoints**: Try debugging the data flow +4. **Add tests**: Create tests for any new features +5. **Submit PRs**: Follow the project's contribution guidelines + +--- + +## ๐Ÿ“š **Additional Resources** + +- **Home Assistant Developer Docs**: +- **SunPower Integration Issues**: +- **Home Assistant Discord**: #devs_custom_components +- **Python Debugging Guide**: + +Happy coding! ๐Ÿš€ diff --git a/custom_components/sunpower/sunpower.py b/custom_components/sunpower/sunpower.py index 7db42bc..9fb5a6a 100644 --- a/custom_components/sunpower/sunpower.py +++ b/custom_components/sunpower/sunpower.py @@ -1,4 +1,4 @@ -""" Basic Sunpower PVS Tool """ +"""Basic Sunpower PVS Tool""" import requests import simplejson @@ -25,12 +25,13 @@ def __init__(self, host): def generic_command(self, command): """All 'commands' to the PVS module use this url pattern and return json - The PVS system can take a very long time to respond so timeout is at 2 minutes""" + The PVS system can take a very long time to respond so timeout is at 2 minutes + """ try: return requests.get(self.command_url + command, timeout=120).json() except requests.exceptions.RequestException as error: raise ConnectionException from error - except simplejson.errors.JSONDecodeError as error: + except simplejson.JSONDecodeError as error: raise ParseException from error def device_list(self): @@ -41,12 +42,14 @@ def energy_storage_system_status(self): """Get the status of the energy storage system""" try: return requests.get( - "http://{0}/cgi-bin/dl_cgi/energy-storage-system/status".format(self.host), + "http://{0}/cgi-bin/dl_cgi/energy-storage-system/status".format( + self.host, + ), timeout=120, ).json() except requests.exceptions.RequestException as error: raise ConnectionException from error - except simplejson.errors.JSONDecodeError as error: + except simplejson.JSONDecodeError as error: raise ParseException from error def network_status(self): diff --git a/env.example b/env.example new file mode 100644 index 0000000..7f502d2 --- /dev/null +++ b/env.example @@ -0,0 +1,18 @@ +# SunPower PVS Configuration +# Copy this file to .env and update with your settings + +# PVS Host IP Address +# This is typically 172.27.153.1 for NAT setup or your PVS's actual IP +PVS_HOST=172.27.153.1 + +# Optional: Custom name for your installation +PVS_NAME="My Solar System" + +# Debug logging level (DEBUG, INFO, WARNING, ERROR) +LOG_LEVEL=DEBUG + +# Home Assistant configuration (if testing with HA) +HA_CONFIG_DIR=/config + +# Test configuration +PYTEST_TIMEOUT=120 diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..39ddb75 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,8 @@ +[pytest] + +markers = + pvs: Tests that work directly against a pvs to verify its working correctly (not if PVS env == MOCK this will test the Mock) + hass: Tests that validate home assistant functionality + +env = + PVS=MOCK diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..1b84d43 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,24 @@ + +# Development dependencies +black>=24.3.0 +flake8>=7.0.0 +flake8-bugbear>=24.2.6 +flake8-pytest>=1.4 +flake8-quotes>=3.4.0 +# Core dependencies +homeassistant>=2025.1.0 +isort>=5.13.2 + +# Type checking +mypy>=1.0.0 +pre-commit>=3.0.0 + +# Testing +pytest>=7.0.0 +pytest-cov +pytest-env +pytest-homeassistant-custom-component +pythonping +requests +simplejson +types-requests diff --git a/samples/device_list.json b/samples/device_list.json deleted file mode 100644 index c249cd6..0000000 --- a/samples/device_list.json +++ /dev/null @@ -1,728 +0,0 @@ -{ - "devices": [{ - "DETAIL": "detail", - "STATE": "working", - "STATEDESCR": "Working", - "SERIAL": "ZT204485000549A0321", - "MODEL": "PV Supervisor PVS6", - "HWVER": "6.02", - "SWVER": "2024.2, Build 61640", - "DEVICE_TYPE": "PVS", - "DATATIME": "2024,04,16,23,45,00", - "dl_err_count": "0", - "dl_comm_err": "400", - "dl_skipped_scans": "0", - "dl_scan_time": "1", - "dl_untransmitted": "2487", - "dl_uptime": "99696", - "dl_cpu_load": "0.33", - "dl_mem_used": "86920", - "dl_flash_avail": "104808", - "panid": 3276331684, - "CURTIME": "2024,04,16,23,46,10" - }, { - "ISDETAIL": true, - "SERIAL": "PVS6M20440321p", - "TYPE": "PVS5-METER-P", - "STATE": "working", - "STATEDESCR": "Working", - "MODEL": "PVS6M0400p", - "DESCR": "Power Meter PVS6M20440321p", - "DEVICE_TYPE": "Power Meter", - "interface": "mime", - "production_subtype_enum": "GROSS_PRODUCTION_SITE", - "subtype": "GROSS_PRODUCTION_SITE", - "SWVER": "3000", - "PORT": "", - "DATATIME": "2024,04,16,23,46,10", - "ct_scl_fctr": "50", - "net_ltea_3phsum_kwh": "19327.99", - "p_3phsum_kw": "0", - "q_3phsum_kvar": "0.4392", - "s_3phsum_kva": "0.4492", - "tot_pf_rto": "0", - "freq_hz": "60", - "i_a": "1.8457", - "v12_v": "243.3916", - "CAL0": "50", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,11" - }, { - "ISDETAIL": true, - "SERIAL": "PVS6M20440321c", - "TYPE": "PVS5-METER-C", - "STATE": "working", - "STATEDESCR": "Working", - "MODEL": "PVS6M0400c", - "DESCR": "Power Meter PVS6M20440321c", - "DEVICE_TYPE": "Power Meter", - "interface": "mime", - "consumption_subtype_enum": "GROSS_CONSUMPTION_LINESIDE", - "subtype": "GROSS_CONSUMPTION_LINESIDE", - "SWVER": "3000", - "PORT": "", - "DATATIME": "2024,04,16,23,46,10", - "ct_scl_fctr": "100", - "net_ltea_3phsum_kwh": "27897.1999", - "p_3phsum_kw": "1.3493", - "q_3phsum_kvar": "0.0311", - "s_3phsum_kva": "1.5168", - "tot_pf_rto": "0.8839", - "freq_hz": "60", - "i1_a": "2.2421", - "i2_a": "10.2358", - "v1n_v": "121.9041", - "v2n_v": "121.4879", - "v12_v": "243.3916", - "p1_kw": "0.1869", - "p2_kw": "1.1624", - "neg_ltea_3phsum_kwh": "2.91", - "pos_ltea_3phsum_kwh": "27900.1399", - "CAL0": "100", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,11" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040011392", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040011392", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,28", - "ltea_3phsum_kwh": "1328.2679", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.38", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0006", - "v_mppt1_v": "21.97", - "i_mppt1_a": "0.02", - "t_htsnk_degc": "18", - "freq_hz": "60", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,11" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040012744", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040012744", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,28", - "ltea_3phsum_kwh": "1391.1325", - "p_3phsum_kw": "0", - "vln_3phavg_v": "244.18", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0006", - "v_mppt1_v": "21.94", - "i_mppt1_a": "0.02", - "t_htsnk_degc": "18", - "freq_hz": "60", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,11" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040013383", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040013383", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,28", - "ltea_3phsum_kwh": "1376.6939", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.82", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0003", - "v_mppt1_v": "21.99", - "i_mppt1_a": "0.01", - "t_htsnk_degc": "15", - "freq_hz": "60", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,12" - }, { - "ISDETAIL": true, - "SERIAL": "E002020400144528", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040014528", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,28", - "ltea_3phsum_kwh": "1325.1421", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.24", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0006", - "v_mppt1_v": "21.98", - "i_mppt1_a": "0.02", - "t_htsnk_degc": "15", - "freq_hz": "60", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,12" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040016250", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040016250", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,28", - "ltea_3phsum_kwh": "1437.9023", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.78", - "i_3phsum_a": "0", - "p_mppt1_kw": "0", - "v_mppt1_v": "21.98", - "i_mppt1_a": "0", - "t_htsnk_degc": "15", - "freq_hz": "60", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,12" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040016476", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040016476", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,28", - "ltea_3phsum_kwh": "1366.0335", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.71", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0004", - "v_mppt1_v": "21.98", - "i_mppt1_a": "0.02", - "t_htsnk_degc": "18", - "freq_hz": "60", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,12" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040016406", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040016406", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,28", - "ltea_3phsum_kwh": "1380.201", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.76", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0003", - "v_mppt1_v": "21.97", - "i_mppt1_a": "0.01", - "t_htsnk_degc": "18", - "freq_hz": "60", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,12" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040016736", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040016736", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,28", - "ltea_3phsum_kwh": "1239.002", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.47", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0017", - "v_mppt1_v": "21.97", - "i_mppt1_a": "0.07", - "t_htsnk_degc": "15", - "freq_hz": "60", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,13" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040016767", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040016767", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,28", - "ltea_3phsum_kwh": "1128.181", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.64", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0003", - "v_mppt1_v": "21.99", - "i_mppt1_a": "0.01", - "t_htsnk_degc": "13", - "freq_hz": "60", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,13" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040016936", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040016936", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,28", - "ltea_3phsum_kwh": "1163.1473", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.6", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0003", - "v_mppt1_v": "21.98", - "i_mppt1_a": "0.01", - "t_htsnk_degc": "14", - "freq_hz": "60", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,13" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040017146", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040017146", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,28", - "ltea_3phsum_kwh": "1402.129", - "p_3phsum_kw": "0", - "vln_3phavg_v": "244.07", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0004", - "v_mppt1_v": "21.98", - "i_mppt1_a": "0.01", - "t_htsnk_degc": "15", - "freq_hz": "60.01", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,13" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040017779", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040017779", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,28", - "ltea_3phsum_kwh": "1341.3654", - "p_3phsum_kw": "0", - "vln_3phavg_v": "219.62", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0004", - "v_mppt1_v": "22.01", - "i_mppt1_a": "0.02", - "t_htsnk_degc": "19", - "freq_hz": "60", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,13" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040019106", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040019106", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,28", - "ltea_3phsum_kwh": "1207.0696", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.53", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0004", - "v_mppt1_v": "21.96", - "i_mppt1_a": "0.02", - "t_htsnk_degc": "14", - "freq_hz": "60", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,14" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040019210", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040019210", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,28", - "ltea_3phsum_kwh": "1211.5855", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.63", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0002", - "v_mppt1_v": "21.96", - "i_mppt1_a": "0.01", - "t_htsnk_degc": "16", - "freq_hz": "60.01", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,14" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040019664", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040019664", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,43", - "ltea_3phsum_kwh": "1376.5009", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.41", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0005", - "v_mppt1_v": "21.99", - "i_mppt1_a": "0.02", - "t_htsnk_degc": "18", - "freq_hz": "60.01", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,14" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040019050", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040019050", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,43", - "ltea_3phsum_kwh": "1408.4185", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.74", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0009", - "v_mppt1_v": "21.96", - "i_mppt1_a": "0.04", - "t_htsnk_degc": "18", - "freq_hz": "60.01", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,14" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040019099", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040019099", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,43", - "ltea_3phsum_kwh": "1432.2282", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.68", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0006", - "v_mppt1_v": "21.97", - "i_mppt1_a": "0.02", - "t_htsnk_degc": "18", - "freq_hz": "60.01", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,14" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040020435", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040020435", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,43", - "ltea_3phsum_kwh": "1427.6486", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.2", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0008", - "v_mppt1_v": "21.97", - "i_mppt1_a": "0.03", - "t_htsnk_degc": "17", - "freq_hz": "60.01", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,15" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040020589", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040020589", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,43", - "ltea_3phsum_kwh": "1293.9053", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.65", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0003", - "v_mppt1_v": "21.95", - "i_mppt1_a": "0.01", - "t_htsnk_degc": "16", - "freq_hz": "60.01", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,15" - }, { - "ISDETAIL": true, - "SERIAL": "E00202040020677", - "TYPE": "SOLARBRIDGE", - "STATE": "error", - "STATEDESCR": "Error", - "MODEL": "AC_Module_Type_E", - "DESCR": "Inverter E00202040020677", - "DEVICE_TYPE": "Inverter", - "hw_version": "4405", - "interface": "mime", - "module_serial": "", - "PANEL": "SPR-X22-360-E-AC", - "slave": 0, - "SWVER": "4.40.1", - "PORT": "", - "MOD_SN": "", - "NMPLT_SKU": "", - "DATATIME": "2024,04,16,23,27,43", - "ltea_3phsum_kwh": "970.7499", - "p_3phsum_kw": "0", - "vln_3phavg_v": "243.16", - "i_3phsum_a": "0", - "p_mppt1_kw": "0.0006", - "v_mppt1_v": "21.98", - "i_mppt1_a": "0.02", - "t_htsnk_degc": "14", - "freq_hz": "60.01", - "stat_ind": "0", - "origin": "data_logger", - "OPERATION": "noop", - "CURTIME": "2024,04,16,23,46,15" - }], - "result": "succeed" -} diff --git a/tests/DEBUG_TOOLS_GUIDE.md b/tests/DEBUG_TOOLS_GUIDE.md new file mode 100644 index 0000000..8a99ad5 --- /dev/null +++ b/tests/DEBUG_TOOLS_GUIDE.md @@ -0,0 +1,308 @@ +# SunPower Integration Debug Tools Guide + +This document explains all the debug tools available for +developing and testing the SunPower Home Assistant integration. + +## ๐Ÿ› ๏ธ **Available Debug Tools** + +### **1. `test_api.py` - API Client Testing** + +**Purpose**: Test direct communication with PVS hardware +**Use When**: You want to verify PVS connectivity and API responses + +```bash +export PVS_HOST=192.168.1.100 # Your PVS IP +python test_api.py +``` + +**What it tests**: + +- โœ… Network connectivity to PVS +- โœ… Basic API commands (network status, device list) +- โœ… ESS functionality (if available) +- โœ… Error handling and timeouts + +**Example Output**: + +```console +Testing SunPower API connection to 192.168.1.100 +-------------------------------------------------- +1. Testing network status... +โœ… Network status successful +2. Testing device list... +โœ… Device list successful +Found 30 devices +``` + +--- + +### **2. `test_with_sample_data.py` - Integration Logic Testing** + +**Purpose**: Test integration logic without requiring real PVS hardware +**Use When**: You want to verify data processing, sensor mapping, and core logic + +```bash +python test_with_sample_data.py +``` + +**What it tests**: + +- โœ… Data conversion from PVS format to HA format +- โœ… Sensor field compatibility (which sensors will work) +- โœ… Virtual meter creation +- โœ… Device type processing +- โœ… Integration consistency + +**Example Output**: + +```console +๐Ÿงช Testing SunPower Integration with Sample Data +โœ… Data conversion successful +๐Ÿ“Š Device types found: ['PVS', 'Power Meter', 'Inverter'] +Sensor field compatibility: + - PVS: 9/9 fields available (100.0%) + - Power Meter: 8/16 fields available (50.0%) +``` + +--- + +### **3. `debug_runner.py` - Core Functionality Testing** + +**Purpose**: Test core integration functionality with real PVS data +**Use When**: You want to test the integration logic with real PVS responses + +```bash +export PVS_HOST=192.168.1.100 +python debug_runner.py +``` + +**What it tests**: + +- โœ… API client functionality +- โœ… Data conversion and processing +- โœ… Core integration functions +- โœ… Data fetch mechanisms +- โœ… Error handling and recovery + +**Example Output**: + +```console +๐Ÿ”ง Starting SunPower Integration Debug +1. Testing SunPower API client... +โœ… PVS connectivity successful +2. Testing data processing... +3. Testing data fetch function... +โœ… Data fetch function successful +``` + +--- + +### **4. `test_ha_integration.py` - Home Assistant Compatibility** + +**Purpose**: Test Home Assistant specific functionality (coordinators, etc.) +**Use When**: You want to verify HA integration components work correctly + +```bash +export PVS_HOST=192.168.1.100 +python test_ha_integration.py +``` + +**What it tests**: + +- โœ… DataUpdateCoordinator creation +- โœ… Home Assistant mock compatibility +- โœ… Integration data flow +- โœ… HA-specific error handling + +--- + +### **5. `pytest tests/` - Unit Testing** + +**Purpose**: Run automated unit tests +**Use When**: You want to verify code changes don't break existing functionality + +```bash +pytest tests/ -v +``` + +**What it tests**: + +- โœ… API client unit tests +- โœ… Mock response handling +- โœ… Error condition testing +- โœ… Edge case validation + +--- + +## ๐ŸŽฏ **Testing Workflow Recommendations** + +### **For New Development** + +1. **Start with**: `test_with_sample_data.py` (no hardware needed) +2. **Then test**: `debug_runner.py` (with real PVS) +3. **Finally verify**: `pytest tests/` (automated validation) + +### **For Bug Investigation** + +1. **Check connectivity**: `test_api.py` +2. **Verify logic**: `debug_runner.py` +3. **Test HA integration**: `test_ha_integration.py` + +### **For Performance Testing** + +1. **Use**: `debug_runner.py` with different update intervals +2. **Monitor**: PVS response times and error rates +3. **Validate**: Sample data processing speed + +--- + +## ๐Ÿ› **VS Code Debug Configurations** + +All scripts are available as VS Code debug configurations: + +| Configuration Name | Script | Purpose | +|-------------------|---------|---------| +| **Test with Sample Data** | `test_with_sample_data.py` | Test logic without hardware | +| **Debug SunPower Integration** | `debug_runner.py` | Test with real PVS | +| **Debug HA Integration** | `test_ha_integration.py` | Test HA compatibility | +| **Debug SunPower API Client** | `test_api.py` | Test API connectivity | + +**To use**: + +1. Press `F5` in VS Code +2. Select desired configuration +3. Set breakpoints anywhere in the code +4. Step through imports and execution + +--- + +## ๐Ÿ” **Debugging Import Issues** + +### **Step Through Import Resolution** + +1. Set breakpoint at top of any integration file +2. Use "Debug SunPower Integration" configuration +3. Step into (`F11`) import statements +4. Watch variables panel for `sys.path` and module loading + +### **Check Python Path** + +```python +# In debug console: +import sys +print('\n'.join(sys.path)) +``` + +### **Verify Module Loading** + +```python +# In debug console: +import custom_components.sunpower.const as const +print(dir(const)) +``` + +--- + +## ๐Ÿ“Š **Output Interpretation** + +### **Success Indicators** + +- โœ… Green checkmarks +- ๐Ÿ“Š Data statistics (device counts, field availability) +- ๐Ÿ“ˆ Performance metrics +- ๐ŸŽ‰ Completion messages + +### **Warning Indicators** + +- โš ๏ธ Yellow warnings (expected issues) +- โ„น๏ธ Informational messages +- ๐Ÿ’ก Helpful suggestions + +### **Error Indicators** + +- โŒ Red X marks +- ๐Ÿ” Troubleshooting sections +- Detailed error tracebacks + +--- + +## ๐Ÿ”ง **Troubleshooting Common Issues** + +### **"No module named 'sunpower'"** + +- Check Python path configuration +- Verify VS Code interpreter points to `./venv/bin/python` +- Restart VS Code language server + +### **Connection Timeouts** + +- Verify PVS IP address in `PVS_HOST` +- Check network connectivity: `curl http:///cgi-bin/dl_cgi?Command=Get_Comm` +- Try different timeout values + +### **Import Errors in Debug** + +- Use "Test with Sample Data" first (no external dependencies) +- Check that virtual environment is activated +- Verify all dependencies installed: `pip install -r requirements-dev.txt` + +--- + +## ๐Ÿš€ **Advanced Debugging Techniques** + +### **Custom Data Testing** + +1. Modify `samples/device_list.json` with your PVS data +2. Run `test_with_sample_data.py` to validate compatibility +3. Test edge cases with missing or malformed data + +### **Performance Profiling** + +```bash +python -m cProfile -o profile_stats debug_runner.py +``` + +### **Memory Usage Monitoring** + +```python +# Add to debug scripts: +import tracemalloc +tracemalloc.start() +# ... run tests ... +current, peak = tracemalloc.get_traced_memory() +print(f"Memory: {current / 1024 / 1024:.1f} MB") +``` + +--- + +## ๐Ÿ“š **Integration with Development Workflow** + +### **Before Committing Code** + +```bash +# 1. Run all tests +pytest tests/ -v + +# 2. Test with sample data +python test_with_sample_data.py + +# 3. Format code (automatic with pre-commit) +black custom_components/ tests/ *.py +``` + +### **Before Releasing** + +```bash +# 1. Test with real PVS +export PVS_HOST=your.pvs.ip +python debug_runner.py + +# 2. Test HA integration +python test_ha_integration.py + +# 3. Run full test suite +pytest tests/ --cov=custom_components/sunpower +``` + +This comprehensive debug tool suite ensures you can develop, test, and +debug the SunPower integration efficiently at every stage of development. diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..852e012 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,150 @@ +# SunPower Integration Testing Suite + +This directory contains comprehensive testing tools +and documentation for the SunPower Home Assistant +integration. + +## ๐Ÿ“ **Tests Directory Structure** + +```console +tests/ +โ”œโ”€โ”€ samples/ +โ”‚ โ””โ”€โ”€ device_list.json # Sample PVS data for testing +โ”‚ โ””โ”€โ”€ DEBUG_TOOLS_GUIDE.md # Detailed debug tools documentation +โ”œโ”€โ”€ test_api.py # Test PVS API connectivity +โ”œโ”€โ”€ test_with_sample_data.py # Test integration logic (no hardware) +โ”œโ”€โ”€ debug_runner.py # Test core functionality +โ”œโ”€โ”€ test_ha_integration.py # Test Home Assistant compatibility +โ””โ”€โ”€ README.md # This file +``` + +## ๐Ÿš€ **Quick Start** + +### **Test Without Hardware** (Recommended first step) + +```bash +cd testing +python test_with_sample_data.py +``` + +### **Test With Your PVS** + +```bash +cd testing +export PVS_HOST=192.168.1.100 # Your PVS IP +python test_api.py +python debug_runner.py +``` + +## ๐Ÿ› ๏ธ **Testing Tools** + +| Script | Purpose | Requires PVS | +|--------|---------|-------------| +| **`test_with_sample_data.py`** | Validate integration logic with sample data | โŒ | +| **`test_api.py`** | Test PVS connectivity and API responses | โœ… | +| **`debug_runner.py`** | Test core integration functionality | โœ… | +| **`test_ha_integration.py`** | Test Home Assistant compatibility | โœ… | + +## ๐Ÿ”ง **VS Code Integration** + +All test scripts are configured as VS Code debug targets: + +1. **Open** the main project directory in VS Code +2. **Press F5** to see debug configurations +3. **Select** your desired test script +4. **Set breakpoints** and debug through the code + +## ๐Ÿ“Š **Sample Data** + +The `samples/device_list.json` contains real PVS API response data for testing: + +- **1 PVS** (Photovoltaic Supervisor) +- **3 Power Meters** (including 1 virtual meter) +- **20 Inverters** (solar panel micro-inverters) + +This allows complete testing of the integration logic without needing physical hardware. + +## ๐Ÿ“š **Documentation** + +### **`docs/DEVELOPMENT_SETUP.md`** + +Complete guide for setting up the development environment including: + +- Python environment setup +- VS Code configuration +- Debugging setup +- Network configuration + +### **`docs/DEBUG_TOOLS_GUIDE.md`** + +Detailed documentation for all debug tools including: + +- Purpose and usage of each script +- Expected output examples +- Troubleshooting common issues +- Advanced debugging techniques + +## ๐ŸŽฏ **Development Workflow** + +### **For New Features** + +1. **Start**: `python test_with_sample_data.py` (validate logic) +2. **Test**: `python debug_runner.py` (test with real PVS) +3. **Verify**: `python test_ha_integration.py` (HA compatibility) + +### **For Bug Fixes** + +1. **Isolate**: `python test_api.py` (check connectivity) +2. **Debug**: Set breakpoints in VS Code and step through +3. **Validate**: Run all tests to ensure fix doesn't break anything + +### **Before Committing** + +```bash +# From project root +pytest tests/ -v # Unit tests +cd testing +python test_with_sample_data.py # Integration logic test +``` + +## ๐Ÿ†˜ **Troubleshooting** + +### **Import Errors** + +- Ensure you're running from the `testing/` directory +- Check that virtual environment is activated +- Verify Python path includes `custom_components` + +### **PVS Connection Issues** + +- Set `PVS_HOST` environment variable +- Test connectivity: `curl http://$PVS_HOST/cgi-bin/dl_cgi?Command=Get_Comm` +- Check network configuration (see development setup guide) + +### **VS Code Debug Issues** + +- Ensure Python interpreter points to `../venv/bin/python` +- Restart VS Code language server if needed +- Check that debug configurations point to correct file paths + +## ๐Ÿ“ˆ **Adding New Tests** + +When adding new test functionality: + +1. **Follow naming convention**: `test_*.py` +2. **Add VS Code debug config** in `../.vscode/launch.json` +3. **Update documentation** in `docs/DEBUG_TOOLS_GUIDE.md` +4. **Include error handling** and helpful output messages + +## ๐Ÿ”— **Related Files** + +- **Unit Tests**: `../tests/` (pytest-based unit tests) +- **VS Code Config**: `../.vscode/` (debug configurations, settings) +- **Integration Code**: `../custom_components/sunpower/` (main integration) +- **Requirements**: `../requirements-dev.txt` (development dependencies) + +--- + +For complete setup instructions, see [`docs/DEVELOPMENT_SETUP.md`](docs/DEVELOPMENT_SETUP.md). + +For detailed tool documentation, see [`docs/DEBUG_TOOLS_GUIDE.md`](docs/DEBUG_TOOLS_GUIDE.md). diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..f019047 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +# Tests for SunPower Home Assistant Integration diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..dfc5537 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,43 @@ +import json +import os +import sys + +import pytest + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "custom_components")) + +import sunpower # noqa: E402 + +DEVICE_LIST_SAMPLE = os.path.join( + os.path.dirname(__file__), + "samples", + "ess_device_list.json", +) +DEVICE_LIST_JSON = json.load(open(DEVICE_LIST_SAMPLE)) +ESS_STATUS_SAMPLE = os.path.join( + os.path.dirname(__file__), + "samples", + "ess_status.json", +) +ESS_STATUS_JSON = json.load(open(ESS_STATUS_SAMPLE)) + + +@pytest.fixture() +def sunpowermonitor(mocker): + """SunPower Monitor fixture.""" + if os.getenv("PVS") == "MOCK": + monitor = sunpower.SunPowerMonitor(None) + mocker.patch.object(monitor, "device_list", return_value=DEVICE_LIST_JSON) + mocker.patch.object( + monitor, + "energy_storage_system_status", + return_value=ESS_STATUS_JSON, + ) + mocker.patch.object(monitor, "network_status", return_value="Something") + yield monitor + return + elif os.getenv("PVS"): + monitor = sunpower.SunPowerMonitor(os.getenv("PVS")) + yield monitor + else: + raise Exception("PVS environment variable not set") diff --git a/tests/debug_runner.py b/tests/debug_runner.py new file mode 100755 index 0000000..94e1c35 --- /dev/null +++ b/tests/debug_runner.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +""" +Debug runner for the SunPower integration. +This script simulates how Home Assistant would load and run the integration. +""" + +import asyncio +import logging +import os +import sys + +# Add the custom_components directory to Python path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "custom_components")) + +from sunpower import ( # noqa: E402 + convert_sunpower_data, + sunpower_fetch, +) +from sunpower.sunpower import SunPowerMonitor # noqa: E402 + +# Set up logging +logging.basicConfig( + level=logging.DEBUG, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", +) +logger = logging.getLogger(__name__) + + +async def debug_integration(): + """Debug the SunPower integration.""" + host = os.getenv("PVS_HOST", "172.27.153.1") + + print("๐Ÿ”ง Starting SunPower Integration Debug") + print(f"๐Ÿ“ก PVS Host: {host}") + print("-" * 50) + + try: + print("1. Testing SunPower API client...") + monitor = SunPowerMonitor(host) + + # Test basic connectivity + network_status = monitor.network_status() + print("โœ… PVS connectivity successful") + interface_count = len( + network_status.get("networkstatus", {}).get("interfaces", []), + ) + print(f"๐Ÿ“Š Network interfaces found: {interface_count}") + + # Test data fetching + device_data = monitor.device_list() + print(f"โœ… Retrieved {len(device_data.get('devices', []))} devices") + + # Test data conversion + converted_data = convert_sunpower_data(device_data) + print("โœ… Data conversion successful") + print(f"๐Ÿ“Š Device types found: {list(converted_data.keys())}") + + print("\n2. Testing data processing...") + + # Show device breakdown + for device_type, devices in converted_data.items(): + print(f" - {device_type}: {len(devices)} devices") + if devices: + sample_device = next(iter(devices.values())) + sample_fields = list(sample_device.keys())[:8] # First 8 fields + print(f" Sample fields: {sample_fields}") + + print("\n3. Testing data fetch function...") + + # Test the sunpower_fetch function directly + entry_id = "test_entry_123" + fetch_data = sunpower_fetch(monitor, 120, 60, entry_id) + + if fetch_data: + print("โœ… Data fetch function successful") + print(f"๐Ÿ“Š Fetched data keys: {list(fetch_data.keys())}") + + # Show data summary + total_devices = sum(len(devices) for devices in fetch_data.values()) + print(f"๐Ÿ“ˆ Total devices processed: {total_devices}") + + print("\n4. Testing ESS functionality...") + + try: + ess_data = monitor.energy_storage_system_status() + print("โœ… ESS data retrieval successful") + print(f"๐Ÿ“Š ESS result: {ess_data.get('result', 'Unknown')}") + except Exception as e: + print(f"โš ๏ธ ESS data failed (normal if no ESS): {e}") + + print("\n๐ŸŽ‰ All core functionality tests completed successfully!") + print("\n๐Ÿ’ก To test full Home Assistant integration:") + print(" 1. Install this integration in Home Assistant") + print(" 2. Use the configuration flow to set up your PVS") + print(" 3. Check entity registry for created sensors") + + except Exception as e: + print(f"โŒ Error during testing: {e}") + logger.exception("Full error details:") + + # Provide helpful troubleshooting info + print("\n๐Ÿ” Troubleshooting:") + print("- Check that PVS_HOST environment variable is set correctly") + print("- Verify PVS is accessible on your network") + print("- Ensure PVS management interface is connected") + print(f"- Try: curl 'http://{host}/cgi-bin/dl_cgi?Command=Get_Comm'") + + print("\nโœ… Debug session completed") + + +if __name__ == "__main__": + print("๐Ÿš€ SunPower Integration Debugger") + print("=" * 50) + print("This script helps debug the SunPower integration outside of Home Assistant.") + print("Set the PVS_HOST environment variable to your PVS IP address.") + print("Example: export PVS_HOST=192.168.1.100") + print("") + + # Run the debug session + asyncio.run(debug_integration()) diff --git a/tests/pytest.ini b/tests/pytest.ini new file mode 100644 index 0000000..5df8039 --- /dev/null +++ b/tests/pytest.ini @@ -0,0 +1,9 @@ +[pytest] + +makers = + pvs: Tests that work directly against a pvs to verify its working correctly (not if PVS env == MOCK this will test the Mock) + ess: Tests that work directly against an ESS to verify its working correctly (not if PVS env == MOCK this will test the Mock) + hass: Tests that validate home assistant functionality + +env = + PVS: MOCK diff --git a/tests/samples/device_list.json b/tests/samples/device_list.json new file mode 100644 index 0000000..ba00441 --- /dev/null +++ b/tests/samples/device_list.json @@ -0,0 +1,1066 @@ +{ + "devices": [ + { + "DETAIL": "detail", + "STATE": "working", + "STATEDESCR": "Working", + "SERIAL": "ZT1234567890", + "MODEL": "PV Supervisor PVS6", + "HWVER": "6.02", + "SWVER": "2022.7, Build 60661", + "DEVICE_TYPE": "PVS", + "DATATIME": "2024,04,17,18,00,00", + "dl_err_count": "0", + "dl_comm_err": "413", + "dl_skipped_scans": "0", + "dl_scan_time": "73", + "dl_untransmitted": "680553", + "dl_uptime": "36226", + "dl_cpu_load": "0.51", + "dl_mem_used": "87084", + "dl_flash_avail": "28593", + "panid": 410089015, + "CURTIME": "2024,04,17,18,02,19" + }, + { + "ISDETAIL": true, + "SERIAL": "SY12345670-111111.11111", + "TYPE": "HUB+", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "SunPower MIDC", + "DESCR": "HUB+ SY12345670-111111.11111", + "DEVICE_TYPE": "HUB+", + "hw_version": "1.5.0", + "interface": "ttymxc5", + "slave": 220, + "SWVER": "0.7.22", + "PORT": "P0, Modbus, Slave 220", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,20" + }, + { + "ISDETAIL": true, + "SERIAL": "SY111111111D1111", + "TYPE": "EQUINOX-MIO", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "SunPower MIO", + "DESCR": "ESS Hub SY111111111D1111", + "DEVICE_TYPE": "ESS Hub", + "hw_version": "0.4.0", + "interface": "ttymxc5", + "parent": 13, + "slave": 221, + "SWVER": "0.8.5", + "PORT": "P0, Modbus, Slave 221", + "DATATIME": "2024,04,17,18,02,13", + "t_degc": "41", + "humidity": "17", + "v_dcdc_spply_v": "11.397", + "v_spply_v": "11.353", + "v_gateway_v": "11.353", + "fan_actv_fl": "0", + "fw_error": "0", + "event_history": "2048", + "origin": "data_logger", + "OPERATION": "noop", + "PARENT": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "CURTIME": "2024,04,17,18,02,20" + }, + { + "ISDETAIL": true, + "SERIAL": "BC1111111101", + "TYPE": "GATEWAY", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "SchneiderElectric-ConextGateway", + "DESCR": "Gateway BC1111111101", + "DEVICE_TYPE": "Gateway", + "interface": "sunspec", + "mac_address": "40:2e:71:da:c6:47", + "slave": 1, + "SWVER": "V1", + "PORT": "P0, SunSpec, Slave 1", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,20" + }, + { + "ISDETAIL": true, + "SERIAL": "1111111111", + "TYPE": "SCHNEIDER-XWPRO", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "SchneiderElectric-XW6848-21", + "DESCR": "Storage Inverter 1111111111", + "DEVICE_TYPE": "Storage Inverter", + "interface": "sunspec", + "mac_address": "40:2e:71:da:c6:47", + "parent": 13, + "slave": 10, + "SWVER": "V1", + "PORT": "P0, SunSpec, Slave 10", + "origin": "data_logger", + "OPERATION": "noop", + "PARENT": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "CURTIME": "2024,04,17,18,02,20" + }, + { + "ISDETAIL": true, + "SERIAL": "BC111111111111111111", + "TYPE": "EQUINOX-BMS", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "SchneiderElectric-SP1", + "DESCR": "ESS BMS BC111111111111111111", + "DEVICE_TYPE": "ESS BMS", + "interface": "sunspec", + "mac_address": "40:2e:71:da:c6:00", + "parent": 13, + "slave": 230, + "PORT": "P0, SunSpec, Slave 230", + "origin": "data_logger", + "OPERATION": "noop", + "PARENT": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "CURTIME": "2024,04,17,18,02,21" + }, + { + "ISDETAIL": true, + "SERIAL": "PVS6M11111111p", + "TYPE": "PVS5-METER-P", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "PVS6M0400p", + "DESCR": "Power Meter PVS6M11111111p", + "DEVICE_TYPE": "Power Meter", + "interface": "mime", + "subtype": "GROSS_PRODUCTION_SITE", + "SWVER": "3000", + "PORT": "", + "DATATIME": "2024,04,17,18,02,20", + "ct_scl_fctr": "50", + "net_ltea_3phsum_kwh": "28817.2799", + "p_3phsum_kw": "5.8049", + "q_3phsum_kvar": "-0.1899", + "s_3phsum_kva": "5.8092", + "tot_pf_rto": "0.9991", + "freq_hz": "60", + "CAL0": "50", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,21" + }, + { + "ISDETAIL": true, + "SERIAL": "PVS6M11111111c", + "TYPE": "PVS5-METER-C", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "PVS6M0400c", + "DESCR": "Power Meter PVS6M11111111c", + "DEVICE_TYPE": "Power Meter", + "interface": "mime", + "subtype": "NET_CONSUMPTION_LOADSIDE", + "SWVER": "3000", + "PORT": "", + "DATATIME": "2024,04,17,18,02,21", + "ct_scl_fctr": "100", + "net_ltea_3phsum_kwh": "18037.2", + "p_3phsum_kw": "0.0727", + "q_3phsum_kvar": "0.3072", + "s_3phsum_kva": "0.6905", + "tot_pf_rto": "-0.0775", + "freq_hz": "60", + "i1_a": "2.9878", + "i2_a": "2.6403", + "v1n_v": "122.9457", + "v2n_v": "122.419", + "v12_v": "245.3642", + "p1_kw": "-0.19", + "p2_kw": "0.2628", + "neg_ltea_3phsum_kwh": "8442.2099", + "pos_ltea_3phsum_kwh": "26479.36", + "CAL0": "100", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,21" + }, + { + "ISDETAIL": true, + "SERIAL": "SY1111111-111111.11111_PVD1", + "TYPE": "PV-DISCONNECT", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "SPWR-PVD-model", + "DESCR": "PV Disconnect SY1111111-111111.11111_PVD1", + "DEVICE_TYPE": "PV Disconnect", + "hw_version": "0", + "interface": "none", + "slave": 0, + "SWVER": "0", + "PORT": "P0, Modbus, Slave 0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,21" + }, + { + "ISDETAIL": true, + "SERIAL": "M111111111111", + "TYPE": "BATTERY", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "POWERAMP-Komodo 1.2", + "DESCR": "Battery M111111111111", + "DEVICE_TYPE": "Battery", + "hw_version": "4.33", + "interface": "none", + "parent": 13, + "SWVER": "2.8", + "PORT": "P0, None, Slave -1", + "origin": "data_logger", + "OPERATION": "noop", + "PARENT": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "CURTIME": "2024,04,17,18,02,22" + }, + { + "ISDETAIL": true, + "SERIAL": "M111111111112", + "TYPE": "BATTERY", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "POWERAMP-Komodo 1.2", + "DESCR": "Battery M111111111112", + "DEVICE_TYPE": "Battery", + "hw_version": "4.33", + "interface": "none", + "parent": 13, + "SWVER": "2.8", + "PORT": "P0, None, Slave -1", + "origin": "data_logger", + "OPERATION": "noop", + "PARENT": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "CURTIME": "2024,04,17,18,02,22" + }, + { + "ISDETAIL": true, + "SERIAL": "M111111111113", + "TYPE": "BATTERY", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "POWERAMP-Komodo 1.2", + "DESCR": "Battery M111111111113", + "DEVICE_TYPE": "Battery", + "hw_version": "4.33", + "interface": "none", + "parent": 13, + "SWVER": "2.8", + "PORT": "P0, None, Slave -1", + "origin": "data_logger", + "OPERATION": "noop", + "PARENT": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "CURTIME": "2024,04,17,18,02,22" + }, + { + "ISDETAIL": true, + "SERIAL": "M111111111114", + "TYPE": "BATTERY", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "POWERAMP-Komodo 1.2", + "DESCR": "Battery M111111111114", + "DEVICE_TYPE": "Battery", + "hw_version": "4.33", + "interface": "none", + "parent": 13, + "SWVER": "2.8", + "PORT": "P0, None, Slave -1", + "origin": "data_logger", + "OPERATION": "noop", + "PARENT": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "CURTIME": "2024,04,17,18,02,23" + }, + { + "ISDETAIL": true, + "SERIAL": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "TYPE": "EQUINOX-ESS", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "SPWR-Equinox-model", + "DESCR": "Energy Storage System 00001C2C9E04_M001220470180F_M001220470180", + "DEVICE_TYPE": "Energy Storage System", + "hw_version": "0", + "interface": "none", + "operational_ac_kW": 6, + "operational_ac_kWh": 21.96, + "rated_ac_kW": 6.8, + "rated_ac_kWh": 26, + "SWVER": "0", + "PORT": "P0, Parent, Slave -1", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,23" + }, + { + "ISDETAIL": true, + "SERIAL": "E222222222222", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E222222222222", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "Q23M20535790", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "Q23M20535790", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1986.214", + "p_3phsum_kw": "0.2334", + "vln_3phavg_v": "249.73", + "i_3phsum_a": "0.93", + "p_mppt1_kw": "0.3063", + "v_mppt1_v": "53.13", + "i_mppt1_a": "5.76", + "t_htsnk_degc": "44", + "freq_hz": "59.99", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,23" + }, + { + "ISDETAIL": true, + "SERIAL": "E222222222223", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E222222222223", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "Q23111111111", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "Q23111111111", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1905.5391", + "p_3phsum_kw": "0.2275", + "vln_3phavg_v": "249.46", + "i_3phsum_a": "0.91", + "p_mppt1_kw": "0.3083", + "v_mppt1_v": "53.24", + "i_mppt1_a": "5.79", + "t_htsnk_degc": "46", + "freq_hz": "59.99", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,23" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901123", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901123", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E12345678901123", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901123", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "2211.4533", + "p_3phsum_kw": "0.1955", + "vln_3phavg_v": "247.1", + "i_3phsum_a": "0.79", + "p_mppt1_kw": "0.2599", + "v_mppt1_v": "54.1", + "i_mppt1_a": "4.8", + "t_htsnk_degc": "51", + "freq_hz": "59.99", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,23" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901124", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901124", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E12345678901124", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901124", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1876.7503", + "p_3phsum_kw": "0.2275", + "vln_3phavg_v": "248.62", + "i_3phsum_a": "0.91", + "p_mppt1_kw": "0.3038", + "v_mppt1_v": "53.21", + "i_mppt1_a": "5.71", + "t_htsnk_degc": "47", + "freq_hz": "60", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,24" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901125", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901125", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E1234567890115", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901155", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1905.0786", + "p_3phsum_kw": "0.2289", + "vln_3phavg_v": "248.69", + "i_3phsum_a": "0.92", + "p_mppt1_kw": "0.2957", + "v_mppt1_v": "52.75", + "i_mppt1_a": "5.6", + "t_htsnk_degc": "46", + "freq_hz": "60", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,24" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901125", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901125", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E12345678901", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "2191.7205", + "p_3phsum_kw": "0.1982", + "vln_3phavg_v": "247.3", + "i_3phsum_a": "0.8", + "p_mppt1_kw": "0.2556", + "v_mppt1_v": "54.65", + "i_mppt1_a": "4.67", + "t_htsnk_degc": "37", + "freq_hz": "60", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,24" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901126", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901126", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456789011", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1923.3012", + "p_3phsum_kw": "0.2347", + "vln_3phavg_v": "249.15", + "i_3phsum_a": "0.94", + "p_mppt1_kw": "0.298", + "v_mppt1_v": "53.19", + "i_mppt1_a": "5.6", + "t_htsnk_degc": "47", + "freq_hz": "60", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,24" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901127", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901127", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E1901124", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E1901124", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1909.901", + "p_3phsum_kw": "0.2341", + "vln_3phavg_v": "248.62", + "i_3phsum_a": "0.94", + "p_mppt1_kw": "0.3021", + "v_mppt1_v": "53.35", + "i_mppt1_a": "5.66", + "t_htsnk_degc": "46", + "freq_hz": "60.01", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,24" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901128", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901128", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123401127", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123401127", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1906.5346", + "p_3phsum_kw": "0.2337", + "vln_3phavg_v": "249.01", + "i_3phsum_a": "0.93", + "p_mppt1_kw": "0.3048", + "v_mppt1_v": "53.1", + "i_mppt1_a": "5.74", + "t_htsnk_degc": "46", + "freq_hz": "60.01", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,25" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901129", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901129", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123451128", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123451128", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1941.8943", + "p_3phsum_kw": "0.2349", + "vln_3phavg_v": "249.2", + "i_3phsum_a": "0.94", + "p_mppt1_kw": "0.3071", + "v_mppt1_v": "52.55", + "i_mppt1_a": "5.84", + "t_htsnk_degc": "48", + "freq_hz": "60.01", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,25" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901130", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901130", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456130", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456130", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1892.0793", + "p_3phsum_kw": "0.2341", + "vln_3phavg_v": "248.7", + "i_3phsum_a": "0.94", + "p_mppt1_kw": "0.3163", + "v_mppt1_v": "53.21", + "i_mppt1_a": "5.94", + "t_htsnk_degc": "46", + "freq_hz": "60.01", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,25" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901131", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901131", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E12345630", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345630", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "2171.8629", + "p_3phsum_kw": "0.2014", + "vln_3phavg_v": "247.67", + "i_3phsum_a": "0.81", + "p_mppt1_kw": "0.2688", + "v_mppt1_v": "54.76", + "i_mppt1_a": "4.9", + "t_htsnk_degc": "35", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,25" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901132", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901132", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456320", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456320", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1948.6541", + "p_3phsum_kw": "0.2364", + "vln_3phavg_v": "249.48", + "i_3phsum_a": "0.94", + "p_mppt1_kw": "0.3116", + "v_mppt1_v": "52.93", + "i_mppt1_a": "5.88", + "t_htsnk_degc": "47", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,25" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901133", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901133", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456321", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456321", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "2238.8076", + "p_3phsum_kw": "0.2071", + "vln_3phavg_v": "247.4", + "i_3phsum_a": "0.83", + "p_mppt1_kw": "0.2702", + "v_mppt1_v": "54.89", + "i_mppt1_a": "4.92", + "t_htsnk_degc": "34", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,26" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901134", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901134", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456322", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456322", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "2163.6433", + "p_3phsum_kw": "0.2009", + "vln_3phavg_v": "247.53", + "i_3phsum_a": "0.81", + "p_mppt1_kw": "0.2626", + "v_mppt1_v": "54.39", + "i_mppt1_a": "4.82", + "t_htsnk_degc": "38", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,26" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901135", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901135", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456323", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456324", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "1954.5777", + "p_3phsum_kw": "0.2424", + "vln_3phavg_v": "250.1", + "i_3phsum_a": "0.96", + "p_mppt1_kw": "0.3116", + "v_mppt1_v": "53.45", + "i_mppt1_a": "5.83", + "t_htsnk_degc": "48", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,26" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901136", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901136", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456323", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456323", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "1881.1138", + "p_3phsum_kw": "0.2365", + "vln_3phavg_v": "249.83", + "i_3phsum_a": "0.94", + "p_mppt1_kw": "0.3067", + "v_mppt1_v": "53.22", + "i_mppt1_a": "5.76", + "t_htsnk_degc": "46", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,26" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901137", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901137", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456324", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456324", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "2192.3515", + "p_3phsum_kw": "0.205", + "vln_3phavg_v": "247.64", + "i_3phsum_a": "0.82", + "p_mppt1_kw": "0.2647", + "v_mppt1_v": "54.46", + "i_mppt1_a": "4.86", + "t_htsnk_degc": "38", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,27" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901138", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901138", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456324", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456324", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "1972.373", + "p_3phsum_kw": "0.2463", + "vln_3phavg_v": "248.97", + "i_3phsum_a": "0.98", + "p_mppt1_kw": "0.3139", + "v_mppt1_v": "53.86", + "i_mppt1_a": "5.82", + "t_htsnk_degc": "43", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,27" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901139", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901139", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456325", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456325", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "2186.3743", + "p_3phsum_kw": "0.2046", + "vln_3phavg_v": "247.34", + "i_3phsum_a": "0.82", + "p_mppt1_kw": "0.2618", + "v_mppt1_v": "54.57", + "i_mppt1_a": "4.79", + "t_htsnk_degc": "38", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,27" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901140", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901140", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E12345678901140", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901140", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "1938.8079", + "p_3phsum_kw": "0.2437", + "vln_3phavg_v": "249.9", + "i_3phsum_a": "0.97", + "p_mppt1_kw": "0.309", + "v_mppt1_v": "53.48", + "i_mppt1_a": "5.77", + "t_htsnk_degc": "46", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,27" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901141", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901141", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E12345678901141", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901141", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "1883.5216", + "p_3phsum_kw": "0.2437", + "vln_3phavg_v": "249.2", + "i_3phsum_a": "0.97", + "p_mppt1_kw": "0.3128", + "v_mppt1_v": "53.59", + "i_mppt1_a": "5.83", + "t_htsnk_degc": "46", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,27" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901142", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901142", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E12345678901141", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901141", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "1931.6134", + "p_3phsum_kw": "0.2454", + "vln_3phavg_v": "249.52", + "i_3phsum_a": "0.98", + "p_mppt1_kw": "0.3053", + "v_mppt1_v": "53.56", + "i_mppt1_a": "5.7", + "t_htsnk_degc": "43", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,28" + } + ], + "result": "succeed" +} diff --git a/tests/samples/ess_device_list.json b/tests/samples/ess_device_list.json new file mode 100644 index 0000000..ba00441 --- /dev/null +++ b/tests/samples/ess_device_list.json @@ -0,0 +1,1066 @@ +{ + "devices": [ + { + "DETAIL": "detail", + "STATE": "working", + "STATEDESCR": "Working", + "SERIAL": "ZT1234567890", + "MODEL": "PV Supervisor PVS6", + "HWVER": "6.02", + "SWVER": "2022.7, Build 60661", + "DEVICE_TYPE": "PVS", + "DATATIME": "2024,04,17,18,00,00", + "dl_err_count": "0", + "dl_comm_err": "413", + "dl_skipped_scans": "0", + "dl_scan_time": "73", + "dl_untransmitted": "680553", + "dl_uptime": "36226", + "dl_cpu_load": "0.51", + "dl_mem_used": "87084", + "dl_flash_avail": "28593", + "panid": 410089015, + "CURTIME": "2024,04,17,18,02,19" + }, + { + "ISDETAIL": true, + "SERIAL": "SY12345670-111111.11111", + "TYPE": "HUB+", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "SunPower MIDC", + "DESCR": "HUB+ SY12345670-111111.11111", + "DEVICE_TYPE": "HUB+", + "hw_version": "1.5.0", + "interface": "ttymxc5", + "slave": 220, + "SWVER": "0.7.22", + "PORT": "P0, Modbus, Slave 220", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,20" + }, + { + "ISDETAIL": true, + "SERIAL": "SY111111111D1111", + "TYPE": "EQUINOX-MIO", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "SunPower MIO", + "DESCR": "ESS Hub SY111111111D1111", + "DEVICE_TYPE": "ESS Hub", + "hw_version": "0.4.0", + "interface": "ttymxc5", + "parent": 13, + "slave": 221, + "SWVER": "0.8.5", + "PORT": "P0, Modbus, Slave 221", + "DATATIME": "2024,04,17,18,02,13", + "t_degc": "41", + "humidity": "17", + "v_dcdc_spply_v": "11.397", + "v_spply_v": "11.353", + "v_gateway_v": "11.353", + "fan_actv_fl": "0", + "fw_error": "0", + "event_history": "2048", + "origin": "data_logger", + "OPERATION": "noop", + "PARENT": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "CURTIME": "2024,04,17,18,02,20" + }, + { + "ISDETAIL": true, + "SERIAL": "BC1111111101", + "TYPE": "GATEWAY", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "SchneiderElectric-ConextGateway", + "DESCR": "Gateway BC1111111101", + "DEVICE_TYPE": "Gateway", + "interface": "sunspec", + "mac_address": "40:2e:71:da:c6:47", + "slave": 1, + "SWVER": "V1", + "PORT": "P0, SunSpec, Slave 1", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,20" + }, + { + "ISDETAIL": true, + "SERIAL": "1111111111", + "TYPE": "SCHNEIDER-XWPRO", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "SchneiderElectric-XW6848-21", + "DESCR": "Storage Inverter 1111111111", + "DEVICE_TYPE": "Storage Inverter", + "interface": "sunspec", + "mac_address": "40:2e:71:da:c6:47", + "parent": 13, + "slave": 10, + "SWVER": "V1", + "PORT": "P0, SunSpec, Slave 10", + "origin": "data_logger", + "OPERATION": "noop", + "PARENT": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "CURTIME": "2024,04,17,18,02,20" + }, + { + "ISDETAIL": true, + "SERIAL": "BC111111111111111111", + "TYPE": "EQUINOX-BMS", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "SchneiderElectric-SP1", + "DESCR": "ESS BMS BC111111111111111111", + "DEVICE_TYPE": "ESS BMS", + "interface": "sunspec", + "mac_address": "40:2e:71:da:c6:00", + "parent": 13, + "slave": 230, + "PORT": "P0, SunSpec, Slave 230", + "origin": "data_logger", + "OPERATION": "noop", + "PARENT": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "CURTIME": "2024,04,17,18,02,21" + }, + { + "ISDETAIL": true, + "SERIAL": "PVS6M11111111p", + "TYPE": "PVS5-METER-P", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "PVS6M0400p", + "DESCR": "Power Meter PVS6M11111111p", + "DEVICE_TYPE": "Power Meter", + "interface": "mime", + "subtype": "GROSS_PRODUCTION_SITE", + "SWVER": "3000", + "PORT": "", + "DATATIME": "2024,04,17,18,02,20", + "ct_scl_fctr": "50", + "net_ltea_3phsum_kwh": "28817.2799", + "p_3phsum_kw": "5.8049", + "q_3phsum_kvar": "-0.1899", + "s_3phsum_kva": "5.8092", + "tot_pf_rto": "0.9991", + "freq_hz": "60", + "CAL0": "50", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,21" + }, + { + "ISDETAIL": true, + "SERIAL": "PVS6M11111111c", + "TYPE": "PVS5-METER-C", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "PVS6M0400c", + "DESCR": "Power Meter PVS6M11111111c", + "DEVICE_TYPE": "Power Meter", + "interface": "mime", + "subtype": "NET_CONSUMPTION_LOADSIDE", + "SWVER": "3000", + "PORT": "", + "DATATIME": "2024,04,17,18,02,21", + "ct_scl_fctr": "100", + "net_ltea_3phsum_kwh": "18037.2", + "p_3phsum_kw": "0.0727", + "q_3phsum_kvar": "0.3072", + "s_3phsum_kva": "0.6905", + "tot_pf_rto": "-0.0775", + "freq_hz": "60", + "i1_a": "2.9878", + "i2_a": "2.6403", + "v1n_v": "122.9457", + "v2n_v": "122.419", + "v12_v": "245.3642", + "p1_kw": "-0.19", + "p2_kw": "0.2628", + "neg_ltea_3phsum_kwh": "8442.2099", + "pos_ltea_3phsum_kwh": "26479.36", + "CAL0": "100", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,21" + }, + { + "ISDETAIL": true, + "SERIAL": "SY1111111-111111.11111_PVD1", + "TYPE": "PV-DISCONNECT", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "SPWR-PVD-model", + "DESCR": "PV Disconnect SY1111111-111111.11111_PVD1", + "DEVICE_TYPE": "PV Disconnect", + "hw_version": "0", + "interface": "none", + "slave": 0, + "SWVER": "0", + "PORT": "P0, Modbus, Slave 0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,21" + }, + { + "ISDETAIL": true, + "SERIAL": "M111111111111", + "TYPE": "BATTERY", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "POWERAMP-Komodo 1.2", + "DESCR": "Battery M111111111111", + "DEVICE_TYPE": "Battery", + "hw_version": "4.33", + "interface": "none", + "parent": 13, + "SWVER": "2.8", + "PORT": "P0, None, Slave -1", + "origin": "data_logger", + "OPERATION": "noop", + "PARENT": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "CURTIME": "2024,04,17,18,02,22" + }, + { + "ISDETAIL": true, + "SERIAL": "M111111111112", + "TYPE": "BATTERY", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "POWERAMP-Komodo 1.2", + "DESCR": "Battery M111111111112", + "DEVICE_TYPE": "Battery", + "hw_version": "4.33", + "interface": "none", + "parent": 13, + "SWVER": "2.8", + "PORT": "P0, None, Slave -1", + "origin": "data_logger", + "OPERATION": "noop", + "PARENT": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "CURTIME": "2024,04,17,18,02,22" + }, + { + "ISDETAIL": true, + "SERIAL": "M111111111113", + "TYPE": "BATTERY", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "POWERAMP-Komodo 1.2", + "DESCR": "Battery M111111111113", + "DEVICE_TYPE": "Battery", + "hw_version": "4.33", + "interface": "none", + "parent": 13, + "SWVER": "2.8", + "PORT": "P0, None, Slave -1", + "origin": "data_logger", + "OPERATION": "noop", + "PARENT": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "CURTIME": "2024,04,17,18,02,22" + }, + { + "ISDETAIL": true, + "SERIAL": "M111111111114", + "TYPE": "BATTERY", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "POWERAMP-Komodo 1.2", + "DESCR": "Battery M111111111114", + "DEVICE_TYPE": "Battery", + "hw_version": "4.33", + "interface": "none", + "parent": 13, + "SWVER": "2.8", + "PORT": "P0, None, Slave -1", + "origin": "data_logger", + "OPERATION": "noop", + "PARENT": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "CURTIME": "2024,04,17,18,02,23" + }, + { + "ISDETAIL": true, + "SERIAL": "111111111111_111111111111F_1111111111110E_111111111111F_1111111111113D", + "TYPE": "EQUINOX-ESS", + "STATE": "error", + "STATEDESCR": "Error", + "MODEL": "SPWR-Equinox-model", + "DESCR": "Energy Storage System 00001C2C9E04_M001220470180F_M001220470180", + "DEVICE_TYPE": "Energy Storage System", + "hw_version": "0", + "interface": "none", + "operational_ac_kW": 6, + "operational_ac_kWh": 21.96, + "rated_ac_kW": 6.8, + "rated_ac_kWh": 26, + "SWVER": "0", + "PORT": "P0, Parent, Slave -1", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,23" + }, + { + "ISDETAIL": true, + "SERIAL": "E222222222222", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E222222222222", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "Q23M20535790", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "Q23M20535790", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1986.214", + "p_3phsum_kw": "0.2334", + "vln_3phavg_v": "249.73", + "i_3phsum_a": "0.93", + "p_mppt1_kw": "0.3063", + "v_mppt1_v": "53.13", + "i_mppt1_a": "5.76", + "t_htsnk_degc": "44", + "freq_hz": "59.99", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,23" + }, + { + "ISDETAIL": true, + "SERIAL": "E222222222223", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E222222222223", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "Q23111111111", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "Q23111111111", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1905.5391", + "p_3phsum_kw": "0.2275", + "vln_3phavg_v": "249.46", + "i_3phsum_a": "0.91", + "p_mppt1_kw": "0.3083", + "v_mppt1_v": "53.24", + "i_mppt1_a": "5.79", + "t_htsnk_degc": "46", + "freq_hz": "59.99", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,23" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901123", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901123", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E12345678901123", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901123", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "2211.4533", + "p_3phsum_kw": "0.1955", + "vln_3phavg_v": "247.1", + "i_3phsum_a": "0.79", + "p_mppt1_kw": "0.2599", + "v_mppt1_v": "54.1", + "i_mppt1_a": "4.8", + "t_htsnk_degc": "51", + "freq_hz": "59.99", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,23" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901124", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901124", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E12345678901124", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901124", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1876.7503", + "p_3phsum_kw": "0.2275", + "vln_3phavg_v": "248.62", + "i_3phsum_a": "0.91", + "p_mppt1_kw": "0.3038", + "v_mppt1_v": "53.21", + "i_mppt1_a": "5.71", + "t_htsnk_degc": "47", + "freq_hz": "60", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,24" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901125", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901125", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E1234567890115", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901155", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1905.0786", + "p_3phsum_kw": "0.2289", + "vln_3phavg_v": "248.69", + "i_3phsum_a": "0.92", + "p_mppt1_kw": "0.2957", + "v_mppt1_v": "52.75", + "i_mppt1_a": "5.6", + "t_htsnk_degc": "46", + "freq_hz": "60", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,24" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901125", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901125", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E12345678901", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "2191.7205", + "p_3phsum_kw": "0.1982", + "vln_3phavg_v": "247.3", + "i_3phsum_a": "0.8", + "p_mppt1_kw": "0.2556", + "v_mppt1_v": "54.65", + "i_mppt1_a": "4.67", + "t_htsnk_degc": "37", + "freq_hz": "60", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,24" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901126", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901126", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456789011", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1923.3012", + "p_3phsum_kw": "0.2347", + "vln_3phavg_v": "249.15", + "i_3phsum_a": "0.94", + "p_mppt1_kw": "0.298", + "v_mppt1_v": "53.19", + "i_mppt1_a": "5.6", + "t_htsnk_degc": "47", + "freq_hz": "60", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,24" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901127", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901127", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E1901124", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E1901124", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1909.901", + "p_3phsum_kw": "0.2341", + "vln_3phavg_v": "248.62", + "i_3phsum_a": "0.94", + "p_mppt1_kw": "0.3021", + "v_mppt1_v": "53.35", + "i_mppt1_a": "5.66", + "t_htsnk_degc": "46", + "freq_hz": "60.01", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,24" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901128", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901128", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123401127", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123401127", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1906.5346", + "p_3phsum_kw": "0.2337", + "vln_3phavg_v": "249.01", + "i_3phsum_a": "0.93", + "p_mppt1_kw": "0.3048", + "v_mppt1_v": "53.1", + "i_mppt1_a": "5.74", + "t_htsnk_degc": "46", + "freq_hz": "60.01", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,25" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901129", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901129", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123451128", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123451128", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1941.8943", + "p_3phsum_kw": "0.2349", + "vln_3phavg_v": "249.2", + "i_3phsum_a": "0.94", + "p_mppt1_kw": "0.3071", + "v_mppt1_v": "52.55", + "i_mppt1_a": "5.84", + "t_htsnk_degc": "48", + "freq_hz": "60.01", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,25" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901130", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901130", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456130", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456130", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1892.0793", + "p_3phsum_kw": "0.2341", + "vln_3phavg_v": "248.7", + "i_3phsum_a": "0.94", + "p_mppt1_kw": "0.3163", + "v_mppt1_v": "53.21", + "i_mppt1_a": "5.94", + "t_htsnk_degc": "46", + "freq_hz": "60.01", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,25" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901131", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901131", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E12345630", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345630", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "2171.8629", + "p_3phsum_kw": "0.2014", + "vln_3phavg_v": "247.67", + "i_3phsum_a": "0.81", + "p_mppt1_kw": "0.2688", + "v_mppt1_v": "54.76", + "i_mppt1_a": "4.9", + "t_htsnk_degc": "35", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,25" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901132", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901132", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456320", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456320", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "1948.6541", + "p_3phsum_kw": "0.2364", + "vln_3phavg_v": "249.48", + "i_3phsum_a": "0.94", + "p_mppt1_kw": "0.3116", + "v_mppt1_v": "52.93", + "i_mppt1_a": "5.88", + "t_htsnk_degc": "47", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,25" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901133", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901133", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456321", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456321", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "2238.8076", + "p_3phsum_kw": "0.2071", + "vln_3phavg_v": "247.4", + "i_3phsum_a": "0.83", + "p_mppt1_kw": "0.2702", + "v_mppt1_v": "54.89", + "i_mppt1_a": "4.92", + "t_htsnk_degc": "34", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,26" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901134", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901134", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456322", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456322", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,43", + "ltea_3phsum_kwh": "2163.6433", + "p_3phsum_kw": "0.2009", + "vln_3phavg_v": "247.53", + "i_3phsum_a": "0.81", + "p_mppt1_kw": "0.2626", + "v_mppt1_v": "54.39", + "i_mppt1_a": "4.82", + "t_htsnk_degc": "38", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,26" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901135", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901135", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456323", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456324", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "1954.5777", + "p_3phsum_kw": "0.2424", + "vln_3phavg_v": "250.1", + "i_3phsum_a": "0.96", + "p_mppt1_kw": "0.3116", + "v_mppt1_v": "53.45", + "i_mppt1_a": "5.83", + "t_htsnk_degc": "48", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,26" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901136", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901136", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456323", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456323", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "1881.1138", + "p_3phsum_kw": "0.2365", + "vln_3phavg_v": "249.83", + "i_3phsum_a": "0.94", + "p_mppt1_kw": "0.3067", + "v_mppt1_v": "53.22", + "i_mppt1_a": "5.76", + "t_htsnk_degc": "46", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,26" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901137", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901137", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456324", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456324", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "2192.3515", + "p_3phsum_kw": "0.205", + "vln_3phavg_v": "247.64", + "i_3phsum_a": "0.82", + "p_mppt1_kw": "0.2647", + "v_mppt1_v": "54.46", + "i_mppt1_a": "4.86", + "t_htsnk_degc": "38", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,27" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901138", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901138", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456324", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456324", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "1972.373", + "p_3phsum_kw": "0.2463", + "vln_3phavg_v": "248.97", + "i_3phsum_a": "0.98", + "p_mppt1_kw": "0.3139", + "v_mppt1_v": "53.86", + "i_mppt1_a": "5.82", + "t_htsnk_degc": "43", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,27" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901139", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901139", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E123456325", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E123456325", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "2186.3743", + "p_3phsum_kw": "0.2046", + "vln_3phavg_v": "247.34", + "i_3phsum_a": "0.82", + "p_mppt1_kw": "0.2618", + "v_mppt1_v": "54.57", + "i_mppt1_a": "4.79", + "t_htsnk_degc": "38", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,27" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901140", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901140", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E12345678901140", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901140", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "1938.8079", + "p_3phsum_kw": "0.2437", + "vln_3phavg_v": "249.9", + "i_3phsum_a": "0.97", + "p_mppt1_kw": "0.309", + "v_mppt1_v": "53.48", + "i_mppt1_a": "5.77", + "t_htsnk_degc": "46", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,27" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901141", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901141", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E12345678901141", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901141", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "1883.5216", + "p_3phsum_kw": "0.2437", + "vln_3phavg_v": "249.2", + "i_3phsum_a": "0.97", + "p_mppt1_kw": "0.3128", + "v_mppt1_v": "53.59", + "i_mppt1_a": "5.83", + "t_htsnk_degc": "46", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,27" + }, + { + "ISDETAIL": true, + "SERIAL": "E12345678901142", + "TYPE": "SOLARBRIDGE", + "STATE": "working", + "STATEDESCR": "Working", + "MODEL": "AC_Module_Type_E", + "DESCR": "Inverter E12345678901142", + "DEVICE_TYPE": "Inverter", + "hw_version": "4405", + "interface": "mime", + "module_serial": "E12345678901141", + "PANEL": "SPR-X21-335-BLK-E-AC", + "slave": 0, + "SWVER": "4.28.7", + "PORT": "", + "MOD_SN": "E12345678901141", + "NMPLT_SKU": "", + "DATATIME": "2024,04,17,18,00,58", + "ltea_3phsum_kwh": "1931.6134", + "p_3phsum_kw": "0.2454", + "vln_3phavg_v": "249.52", + "i_3phsum_a": "0.98", + "p_mppt1_kw": "0.3053", + "v_mppt1_v": "53.56", + "i_mppt1_a": "5.7", + "t_htsnk_degc": "43", + "freq_hz": "60.02", + "stat_ind": "0", + "origin": "data_logger", + "OPERATION": "noop", + "CURTIME": "2024,04,17,18,02,28" + } + ], + "result": "succeed" +} diff --git a/tests/samples/ess_status.json b/tests/samples/ess_status.json new file mode 100644 index 0000000..049fb09 --- /dev/null +++ b/tests/samples/ess_status.json @@ -0,0 +1,190 @@ +{ + "errors": [ + { + "device_sn": "111111111111", + "error_code": "12001", + "error_message": "storage_inv_over_temperature_warning", + "error_name": "storage_inv_over_temperature_warning", + "last_occurence": "2024-04-17 18:01:22", + "value": { + "unit": "", + "value": 51.15 + } + } + ], + "ess_report": { + "battery_status": [ + { + "battery_amperage": { + "unit": "A", + "value": 8.4 + }, + "battery_voltage": { + "unit": "V", + "value": 53.2 + }, + "customer_state_of_charge": { + "unit": "%", + "value": 55.000000000000007 + }, + "last_updated": "2024-04-17 18:01:13", + "serial_number": "BC1111111111111111111", + "system_state_of_charge": { + "unit": "%", + "value": 62 + }, + "temperature": { + "unit": "C", + "value": 29.8 + } + } + ], + "ess_state": [ + { + "operational_mode": "SELF_CONSUMPTION", + "permission_to_operate": true, + "storage_controller_status": "RUNNING" + } + ], + "ess_status": [ + { + "enclosure_humidity": { + "unit": "%", + "value": 17 + }, + "enclosure_temperature": { + "unit": "C", + "value": 41 + }, + "ess_meter_reading": { + "agg_power": { + "unit": "kW", + "value": -4.962 + }, + "last_updated": "2024-04-17 18:01:22", + "meter_a": { + "reading": { + "current": { + "unit": "A", + "value": 17.78 + }, + "last_updated": "2024-04-17 18:01:22", + "power": { + "unit": "W", + "value": 2164.3594000000003 + }, + "voltage": { + "unit": "V", + "value": 121.73 + } + } + }, + "meter_b": { + "reading": { + "current": { + "unit": "A", + "value": 17.82 + }, + "last_updated": "2024-04-17 18:01:22", + "power": { + "unit": "W", + "value": 2169.2286 + }, + "voltage": { + "unit": "V", + "value": 121.73 + } + } + } + }, + "last_updated": "2024-04-17 18:01:13", + "serial_number": "11111111111_M1111111111F_M1111111111111111E_M1111111111111F_M111111111111D" + } + ], + "hub_plus_status": { + "aux_port_voltage": { + "unit": "V", + "value": 11.276 + }, + "contactor_error": "NONE", + "contactor_position": "CLOSED", + "grid_frequency_state": "METER_FREQ_IN_RANGE", + "grid_phase1_voltage": { + "unit": "V", + "value": 123.30000000000001 + }, + "grid_phase2_voltage": { + "unit": "V", + "value": 123.2 + }, + "grid_voltage_state": "METER_VOLTAGE_IN_RANGE", + "hub_humidity": { + "unit": "%", + "value": 33 + }, + "hub_temperature": { + "unit": "C", + "value": 26 + }, + "inverter_connection_voltage": { + "unit": "V", + "value": 0.277 + }, + "jump_start_voltage": { + "unit": "V", + "value": 1.177 + }, + "last_updated": "2024-04-17 18:01:20", + "load_frequency_state": "METER_FREQ_IN_RANGE", + "load_phase1_voltage": { + "unit": "V", + "value": 123.2 + }, + "load_phase2_voltage": { + "unit": "V", + "value": 123.30000000000001 + }, + "load_voltage_state": "METER_VOLTAGE_IN_RANGE", + "main_voltage": { + "unit": "V", + "value": 11.364 + }, + "serial_number": "SY1234567-11111111.111111" + }, + "inverter_status": [ + { + "a_n_voltage": { + "unit": "V", + "value": 121.73 + }, + "ac_current": { + "unit": "A", + "value": 17.8 + }, + "ac_power": { + "unit": "kW", + "value": -4.962 + }, + "b_n_voltage": { + "unit": "V", + "value": 121.73 + }, + "last_updated": "2024-04-17 18:01:22", + "phase_a_current": { + "unit": "A", + "value": 17.78 + }, + "phase_b_current": { + "unit": "A", + "value": 17.82 + }, + "serial_number": "111111111111", + "temperature": { + "unit": "C", + "value": 52.550000000000004 + } + } + ], + "last_updated": "2024-04-17 18:01:22" + } +} diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100755 index 0000000..2ef3748 --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +""" +Simple script to test the SunPower API client directly. +Set PVS_HOST environment variable or edit the host below. +""" + +import asyncio +import os +import sys +from pprint import pprint + +# Add the custom_components directory to Python path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "custom_components")) + +from sunpower.sunpower import ( # noqa: E402 + ConnectionException, + ParseException, + SunPowerMonitor, +) + + +async def test_api(): + """Test the SunPower API client.""" + # Change this to your PVS IP address + host = os.getenv("PVS_HOST", "172.27.153.1") # Default PVS IP for NAT setup + + print(f"Testing SunPower API connection to {host}") + print("-" * 50) + + monitor = SunPowerMonitor(host) + + try: + print("1. Testing network status...") + network_status = monitor.network_status() + print("โœ… Network status successful") + pprint(network_status) + print() + + except (ConnectionException, ParseException) as e: + print(f"โŒ Network status failed: {e}") + print("Check that:") + print("- PVS is accessible at the IP address") + print("- Network connectivity is working") + print("- PVS management interface is enabled") + return + + try: + print("2. Testing device list...") + device_list = monitor.device_list() + print("โœ… Device list successful") + print(f"Found {len(device_list.get('devices', []))} devices") + + # Show device summary + device_types = {} + for device in device_list.get("devices", []): + device_type = device.get("DEVICE_TYPE", "Unknown") + device_types[device_type] = device_types.get(device_type, 0) + 1 + + print("Device breakdown:") + for device_type, count in device_types.items(): + print(f" - {device_type}: {count}") + print() + + except (ConnectionException, ParseException) as e: + print(f"โŒ Device list failed: {e}") + print() + + try: + print("3. Testing energy storage system status...") + ess_status = monitor.energy_storage_system_status() + print("โœ… ESS status successful") + pprint(ess_status) + print() + + except (ConnectionException, ParseException) as e: + print(f"โŒ ESS status failed (this is normal if no ESS): {e}") + print() + + +if __name__ == "__main__": + # Run the async test + asyncio.run(test_api()) diff --git a/tests/test_ha_integration.py b/tests/test_ha_integration.py new file mode 100755 index 0000000..8895b7f --- /dev/null +++ b/tests/test_ha_integration.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +""" +Test Home Assistant integration with proper mocking. +This script tests the integration setup without requiring a full HA environment. +""" + +import asyncio +import logging +import os +import sys +from unittest.mock import ( + AsyncMock, + Mock, +) + +# Add the custom_components directory to Python path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "custom_components")) + +from homeassistant.config_entries import ConfigEntry # noqa: E402 +from homeassistant.core import HomeAssistant # noqa: E402 +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator # noqa: E402 +from sunpower.const import ( # noqa: E402 + DOMAIN, + SUNPOWER_DESCRIPTIVE_NAMES, + SUNPOWER_HOST, + SUNPOWER_PRODUCT_NAMES, +) + +# Set up logging +logging.basicConfig( + level=logging.DEBUG, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", +) +logger = logging.getLogger(__name__) + + +async def test_ha_integration(): + """Test the integration with a more realistic Home Assistant mock.""" + host = os.getenv("PVS_HOST", "172.27.153.1") + + print("๐Ÿ  Testing Home Assistant Integration") + print(f"๐Ÿ“ก PVS Host: {host}") + print("-" * 50) + + # Create a minimal mock HA that satisfies the coordinator requirements + hass = Mock(spec=HomeAssistant) + hass.data = {} + hass.loop = asyncio.get_event_loop() + hass.bus = Mock() + hass.bus.async_fire = AsyncMock() + hass.states = Mock() + + # Mock async_add_executor_job + async def mock_executor_job(func, *args): + return func(*args) + + hass.async_add_executor_job = mock_executor_job + + # Create config entry + config_entry = Mock(spec=ConfigEntry) + config_entry.entry_id = "test_entry_123" + config_entry.data = { + SUNPOWER_HOST: host, + SUNPOWER_DESCRIPTIVE_NAMES: True, + SUNPOWER_PRODUCT_NAMES: False, + } + config_entry.options = {} + + def mock_unload(func): + pass + + config_entry.async_on_unload = mock_unload + config_entry.add_update_listener = Mock(return_value=mock_unload) + + try: + print("1. Testing basic data structures...") + + # Initialize domain data + hass.data.setdefault(DOMAIN, {}) + print("โœ… Domain data initialized") + + print("\n2. Testing data coordinator creation...") + + # Create a simple coordinator without the full integration + from datetime import timedelta + + from sunpower import sunpower_fetch # noqa: E402 + from sunpower.sunpower import SunPowerMonitor # noqa: E402 + + monitor = SunPowerMonitor(host) + + async def async_update_data(): + """Test update function.""" + return sunpower_fetch(monitor, 120, 60, config_entry.entry_id) + + coordinator = DataUpdateCoordinator( + hass, + logger, + name="SunPower PVS Test", + update_method=async_update_data, + update_interval=timedelta(seconds=120), + ) + print("โœ… Data coordinator created successfully") + + print("\n3. Testing data fetch...") + + # Test data fetch + await coordinator.async_refresh() + + if coordinator.data: + print("โœ… Data fetch successful") + print(f"๐Ÿ“Š Data keys: {list(coordinator.data.keys())}") + + # Show device summary + total_devices = sum(len(devices) for devices in coordinator.data.values()) + print(f"๐Ÿ“ˆ Total devices: {total_devices}") + else: + print("โš ๏ธ No data returned from coordinator") + + print("\n๐ŸŽ‰ Home Assistant integration test completed successfully!") + + except Exception as e: + print(f"โŒ Error during HA integration testing: {e}") + logger.exception("Full error details:") + + # Provide helpful info + print("\n๐Ÿ” This test validates that:") + print("- DataUpdateCoordinator can be created") + print("- Data fetching works with the coordinator") + print("- Basic Home Assistant compatibility") + + print("\nโœ… HA integration test completed") + + +if __name__ == "__main__": + print("๐Ÿ  SunPower Home Assistant Integration Tester") + print("=" * 50) + print("This script tests the integration's compatibility with Home Assistant.") + print("Set PVS_HOST environment variable to test with a real PVS.") + print("") + + # Run the test + asyncio.run(test_ha_integration()) diff --git a/tests/test_hardware.py b/tests/test_hardware.py new file mode 100644 index 0000000..fef9070 --- /dev/null +++ b/tests/test_hardware.py @@ -0,0 +1,70 @@ +import logging +import os +import subprocess +import sys + +import pytest + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "custom_components")) + +import sunpower # noqa: E402 + +_LOGGER = logging.getLogger(__name__) + + +@pytest.mark.pvs() +@pytest.mark.skipif( + os.getenv("PVS") == "MOCK", + reason="Skipping real PVS tests in MOCK mode", +) +def test_ping(): + """Test we can ping the PVS device.""" + _LOGGER.warning("Pinging PVS at", os.environ["PVS"]) + subprocess.check_call(["ping", "-c", "1", os.environ["PVS"]]) + + +@pytest.mark.pvs() +def test_pvs_device_list(sunpowermonitor): + """Test we can get a device list from the PVS.""" + result = sunpowermonitor.device_list() + _LOGGER.debug("Device List: %s", result) + assert "devices" in result + assert isinstance(result["devices"], list) + assert len(result["devices"]) > 0 + _LOGGER.warning(f"Found {len(result['devices'])} devices in device list") + + +@pytest.mark.ess() +def test_pvs_ess(sunpowermonitor): + """Test we can get an ESS list from the PVS.""" + result = sunpowermonitor.energy_storage_system_status() + _LOGGER.debug("ESS data: %s", result) + assert "errors" in result + assert "ess_report" in result + + +@pytest.mark.pvs() +def test_pvs_network(sunpowermonitor): + """Test we can get an Netork from the PVS.""" + result = sunpowermonitor.network_status() + _LOGGER.debug("Network data: %s", result) + assert result is not None + + +@pytest.mark.pvs() +def test_pvs_parse(sunpowermonitor): + data = sunpower.convert_sunpower_data(sunpowermonitor.device_list()) + _LOGGER.debug("Converted data: %s", data) + assert len(data["PVS"]) > 0 + assert len(data["Inverter"]) > 0 + for type, count in [(x, len(data[x])) for x in data.keys()]: + _LOGGER.warning(f"Found {count} devices of type {type}") + + +@pytest.mark.ess() +def test_ess_parse(sunpowermonitor): + data = sunpower.convert_ess_data( + sunpowermonitor.energy_storage_system_status(), + sunpowermonitor.device_list(), + ) + _LOGGER.warning(data) diff --git a/tests/test_sunpower_api.py b/tests/test_sunpower_api.py new file mode 100644 index 0000000..48ef789 --- /dev/null +++ b/tests/test_sunpower_api.py @@ -0,0 +1,74 @@ +"""Test the SunPower API client.""" + +import os +import sys +from unittest.mock import ( + Mock, + patch, +) + +import pytest +import requests + +# Add custom_components to path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "custom_components")) + +from sunpower.sunpower import ( # noqa: E402 + ConnectionException, + SunPowerMonitor, +) + + +class TestSunPowerMonitor: + """Test the SunPowerMonitor class.""" + + def test_init(self): + """Test SunPowerMonitor initialization.""" + monitor = SunPowerMonitor("192.168.1.100") + assert monitor.host == "192.168.1.100" + assert monitor.command_url == "http://192.168.1.100/cgi-bin/dl_cgi?Command=" + + @patch("sunpower.sunpower.requests") + def test_device_list_success(self, mock_requests): + """Test successful device list retrieval.""" + # Mock response + mock_response = Mock() + mock_response.json.return_value = {"devices": []} + mock_requests.get.return_value = mock_response + + monitor = SunPowerMonitor("192.168.1.100") + result = monitor.device_list() + + assert result == {"devices": []} + mock_requests.get.assert_called_once_with( + "http://192.168.1.100/cgi-bin/dl_cgi?Command=DeviceList", + timeout=120, + ) + + @patch("sunpower.sunpower.requests") + def test_device_list_connection_error(self, mock_requests): + """Test device list with connection error.""" + # Mock the exception class as well + mock_requests.exceptions.RequestException = requests.exceptions.RequestException + mock_requests.get.side_effect = requests.exceptions.RequestException("Connection failed") + + monitor = SunPowerMonitor("192.168.1.100") + + with pytest.raises(ConnectionException): + monitor.device_list() + + @patch("sunpower.sunpower.requests") + def test_network_status_success(self, mock_requests): + """Test successful network status retrieval.""" + mock_response = Mock() + mock_response.json.return_value = {"status": "ok"} + mock_requests.get.return_value = mock_response + + monitor = SunPowerMonitor("192.168.1.100") + result = monitor.network_status() + + assert result == {"status": "ok"} + mock_requests.get.assert_called_once_with( + "http://192.168.1.100/cgi-bin/dl_cgi?Command=Get_Comm", + timeout=120, + ) diff --git a/tests/test_with_sample_data.py b/tests/test_with_sample_data.py new file mode 100755 index 0000000..48724bd --- /dev/null +++ b/tests/test_with_sample_data.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 +""" +Test SunPower integration with sample data. +This script uses the sample device_list.json to test integration logic +without requiring a real PVS connection. +""" + +import json +import os +import sys + +# Add the custom_components directory to Python path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "custom_components")) + +from unittest.mock import Mock # noqa: E402 + +from sunpower import ( # noqa: E402 + convert_sunpower_data, + sunpower_fetch, +) +from sunpower.sunpower import SunPowerMonitor # noqa: E402 + + +def load_sample_data(): + """Load the sample device list data.""" + sample_file = os.path.join(os.path.dirname(__file__), "samples", "device_list.json") + + if not os.path.exists(sample_file): + print("โŒ Sample file not found: samples/device_list.json") + return None + + with open(sample_file, "r") as f: + return json.load(f) + + +def test_data_processing(): + """Test data processing with sample data.""" + print("๐Ÿงช Testing SunPower Integration with Sample Data") + print("=" * 50) + + # Load sample data + sample_data = load_sample_data() + if not sample_data: + return + + print(f"โœ… Loaded sample data with {len(sample_data.get('devices', []))} devices") + + print("\n1. Testing data conversion...") + + # Test data conversion + converted_data = convert_sunpower_data(sample_data) + print("โœ… Data conversion successful") + print(f"๐Ÿ“Š Device types found: {list(converted_data.keys())}") + + # Show detailed breakdown + print("\n2. Device breakdown:") + total_devices = 0 + for device_type, devices in converted_data.items(): + count = len(devices) + total_devices += count + print(f" - {device_type}: {count} devices") + + if devices: + # Show sample device info + sample_device = next(iter(devices.values())) + print(f" Serial example: {sample_device.get('SERIAL', 'Unknown')}") + print(f" Model example: {sample_device.get('MODEL', 'Unknown')}") + print(f" State example: {sample_device.get('STATE', 'Unknown')}") + + # Show available fields + field_count = len(sample_device.keys()) + sample_fields = list(sample_device.keys())[:6] # First 6 fields + print(f" Fields ({field_count} total): {sample_fields}") + print() + + print(f"๐Ÿ“ˆ Total devices processed: {total_devices}") + + print("\n3. Testing sensor mapping...") + + # Test sensor mapping by checking if key fields are available + from sunpower.const import ( # noqa: E402 + SUNPOWER_SENSORS, + SUNVAULT_SENSORS, + ) + + # Combine all sensors + all_sensors = {**SUNPOWER_SENSORS} + if any("ESS" in device_type for device_type in converted_data.keys()): + all_sensors.update(SUNVAULT_SENSORS) + print("โœ… ESS devices detected - SunVault sensors will be included") + else: + print("โ„น๏ธ No ESS devices - SunVault sensors not needed") + + # Check sensor field availability + sensor_compatibility = {} + for device_type, type_config in all_sensors.items(): + if device_type in converted_data: + devices = converted_data[device_type] + if devices: + sample_device = next(iter(devices.values())) + sensors = type_config["sensors"] + + available_fields = [] + missing_fields = [] + + for _sensor_name, sensor_config in sensors.items(): + field = sensor_config["field"] + if field in sample_device: + available_fields.append(field) + else: + missing_fields.append(field) + + sensor_compatibility[device_type] = { + "available": len(available_fields), + "missing": len(missing_fields), + "total": len(sensors), + "missing_fields": missing_fields[:3], # Show first 3 missing + } + + print("\n4. Sensor field compatibility:") + for device_type, stats in sensor_compatibility.items(): + available = stats["available"] + total = stats["total"] + percentage = (available / total * 100) if total > 0 else 0 + print(f" - {device_type}: {available}/{total} fields available ({percentage:.1f}%)") + + if stats["missing"] > 0: + missing_sample = stats["missing_fields"] + print(f" Missing examples: {missing_sample}") + + print("\n5. Testing virtual meter creation...") + + # Check if virtual meter was created + if "Power Meter" in converted_data: + meters = converted_data["Power Meter"] + virtual_meters = [m for serial, m in meters.items() if m.get("origin") == "virtual"] + + if virtual_meters: + print(f"โœ… Virtual meter created: {len(virtual_meters)} virtual meter(s)") + vm = virtual_meters[0] + print(f" - Serial: {vm.get('SERIAL')}") + print(f" - Type: {vm.get('TYPE')}") + print(f" - Model: {vm.get('MODEL')}") + print(f" - Power: {vm.get('p_3phsum_kw', 'N/A')} kW") + print(f" - Energy: {vm.get('net_ltea_3phsum_kwh', 'N/A')} kWh") + else: + print("โš ๏ธ No virtual meter found") + else: + print("โš ๏ธ No power meters found") + + print("\n6. Testing mock data fetch...") + + # Create a mock monitor that returns our sample data + mock_monitor = Mock(spec=SunPowerMonitor) + mock_monitor.device_list.return_value = sample_data + mock_monitor.energy_storage_system_status.return_value = {"result": "sample"} + + # Test the sunpower_fetch function + entry_id = "test_sample_entry" + try: + fetch_data = sunpower_fetch(mock_monitor, 120, 60, entry_id) + + if fetch_data: + print("โœ… Data fetch function successful") + print(f"๐Ÿ“Š Fetched data keys: {list(fetch_data.keys())}") + + # Compare with direct conversion + total_devices_fetch = sum(len(devices) for devices in fetch_data.values()) + print(f"๐Ÿ“ˆ Total devices from fetch: {total_devices_fetch}") + + if total_devices_fetch == total_devices: + print("โœ… Data consistency check passed") + else: + print("โš ๏ธ Data count mismatch between methods") + else: + print("โŒ Data fetch returned None") + + except Exception as e: + print(f"โŒ Data fetch failed: {e}") + + print("\n๐ŸŽ‰ Sample data testing completed!") + print("\n๐Ÿ’ก This validates that:") + print(" - Data conversion logic works correctly") + print(" - Sensor field mapping is compatible") + print(" - Virtual meter creation functions") + print(" - Core integration logic is sound") + + +if __name__ == "__main__": + test_data_processing()