Version 2.0.0 | Node.js 20 LTS | TypeScript 5.9.3 | Zod v4
Bridge Sungrow Winet/WinetβS/WinetβS2 gateways to Home Assistant using MQTT Discovery.
For detailed troubleshooting notes and register history see DIAGNOSTICS.md.
- What it is: A Node.js/TypeScript service that connects to Winet devices over WebSocket, polls live metrics, and publishes HAβcompatible MQTT topics.
- Why: Autoβcreate sensors in Home Assistant without cloud dependencies.
- How: WebSocket to Winet β parse/normalize β MQTT publish (config + state).
- π Multi-inverter support with automatic device discovery
- οΏ½ Modbus TCP integration for smart meter data (grid import/export energy)
- οΏ½π SSL/TLS security with flexible certificate validation
- π MQTT authentication support
- π 29+ sensor types per inverter (power, voltage, current, temperature, etc.)
- π‘οΈ Robust error handling with graceful recovery
- β‘ Performance optimized with efficient data structures
- π·οΈ Friendly sensor naming - "Daily Yield (inverter_1)" format
- π³ Docker support with Node.js 20 LTS
- π¦ Modern stack - TypeScript 5.9.3, Zod v4, latest dependencies
- β‘ Energy Dashboard ready - automatic device_class and state_class configuration
This project is built upon the excellent foundation created by nickstallman in his original winet-extractor project.
Special thanks to Nick for:
- Creating the original WebSocket communication protocol with Winet devices
- Developing the initial MQTT Home Assistant integration
- Establishing the core architecture for multi-device support
- Providing the foundation that made this enhanced version possible
This project represents a complete TypeScript rewrite and enhancement of Nick's original work, with performance optimizations, improved error handling, and expanded features while maintaining the core functionality he pioneered.
Note: This enhanced version was developed with the assistance of AI tools to improve codebase quality, add TypeScript support, and implement performance optimizations.
Create a .env file with minimal variables:
# Single inverter setup
WINET_HOST=192.168.1.114
MQTT_URL=mqtt://192.168.1.101:1883
INVERTER_TYPE=STRING # or HYBRID
# Optional: Modbus smart meter (leave blank if none)
MODBUS_IPS=
# Optional: MQTT authentication
MQTT_USER=your_mqtt_user
MQTT_PASS=your_mqtt_password
# Optional: Custom inverter credentials (default: admin/pw8888)
WINET_USER=admin
WINET_PASS=pw8888# Build and run with Docker
docker build -t winet2 .
docker run -d --name winet2-bridge --restart unless-stopped --env-file .env winet2
# Or use docker-compose
docker-compose up -dnpm install
npm run compile
npm run cli # Runs the compiled applicationHome Assistant will auto-discover entities via MQTT Discovery under homeassistant/sensor/... with friendly names like:
- "Daily Yield (inverter_1)"
- "Total Active Power (inverter_1)"
- "Phase A Voltage (inverter_2)"
- Confirm each inverter is reachable on the network (ping or web UI).
- Decide whether the site is STRING or HYBRID and set
INVERTER_TYPEaccordingly. - If a Sungrow smart meter is installed, identify which inverter it is wired to and place that IP in the matching slot of
MODBUS_IPS. - Test MQTT credentials by publishing a sample message (optional but helpful for first-time users).
- Start the bridge (
docker compose up --build -d) and watchdocker compose logs --tail 200forπ Modbus meter dataentries.
Set a comma-separated list of hosts via WINET_HOSTS:
# Multiple inverter setup
WINET_HOSTS=192.168.1.10,192.168.1.11,192.168.1.12
WINET_NAMES=inverter_1,inverter_2,inverter_3
MQTT_URL=mqtt://192.168.1.101:1883
INVERTER_TYPE=STRING # applies to all inverters unless you run separate containers
# Modbus smart meter mapping (blank entry means no meter)
MODBUS_IPS=,192.168.1.12, # Only inverter_2 has the chained smart meter
# Optional: Customize discovery prefix
HA_PREFIX=homeassistant/sensorFeatures:
- β‘ Staggered startup prevents network congestion
- π Independent connections per inverter
- π Unique sensor paths prevent conflicts
- π·οΈ Custom naming for easy identification
Smart Meter Pairing (String Inverters)
- Sungrow smart meters daisy-chain to a single inverter on the RS485 bus.
- In the
.envfile,MODBUS_IPSmust list an entry for every inverter inWINET_HOSTS.- Use the meter host (typically the inverter IP) in the slot for the inverter that owns the meter.
- Leave the other slots blank (
'').
- Example: if the meter is wired to
192.168.1.12, setMODBUS_IPS=,192.168.1.12so the second inverter supplies meter data for the whole array. - Only that inverter will expose
meter_power,grid_import_energy, andgrid_export_energy.
# Enable SSL (auto-detects if required)
SSL=true
# Security levels (choose one)
SSL_VALIDATION=bypass # Default - maximum compatibility
SSL_VALIDATION=pinned # Enhanced security with certificate pinning
SSL_VALIDATION=strict # Maximum security (may fail with self-signed certs)
# Certificate pinning (for enhanced security)
INVERTER_CERT_FINGERPRINTS=sha256:A1:B2:C3...,sha256:D4:E5:F6...| Level | Security | Compatibility | Use Case |
|---|---|---|---|
bypass |
β Works with all inverters | Default, trusted networks | |
pinned |
π High | β‘ Works with known certificates | Enhanced security |
strict |
π Maximum | β May fail with self-signed | Maximum security environments |
π‘ Tip: Use ./quick-ssl-check.sh to analyze your inverter certificates and get pinning recommendations.
- Out of the box the app loads register defaults from
tools/modbus-discovery/modbus-register-defaults.json(built from Sungrow CSVs). - To verify or regenerate registers on your inverter:
npm run build:register-defaultsthennode tools/modbus-discovery/discover.js. - The discovery CLI now asks for inverter type (
1=String,2=Hybrid) and rewritesmodbus-registers.jsonwith validated overrides.
# Required
WINET_HOST=192.168.1.114 # Single inverter IP/hostname
WINET_HOSTS=192.168.1.10,192.168.1.11 # Multi-inverter (overrides WINET_HOST)
MQTT_URL=mqtt://192.168.1.101:1883 # MQTT broker URL
INVERTER_TYPE=STRING # Register defaults: STRING or HYBRID
# Authentication
MQTT_USER=mqtt_username # MQTT authentication (optional)
MQTT_PASS=mqtt_password # MQTT password (optional)
WINET_USER=admin # Inverter username (default: admin)
WINET_PASS=pw8888 # Inverter password (default: pw8888)
# Modbus TCP (optional - for smart meter data)
MODBUS_IPS=,192.168.1.12 # One entry per inverter matching WINET_HOSTS
# Use the meter owner's IP; leave others blank
# Enables meter_power/grid_import/grid_export on that inverter
# Customization
WINET_NAMES=House,Garage,Shed # Custom inverter names (optional)
HA_PREFIX=homeassistant/sensor # MQTT discovery prefix (default)
POLL_INTERVAL=10 # Polling interval in seconds (default: 10)
# Privacy & System
ANALYTICS=false # Disable telemetry (default: false)
SINGLE_PHASE_SYSTEM=true # Hide 3-phase sensors (optional)
# SSL/TLS Security
SSL=true # Enable SSL (auto-detected if needed)
SSL_VALIDATION=bypass # Security level: bypass/pinned/strict
INVERTER_CERT_FINGERPRINTS=sha256:A1:B2... # Certificate pinning (pinned mode)Create /data/options.json for Home Assistant add-on compatibility:
{
"winet_hosts": ["192.168.1.10", "192.168.1.11"],
"mqtt_url": "mqtt://192.168.1.101:1883",
"mqtt_user": "username",
"mqtt_pass": "password",
"poll_interval": "10",
"analytics": false,
"ssl": true
}- Node.js: 20 LTS (Alpine Linux in Docker)
- TypeScript: 5.9.3
- Dependencies: Modern stack (zod v4, winston, mqtt, ws)
- Memory: ~50MB typical usage
- CPU: Minimal (polling every 10 seconds)
- β Sungrow SG50RS (Type 21) - REAL + DIRECT stages
- β Winet-S/Winet-S2 devices with firmware variations
- β Auto-detection of device capabilities and sensor types
- π Power: AC/DC power, MPPT power per string
- β‘ Electrical: Voltage, current, frequency, power factor
- π‘οΈ Temperature: Inverter internal temperature
- π Energy: Daily/total energy production
- βοΈ Grid: Import/export power and energy
- π Battery: Status, voltage, current (if equipped)
Connection Refused
# Check if inverter is reachable
ping 192.168.1.114
# Test HTTP endpoint
curl -k http://192.168.1.114/i18n/en_US.propertiesSSL Certificate Errors
# Run SSL analysis
./quick-ssl-check.sh
# Or use bypass mode temporarily
SSL_VALIDATION=bypassMQTT Authentication Failed
# Test MQTT connection
mosquitto_pub -h your-mqtt-host -u username -P password -t test -m "hello"# Create .env file with your configuration
cat > .env << EOF
WINET_HOSTS=192.168.1.114,192.168.1.12
WINET_NAMES=inverter_1,inverter_2
MQTT_URL=mqtt://192.168.1.101:1883
EOF
# Build and deploy
docker build -t winet2 .
docker run -d --name winet2-bridge --restart unless-stopped --env-file .env winet2version: '3.8'
services:
winet2:
build: .
container_name: winet2-bridge
restart: unless-stopped
env_file: .env
network_mode: host # Required for inverter discoveryThe container includes built-in health monitoring:
# Check container health
docker ps | grep winet2
# Should show "healthy" status
# View logs
docker logs winet2-bridge --tail 50Sungrow Winet Device β WebSocket β Node.js Service β MQTT β Home Assistant
| Module | Responsibility |
|---|---|
index.ts |
Bootstrap, configuration loading, component orchestration |
winetHandler.ts |
WebSocket client, device discovery, polling stages, watchdog |
homeassistant.ts |
MQTT publisher, HA Discovery, unit normalization |
getProperties.ts |
i18n label fetching with HTTP/HTTPS fallback |
sslConfig.ts |
SSL/TLS certificate validation |
types/* |
Zod schemas, device type mappings, HA class definitions |
- Configuration Loading - Environment variables or JSON config
- i18n Properties Fetching - Human-readable labels from device
- WebSocket Connection - Authentication and device discovery
- Polling & Data Processing - Continuous metrics collection with validation
- MQTT Publishing - Home Assistant Discovery configs and live state data
This project is based on winet-extractor by nickstallman, which provided the foundational WebSocket communication protocol and MQTT integration for Sungrow WiNet devices.
- Converted from JavaScript to TypeScript for improved type safety and developer experience
- Full type definitions for all data structures, device schemas, and API responses
- Zod v4 schema validation for runtime type checking and data validation
- Enhanced IntelliSense and compile-time error detection
- Node.js 20 LTS runtime with Alpine Linux containers
- Modular design with separated concerns:
getProperties.ts- HTTP requests and i18n property loadingwinetHandler.ts- WebSocket communication and device managementhomeassistant.ts- MQTT publishing and Home Assistant discoverysslConfig.ts- SSL/TLS certificate validationanalytics.ts- Usage tracking and error reporting
- Set-based lookups replacing O(n) array operations with O(1) performance
- Map-based device type caching for efficient device stage lookup
- Batch processing for MQTT operations and status updates
- Memory leak prevention with proper timer cleanup and resource management
- Optimized data structures reducing object allocations and GC pressure
- Global exception handlers preventing application crashes
- Graceful reconnection logic with exponential backoff
- Comprehensive logging with Winston logger integration
- Watchdog mechanisms for detecting and recovering from stuck operations
- Flexible SSL/TLS validation with bypass, pinned, and strict modes
- Certificate fingerprint pinning for enhanced security
- Secure credential handling with environment variable validation
- MQTT authentication support with user/password credentials
- Enhanced discovery protocol with proper device classification
- Automatic sensor configuration with appropriate device classes
- Improved sensor naming - "Sensor Name (Device)" format for clarity
- Individual sensor topics matching Home Assistant best practices
- State management with dirty flag tracking for efficient updates
- Retention policies and optimal publishing strategies
- Google TypeScript Style (GTS) for consistent code formatting
- Comprehensive linting with ESLint and Prettier integration
- Build automation with TypeScript compilation and validation
- Development tooling for testing and debugging
This enhanced version was developed with a focus on code quality, maintainability, and expanded features, building on the original foundation.
- β Node.js 20 LTS (upgraded from 18)
- β TypeScript 5.9.3 (latest stable)
- β Zod v4 (schema validation with performance improvements)
- β All dependencies updated to latest stable versions
- β Better sensor naming - "Daily Yield (inverter_1)" format
- β Cleaner MQTT topics - individual sensor topics for better HA integration
- β Enhanced Docker support with multi-stage builds
- β Comprehensive cleanup - removed unused dependencies (yargs)
- β Modern linting with Google TypeScript Style (GTS) v6
- β Type safety improvements with latest TypeScript features
- β Performance optimizations across all modules
- β Clean codebase - no technical debt or redundant code
- Original concept and protocol: Nick Stallman
- TypeScript rewrite and enhancements: Nathan Kissick (with AI assistance)
- All improvements maintain backward compatibility with Nick's original configuration format
This project builds upon and extends the work of Nick Stallman's original winet-extractor. Please ensure to respect any licensing terms from the original project when using this enhanced version.
This app supports dynamic mapping of Modbus registers for maximum compatibility with different Sungrow inverter models and firmware versions.
- On first setup (or after a firmware update), run the Modbus register discovery tool:
node tools/modbus-discovery/discover.js- The tool will prompt for your inverter IP, port, slave ID, scan range, and current meter values (from your inverter's web UI).
- It will scan the specified register range and auto-match values to your readings.
- Discovered register addresses are saved to
modbus-registers.json. - The main app will use these addresses for Modbus polling.
MODBUS_DISCOVERY_ON_START=true|falseβ Run register scan on startup (default: false)MODBUS_SCAN_START=5000β Start of scan range (default: 5000)MODBUS_SCAN_END=5700β End of scan range (default: 5700)
You can set these in your .env file to control scanning behavior and range. For advanced troubleshooting, you may scan the full range (0β65535), but this is slower and not usually necessary.
You can always run the discovery tool manually to update register mappings:
node tools/modbus-discovery/discover.jsScanning the full Modbus register range may take several minutes and could stress the inverter. Use the default ranges unless you have a special need.
If you change inverter models or update firmware and your sensors stop working, re-run the discovery tool and update your config.