duosida-ev 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,53 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ .venv/
25
+ venv/
26
+ ENV/
27
+ env/
28
+
29
+ # IDE
30
+ .idea/
31
+ .vscode/
32
+ *.swp
33
+ *.swo
34
+ *~
35
+
36
+ # OS
37
+ .DS_Store
38
+ Thumbs.db
39
+
40
+ # Testing
41
+ .coverage
42
+ .pytest_cache/
43
+ htmlcov/
44
+
45
+ # Build
46
+ *.manifest
47
+ *.spec
48
+
49
+ # Claude Code
50
+ .claude/
51
+
52
+ # uv
53
+ uv.lock
@@ -0,0 +1,45 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.1] - 2024-11-19
9
+
10
+ ### Fixed
11
+
12
+ - Updated README.md to use GitHub repository URL instead of git.dias.pt
13
+
14
+ ## [0.1.0] - 2024-11-18
15
+
16
+ ### Added
17
+
18
+ - **Network Discovery**: UDP broadcast discovery of Duosida chargers on local network
19
+ - **Direct TCP Communication**: Bypass cloud API for local control
20
+ - **Real-time Telemetry**: Read voltage, current, power, temperature, energy consumption
21
+ - **Charging Control**:
22
+ - Start/stop charging sessions
23
+ - Set maximum charging current (6-32A)
24
+ - **Configuration Settings**:
25
+ - Connection timeout (30-900s)
26
+ - Max/min voltage limits (265-290V / 70-110V)
27
+ - Max temperature (85-95°C)
28
+ - Direct work mode (plug and charge)
29
+ - LED brightness (0=off, 1=low, 3=high)
30
+ - **JSON Export**: Get telemetry data as JSON for integration
31
+ - **CLI Tool**: Full-featured command-line interface with auto-discovery
32
+ - **Proper Logging**: Python logging module with verbose mode
33
+ - **Custom Exceptions**: DuosidaError hierarchy for error handling
34
+ - **3-Phase Support**: Read voltage/current for all phases
35
+
36
+ ### Technical Details
37
+
38
+ - Protocol: Protobuf over TCP port 9988
39
+ - Discovery: UDP broadcast on port 48890/48899
40
+ - 46 unit tests
41
+ - Python 3.6+ compatible
42
+ - No external dependencies (stdlib only)
43
+
44
+ [0.1.1]: https://github.com/americodias/duosida-ev/releases/tag/v0.1.1
45
+ [0.1.0]: https://github.com/americodias/duosida-ev/releases/tag/v0.1.0
@@ -0,0 +1,120 @@
1
+ # Duosida EV Charger - Direct TCP Control
2
+
3
+ ## Project Overview
4
+
5
+ Python library for direct TCP communication with Duosida EV wall chargers, bypassing the cloud API. Provides real-time telemetry and control via the charger's native protobuf protocol.
6
+
7
+ ## Package Structure
8
+
9
+ - **src/duosida_ev/charger.py** - Main `DuosidaCharger` class
10
+ - **src/duosida_ev/cli.py** - Command-line interface
11
+ - **src/duosida_ev/discovery.py** - Network discovery
12
+ - **src/duosida_ev/exceptions.py** - Custom exceptions
13
+
14
+ ## Usage
15
+
16
+ ```python
17
+ from duosida_ev import DuosidaCharger, discover_chargers
18
+
19
+ # Discover chargers
20
+ devices = discover_chargers()
21
+ for device in devices:
22
+ print(f"Found: {device['ip']} - {device['device_id']}")
23
+
24
+ # Connect and get status
25
+ charger = DuosidaCharger(
26
+ host="192.168.1.100",
27
+ device_id="YOUR_DEVICE_ID"
28
+ )
29
+
30
+ charger.connect()
31
+ status = charger.get_status()
32
+ print(status)
33
+ charger.disconnect()
34
+ ```
35
+
36
+ ### CLI Commands
37
+
38
+ ```bash
39
+ # Auto-discover and get status
40
+ duosida status
41
+
42
+ # Get status with JSON output
43
+ duosida status --json
44
+
45
+ # Set max current (6-32A)
46
+ duosida set-current 16
47
+
48
+ # Start/stop charging
49
+ duosida start
50
+ duosida stop
51
+
52
+ # Monitor continuously
53
+ duosida monitor
54
+
55
+ # Configuration settings
56
+ duosida set-timeout 120 # 30-900 seconds
57
+ duosida set-max-temp 90 # 85-95°C
58
+ duosida set-max-voltage 280 # 265-290V
59
+ duosida set-min-voltage 90 # 70-110V
60
+ duosida set-direct-mode on # on/off
61
+ duosida set-led-brightness 3 # 0=off, 1=low, 3=high
62
+ ```
63
+
64
+ ## Telemetry Fields
65
+
66
+ | Field | Source | Description |
67
+ |-------|--------|-------------|
68
+ | voltage | Field 1 | Line voltage (V) |
69
+ | current | Field 2 | Charging current (A) |
70
+ | current2 | Field 15 | Secondary/average current (A) |
71
+ | temperature_station | Field 8 | Station temperature (°C) |
72
+ | temperature_internal | Field 7 | Internal temperature (°C) |
73
+ | conn_status | Field 17 | Connection status (0-6) |
74
+ | today_consumption | Field 20 | Daily energy (kWh) |
75
+ | session_energy | Field 9 | Session energy (kWh) |
76
+ | timestamp | Field 18 | Reading timestamp |
77
+ | power | Calculated | V × I (W) |
78
+
79
+ ## Status Codes
80
+
81
+ Based on [official HA integration](https://github.com/jello1974/duosidaEV-home-assistant):
82
+
83
+ - 0: Available
84
+ - 1: Preparing
85
+ - 2: Charging
86
+ - 3: Cooling
87
+ - 4: SuspendedEV
88
+ - 5: Finished
89
+ - 6: Holiday
90
+
91
+ ## Protocol Details
92
+
93
+ - **Port**: 9988 (TCP)
94
+ - **Discovery**: UDP broadcast on port 48890/48899
95
+ - **Message format**: Protobuf with nested fields
96
+ - **Telemetry path**: Field 16 → Field 10 (DataVendorStatusReq)
97
+
98
+ ## Dependencies
99
+
100
+ - Python 3.6+
101
+ - No external dependencies (uses only stdlib)
102
+
103
+ ## Development Environment
104
+
105
+ ```bash
106
+ # Activate the virtual environment
107
+ source .venv/bin/activate
108
+
109
+ # Run tests
110
+ python -m pytest tests/
111
+
112
+ # Install in development mode
113
+ pip install -e .
114
+ ```
115
+
116
+ ## Notes
117
+
118
+ - Device ID can be found via discovery or in the official Duosida app
119
+ - CLI auto-discovers charger if --host/--device-id not provided
120
+ - Use -v/--verbose for debug output
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Americo Dias
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,340 @@
1
+ Metadata-Version: 2.4
2
+ Name: duosida-ev
3
+ Version: 0.1.1
4
+ Summary: Python library for direct TCP control of Duosida EV wall chargers
5
+ Project-URL: Homepage, https://github.com/americodias/duosida-ev
6
+ Project-URL: Repository, https://github.com/americodias/duosida-ev
7
+ Project-URL: Issues, https://github.com/americodias/duosida-ev/issues
8
+ Author-email: Americo Dias <americo.dias@gmail.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: charger,duosida,electric-vehicle,ev,iot,smart-home
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.6
18
+ Classifier: Programming Language :: Python :: 3.7
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Topic :: Home Automation
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Requires-Python: >=3.6
27
+ Description-Content-Type: text/markdown
28
+
29
+ # duosida-ev
30
+
31
+ Python library for direct TCP control of Duosida EV wall chargers, bypassing the cloud API for local control and monitoring.
32
+
33
+ ## Features
34
+
35
+ - **Network Discovery**: Find Duosida chargers on your local network
36
+ - **Real-time Telemetry**: Read voltage, current, power, temperature, and energy consumption
37
+ - **Current Control**: Set maximum charging current (6-32A)
38
+ - **Start/Stop Charging**: Remote control to start or stop charging sessions
39
+ - **Configuration Settings**: Connection timeout, max/min voltage, max temperature, direct work mode
40
+ - **JSON Export**: Get telemetry data as JSON for integration with other tools
41
+ - **No Cloud Required**: Direct TCP communication with the charger
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ pip install duosida-ev
47
+ ```
48
+
49
+ Or install from source:
50
+
51
+ ```bash
52
+ git clone https://github.com/americodias/duosida-ev.git
53
+ cd duosida-ev
54
+ pip install -e .
55
+ ```
56
+
57
+ ## Quick Start
58
+
59
+ ### Discover Chargers
60
+
61
+ ```python
62
+ from duosida_ev import discover_chargers
63
+
64
+ devices = discover_chargers()
65
+ for device in devices:
66
+ print(f"Found: {device['ip']}")
67
+ print(f" Device ID: {device['device_id']}")
68
+ print(f" MAC: {device['mac']}")
69
+ ```
70
+
71
+ ### Get Status
72
+
73
+ ```python
74
+ from duosida_ev import DuosidaCharger
75
+
76
+ charger = DuosidaCharger(
77
+ host="192.168.1.100",
78
+ device_id="YOUR_DEVICE_ID"
79
+ )
80
+
81
+ charger.connect()
82
+ status = charger.get_status()
83
+ print(f"Voltage: {status.voltage}V")
84
+ print(f"Current: {status.current}A")
85
+ print(f"Power: {status.power}W")
86
+ print(f"State: {status.state}")
87
+ charger.disconnect()
88
+ ```
89
+
90
+ ### Set Maximum Current
91
+
92
+ ```python
93
+ charger.connect()
94
+ charger.set_max_current(16) # Set to 16A
95
+ charger.disconnect()
96
+ ```
97
+
98
+ ### Configure Charger Settings
99
+
100
+ ```python
101
+ charger.connect()
102
+
103
+ # Connection timeout (30-900 seconds)
104
+ charger.set_connection_timeout(120)
105
+
106
+ # Temperature limits (85-95°C)
107
+ charger.set_max_temperature(90)
108
+
109
+ # Voltage limits
110
+ charger.set_max_voltage(280) # 265-290V
111
+ charger.set_min_voltage(90) # 70-110V
112
+
113
+ # Direct work mode (plug and charge)
114
+ charger.set_direct_work_mode(True) # Enable
115
+ charger.set_direct_work_mode(False) # Disable
116
+
117
+ # LED brightness (0=off, 1=low, 3=high)
118
+ charger.set_led_brightness(3)
119
+
120
+ # Generic config (any key/value)
121
+ charger.set_config("VendorMaxWorkCurrent", "16")
122
+
123
+ charger.disconnect()
124
+ ```
125
+
126
+ ### Start/Stop Charging
127
+
128
+ ```python
129
+ charger.connect()
130
+
131
+ # Start charging
132
+ charger.start_charging()
133
+
134
+ # Stop charging
135
+ charger.stop_charging()
136
+
137
+ charger.disconnect()
138
+ ```
139
+
140
+ ### Get Telemetry as JSON
141
+
142
+ ```python
143
+ import json
144
+
145
+ charger.connect()
146
+ status = charger.get_status()
147
+
148
+ # Convert to dictionary
149
+ data = status.to_dict()
150
+ print(json.dumps(data, indent=2))
151
+
152
+ # Access individual fields
153
+ print(f"CP Voltage: {status.cp_voltage}V")
154
+ print(f"State: {status.state}")
155
+
156
+ charger.disconnect()
157
+ ```
158
+
159
+ ### Monitor Continuously
160
+
161
+ ```python
162
+ def on_status(status):
163
+ print(f"Power: {status.power}W")
164
+
165
+ charger.connect()
166
+ charger.monitor(interval=2.0, callback=on_status)
167
+ charger.disconnect()
168
+ ```
169
+
170
+ ## Command Line Interface
171
+
172
+ ```bash
173
+ # Discover chargers on the network
174
+ duosida discover
175
+
176
+ # Get charger status
177
+ duosida status --host 192.168.1.100 --device-id YOUR_DEVICE_ID
178
+
179
+ # Get status in JSON format
180
+ duosida status --host 192.168.1.100 --device-id YOUR_DEVICE_ID --json
181
+
182
+ # Set maximum current
183
+ duosida set-current --host 192.168.1.100 --device-id YOUR_DEVICE_ID 16
184
+
185
+ # Start charging
186
+ duosida start --host 192.168.1.100 --device-id YOUR_DEVICE_ID
187
+
188
+ # Stop charging
189
+ duosida stop --host 192.168.1.100 --device-id YOUR_DEVICE_ID
190
+
191
+ # Monitor continuously
192
+ duosida monitor --host 192.168.1.100 --device-id YOUR_DEVICE_ID
193
+
194
+ # Configuration commands (host only - device ID auto-discovered)
195
+ duosida set-timeout --host 192.168.1.100 120 # 30-900 seconds
196
+ duosida set-max-temp --host 192.168.1.100 90 # 85-95°C
197
+ duosida set-max-voltage --host 192.168.1.100 280 # 265-290V
198
+ duosida set-min-voltage --host 192.168.1.100 90 # 70-110V
199
+ duosida set-direct-mode --host 192.168.1.100 on # on/off
200
+ duosida set-led-brightness --host 192.168.1.100 3 # 0=off, 1=low, 3=high
201
+ ```
202
+
203
+ ## Telemetry Fields
204
+
205
+ | Field | Description |
206
+ |-------|-------------|
207
+ | `voltage` | Line voltage (V) |
208
+ | `current` | Charging current (A) |
209
+ | `power` | Power consumption (W) |
210
+ | `temperature_station` | Station temperature (°C) |
211
+ | `state` | Connection status (Available, Charging, Finished, etc.) |
212
+ | `today_consumption` | Daily energy consumption (kWh) |
213
+ | `session_energy` | Current session energy (kWh) |
214
+
215
+ ## Status Codes
216
+
217
+ | Code | State |
218
+ |------|-------|
219
+ | 0 | Available |
220
+ | 1 | Preparing |
221
+ | 2 | Charging |
222
+ | 3 | Cooling |
223
+ | 4 | SuspendedEV |
224
+ | 5 | Finished |
225
+ | 6 | Holiday |
226
+
227
+ ## Finding Your Device ID
228
+
229
+ The device ID can be found:
230
+ - On the QR code label on the left side of the charger
231
+ - Using the `duosida discover` command (when on the same network)
232
+ - In the official Duosida mobile app
233
+ - In Home Assistant integration settings
234
+
235
+ ## Requirements
236
+
237
+ - Python 3.6+
238
+ - No external dependencies (uses only standard library)
239
+
240
+ ## Protocol Details
241
+
242
+ - **Port**: 9988 (TCP)
243
+ - **Message Format**: Protobuf
244
+ - **Discovery**: UDP broadcast on port 48890/48899
245
+
246
+ See [Settings Reference](docs/SETTINGS.md) for detailed documentation on charger configuration options (Direct Work Mode, Level Detection, etc.).
247
+
248
+ ## Capturing Network Traffic
249
+
250
+ To capture traffic between the Duosida app and charger for reverse engineering, you can use Wireshark on a computer connected to the same network, or use tcpdump on your router if it supports SSH access.
251
+
252
+ ### Using Wireshark
253
+
254
+ 1. Install [Wireshark](https://www.wireshark.org/)
255
+ 2. Connect your computer to the same network as the charger
256
+ 3. Start capture with filter: `host <charger-ip> and tcp port 9988`
257
+ 4. Use the Duosida app to interact with the charger
258
+ 5. Stop capture and save the `.pcap` file
259
+
260
+ ### Using tcpdump on Router
261
+
262
+ If your router supports SSH (OpenWrt, DD-WRT, etc.):
263
+
264
+ ```bash
265
+ ssh root@<router-ip>
266
+
267
+ # Capture traffic to/from charger
268
+ tcpdump -i br-lan -w /tmp/charger_capture.pcap \
269
+ 'host <charger-ip> and tcp port 9988'
270
+ ```
271
+
272
+ Download the capture:
273
+
274
+ ```bash
275
+ scp root@<router-ip>:/tmp/charger_capture.pcap ./charger_capture.pcap
276
+ ```
277
+
278
+ <details>
279
+ <summary>UniFi Dream Machine Pro specific instructions</summary>
280
+
281
+ For UDM Pro with separate access points, you need to SSH into the Access Point where the phone or charger is connected (not the UDM Pro itself, since local traffic may not pass through the router):
282
+
283
+ ```bash
284
+ # SSH into the Access Point (not the UDM Pro)
285
+ ssh root@<access-point-ip>
286
+
287
+ # Capture on the wireless interface (ra2) for your IoT VLAN
288
+ tcpdump -i ra2 -w /tmp/charger_capture.pcap \
289
+ 'host 192.168.1.100 and host 192.168.1.50 and tcp port 9988'
290
+ ```
291
+
292
+ Options:
293
+ - `-i ra2` - Interface for 2.4GHz/5GHz AP (wireless)
294
+ - `host 192.168.1.100` - Charger IP
295
+ - `host 192.168.1.50` - Phone IP (update as needed)
296
+
297
+ Alternative - capture everything for 30 seconds:
298
+
299
+ ```bash
300
+ timeout 30 tcpdump -i ra2 -w /tmp/charger_full.pcap \
301
+ 'host 192.168.1.100 and tcp port 9988'
302
+ ```
303
+
304
+ Download the capture from the AP:
305
+
306
+ ```bash
307
+ scp root@<access-point-ip>:/tmp/charger_capture.pcap ./charger_capture.pcap
308
+ ```
309
+
310
+ </details>
311
+
312
+ ## TODO
313
+
314
+ Features available in the [cloud API](https://github.com/jello1974/duosidaEV-home-assistant) but not yet implemented:
315
+
316
+ - [x] **Start/Stop Charging** - Remote control to start or stop charging session
317
+ - [x] **Level Detection** - CP voltage display based on charging state (IEC 61851-1)
318
+ - [x] **Direct Work Mode** - Toggle VendorDirectWorkMode setting (plug and charge)
319
+ - [x] **3-Phase Support** - Read voltage/current for L2 and L3 phases (voltage2, voltage3, current2, current3)
320
+ - [x] **Configuration Settings** - Connection timeout, max/min voltage, max temperature
321
+ - [x] **LED Brightness** - Adjust display brightness (0=off, 1=low, 3=high)
322
+ - [ ] **Charging Records** - Retrieve historical charging sessions
323
+ - [ ] **Accumulated Energy** - Total lifetime energy consumption (may be cloud-only)
324
+
325
+ ## License
326
+
327
+ MIT License - see [LICENSE](LICENSE) file for details.
328
+
329
+ ## Contributing
330
+
331
+ Contributions are welcome! Please feel free to submit a Pull Request.
332
+
333
+ ## Acknowledgments
334
+
335
+ This library was created by reverse-engineering the TCP communication between the official Duosida app (orange icon, local control) and the charger. Note that Duosida has two apps - the orange one uses direct local communication while the blue one uses the cloud API.
336
+
337
+ The reverse engineering and code development was mostly done using [Claude Code](https://claude.ai/claude-code) and [Genspark](https://www.genspark.ai/).
338
+
339
+ **References:**
340
+ - [Home Assistant Duosida integration](https://github.com/jello1974/duosidaEV-home-assistant) - Cloud API integration, used as reference for status codes and feature identification