Skip to content

Commit 30f96d6

Browse files
authored
Merge pull request #319 from open3e/variant_dids
Added handling for variant DIDs
2 parents 74f7bcf + 93ccbb2 commit 30f96d6

19 files changed

+10852
-1098
lines changed

README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
**_New: Complex addressing mode available for MQTT commands (listener mode)_**
1+
**_New: Handling of data points with length different from common data point via open3e_depictSystem_**
22

33
<BR>
44

@@ -24,7 +24,7 @@ You created your own extension based on open3e? Great! Please let us know! Just
2424
# Installation
2525
There is a [Video Tutorial](https://youtu.be/u_fkwtIARug) (German languge) available from CRYDTEAM - thank you very much for it! Find the according web site [here](https://crydteam.de/2025/04/27/viessmann-vx3-in-homeassistant/). The final 1/3 is related to Home Assistant, but the first part shows the complete installation process of open3e and hardware very vividly.
2626

27-
Hint: An installation guide is available also in [German language](https://github.com/open3e/open3e/wiki/030-Installation-und-Inbetriebnahme-von-open3E).
27+
For a **detailed step-by-step installation guide** (German language) see [Wiki, chapt. 030](https://github.com/open3e/open3e/wiki/030-Installation-und-Inbetriebnahme-von-open3E).
2828

2929
<br>
3030

@@ -57,9 +57,9 @@ https://github.com/open3e/open3e/wiki/030-Installation-und-Inbetriebnahme-von-op
5757
# Depict System
5858
In advance of first time starting the client and after every firmware update, run
5959

60-
open3e_depictSystem [-s]
60+
open3e_depictSystem -s
6161

62-
to scan the system and generate devices.json and Open3Edatapoints_678.py files.<br>
62+
to scan the system and generate devices.json and Open3Edatapoints_6yz.py files.<br>
6363
Use `open3e` with cmd line argument `-cnfg devices.json` afterwards.<br>
6464
Pls. make sure to use same working directory for `open3e` as used for running `open3e_depictSystem`.<br>
6565
By using the optional switch `-s` data files for simulation get created.
@@ -280,8 +280,8 @@ Use
280280
```
281281
open3e_dids2json
282282
```
283-
to convert common list of data points (Open3Edatapoints.py) to json format.
284-
A white list of writable data points is also created by this tool.
283+
to convert common list of data points (Open3Edatapoints.py, Open3EdatapointsVariants.py) to json format.
284+
This tool converts data points for use in the ioBroker adapter ioBroker.e3oncan. It is not used by open3e.
285285

286286
# For developers
287287

@@ -295,6 +295,9 @@ If you want to work on the codebase you can clone the repository and work in "ed
295295

296296
# Changelog
297297

298+
### 0.6.0 (2026-02-07)
299+
* Introduced list of data points (Open3EdatapointsVariants.py) with lengths different from common data points. Via open3e_depictSystem open3e can handle those device specific data points.
300+
298301
### 0.5.10 (2025-12-10)
299302
* Added support for data points 511-520
300303
* Added support for complex addressing mode for listener mode

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ version_file_template = 'git_ref = "{scm_version.node}"'
3131
# PEP 621 https://peps.python.org/pep-0621/
3232
[project]
3333
name = "open3e"
34-
version = "0.5.10"
34+
version = "0.6.0"
3535
# for automated versioning by pypa/setuptools_scm. see https://peps.python.org/pep-0621/#dynamic
3636
# dynamic = ["version"]
3737
description = "Connects to E3 (vcal, vdens, vx3...) controllers via CAN or DOIP (UDS)."

src/open3e/Open3E_depictSystem.py

Lines changed: 104 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,19 @@
3737
from open3e.Open3Edatapoints import *
3838
import open3e.Open3Eenums
3939

40+
from pathlib import Path
41+
import importlib.util
42+
43+
4044

4145
def main():
4246
# cob scan, default 0x680 to 0x6ff
4347
startcob = 0x680
4448
lastcob = 0x6ff
4549

46-
# did scan, default 256 to 3500
50+
# did scan, default 256 to 4000
4751
startdid = 256
48-
lastdid = 3500
52+
lastdid = 4000
4953

5054
# connection
5155
can = "can0"
@@ -249,7 +253,7 @@ def write_simul_datafile(lstdids:list, cobid:int, devprop:str):
249253
print("done.")
250254

251255

252-
def write_datapoints_file(lstdids:list, cobid:int, devprop:str):
256+
def write_datapoints_file(lstdids:list, cobid:int, devprop:str, dicvariants:dict):
253257
filename = "Open3Edatapoints_" + shex(cobid) + ".py"
254258
print(f"write datapoints file {filename} ...")
255259
with open(filename, "w") as file:
@@ -264,8 +268,10 @@ def write_datapoints_file(lstdids:list, cobid:int, devprop:str):
264268
genlen,idstr = did_info(did)
265269
if(dlen == genlen):
266270
sline += 'None,'
271+
elif(varitem := dicvariants.get(did, {}).get(dlen)):
272+
sline += varitem.getCodecString() + ','
267273
else:
268-
sline += 'RawCodec(' + str(dlen) + ', \"' + idstr + '\"),'
274+
sline += 'RawCodec(' + str(dlen) + ', \"' + idstr + '\"),'
269275
file.write(sline + '\n')
270276
file.write(' }\n')
271277
file.write('}\n')
@@ -305,6 +311,91 @@ def get_pycan_conn(can, ecurx:int, ecutx:int):
305311
return bus, conn
306312

307313

314+
def get_variants_dict(o3e_path) -> dict:
315+
module_name = "Open3EdatapointsVariants"
316+
# check for variants file
317+
source = Path(o3e_path, f"{module_name}.py")
318+
if not source.is_file():
319+
print(f"Info: no codec variants - {module_name}.py not found")
320+
return {}
321+
322+
try:
323+
# -------------------------------
324+
# 5. Modul dynamisch laden
325+
# -------------------------------
326+
spec = importlib.util.spec_from_file_location(module_name, source)
327+
variants_temp = importlib.util.module_from_spec(spec)
328+
spec.loader.exec_module(variants_temp)
329+
330+
# -------------------------------
331+
# 6. Zugriff auf dataIdentifiers
332+
# -------------------------------
333+
return variants_temp.dataIdentifiers["dids"]
334+
335+
except Exception as e:
336+
print(f"ERROR getting DID variants:\n {e}")
337+
return {}
338+
339+
340+
# def get_variants_dict(target_path) -> dict:
341+
# # check for variants file
342+
# source = Path(target_path, "Open3EdatapointsVariants.py")
343+
# if not source.is_file():
344+
# return {}
345+
346+
# try:
347+
# # -------------------------------
348+
# # 1. Datei einlesen
349+
# # -------------------------------
350+
# text = source.read_text(encoding="utf-8")
351+
352+
# # -------------------------------
353+
# # 2. Alle ' → " ersetzen
354+
# # -------------------------------
355+
# text = text.replace("'", '"')
356+
357+
# # -------------------------------
358+
# # 3. Alle DID/LEN-Ausdrücke in '...' einfassen
359+
# # Match: <did> : { <len> : ... }
360+
# # -------------------------------
361+
# pattern = re.compile(
362+
# r'(\d+\s*:\s*\{\s*\d+\s*:\s*)(.+?)(\s*\})',
363+
# re.DOTALL
364+
# )
365+
366+
# def repl(match):
367+
# prefix = match.group(1)
368+
# content = match.group(2).strip().rstrip(",")
369+
# suffix = match.group(3)
370+
# return f"{prefix}'{content}'{suffix}"
371+
372+
# text = pattern.sub(repl, text)
373+
374+
# # -------------------------------
375+
# # 4. Datei schreiben
376+
# # -------------------------------
377+
# module_name = ".variants_temp"
378+
# target = Path(target_path, f"{module_name}.py")
379+
# target.write_text(text, encoding="utf-8")
380+
# print(f"Geschrieben: {target}") #TEMP!
381+
382+
# # -------------------------------
383+
# # 5. Modul dynamisch laden
384+
# # -------------------------------
385+
# spec = importlib.util.spec_from_file_location(module_name, target)
386+
# variants_temp = importlib.util.module_from_spec(spec)
387+
# spec.loader.exec_module(variants_temp)
388+
389+
# # -------------------------------
390+
# # 6. Zugriff auf dataIdentifiers
391+
# # -------------------------------
392+
# return variants_temp.dataIdentifiers["dids"]
393+
394+
# except Exception as e:
395+
# print(f"ERROR getting DID variants:\n {e}")
396+
# return {}
397+
398+
308399
# +++++++++++++++++++++++++++++++++
309400
# Main
310401
# +++++++++++++++++++++++++++++++++
@@ -317,28 +408,31 @@ def get_pycan_conn(can, ecurx:int, ecutx:int):
317408
if(args.can != None):
318409
can = args.can
319410

320-
# peparations
411+
# ++ peparations +++++++
321412
dataIdentifiers = dict(open3e.Open3Edatapoints.dataIdentifiers["dids"])
322413
e3Devices = open3e.Open3Eenums.E3Enums['Devices']
323414

324415
open3e_path = os.path.split(open3e.Open3Eenums.__file__)[0]
325416
dicDidEnums = read_didenums(os.path.join(open3e_path, "DidEnums.txt"))
326417

327-
# scan ECUs/COB-IDs
418+
did_variants = get_variants_dict(open3e_path)
419+
420+
# ++ scan ECUs/COB-IDs +++++++
328421
lstEcus = scan_cobs(startcob, lastcob)
329422

330-
# generate devices.json
423+
# ++ generate devices.json +++++++
331424
write_devices_json(lstEcus)
332425

333-
# scan dids of each responding ECU
426+
# ++ scan dids of each responding ECU +++++++
334427
for cob,prop in lstEcus:
335428
lstdids = scan_dids(cob, startdid, lastdid)
336429
# write sumilation data for virtualE3in case
337430
if(args.simul):
338431
write_simul_datafile(lstdids, cob, prop)
339432
# write ECU specific datapoints_cob.py
340-
write_datapoints_file(lstdids, cob, prop)
341-
# report
433+
write_datapoints_file(lstdids, cob, prop, did_variants)
434+
435+
# ++ report +++++++
342436
print("\nconfiguration:")
343437
with open('devices.json', 'r') as file:
344438
lines = file.readlines()

src/open3e/Open3E_dids2json.py

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,74 @@
1+
"""
2+
Copyright 2025 MyHomeMyData
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you 05_may not use this file except in compliance with the License.
6+
You 05_may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
"""
16+
117
#
2-
# Convert Open3E datapoint list to JSON format, e.g. for use in ioBroker adapter E3onCAN
18+
# Convert Open3E datapoint list to JSON format, e.g. for use in ioBroker adapter ioBroker.e3oncan.
319
#
4-
# Create white list of writable datapoints based on filter patterns
20+
# There is no need to use this tool when using open3e. The resulting files are not used by open3e.
521
#
622
# 08.02.2024: Added Version-String to Open3Edatapoints.json
23+
#
24+
# 14.01.2026: Deactivated generation of list of writables. Now based on vddList
25+
# 14.01.2026: Added creation of list of variant dids.
726

827
import json
928
from datetime import date
1029

1130
import open3e.Open3Edatapoints
31+
import open3e.Open3EdatapointsVariants
1232

1333
from open3e.Open3Ecodecs import *
1434

1535

1636
def main():
17-
dataIdentifiers = dict(open3e.Open3Edatapoints.dataIdentifiers["dids"])
37+
dataIdentifiers = dict(open3e.Open3Edatapoints.dataIdentifiers)
38+
variants = dict(open3e.Open3EdatapointsVariants.dataIdentifiers)
1839

1940
didsDict = {}
20-
didsWritable = {}
21-
writablesPatterns = ['setpoint',
22-
'schedule',
23-
'backupboxconfiguration',
24-
'target',
25-
'offset',
26-
'minimummaximumset',
27-
'minimummaximumlimit',
28-
'domestichotwatercirculationpump',
29-
'operationstate'
30-
]
31-
32-
def setToWritable(id):
33-
res = False
34-
for pattern in writablesPatterns:
35-
if pattern in id.lower():
36-
return True
37-
return False
38-
39-
print('Start conversion of datapoints "open3e.Open3Edatapoints.py" to json format.')
40-
print('Create white list for writable datapoints based on filter patterns.\n')
41-
42-
didsListVersion = date.today().strftime("%Y%m%d")
41+
didsDictVars = {}
42+
43+
print('This tool converts data points for use in the ioBroker adapter ioBroker.e3oncan. It is not used by open3e.')
44+
print('Start conversion of data points "open3e.Open3Edatapoints.py" and "open3e.Open3EdatapointsVariants.py" to json format.')
4345

4446
cntDps = 0
47+
cntVars = 0
4548
cntWrt = 0
4649

47-
for dp in dataIdentifiers:
48-
didsDict[dp] = dataIdentifiers[dp].getCodecInfo()
49-
if setToWritable(didsDict[dp]['id']):
50-
didsWritable[dp] = didsDict[dp]['id']
51-
cntWrt += 1
50+
for dp in dataIdentifiers["dids"]:
51+
didsDict[dp] = dataIdentifiers["dids"][dp].getCodecInfo()
5252
cntDps += 1
5353

54-
didsDict['Version'] = didsListVersion
54+
didsDict['Version'] = dataIdentifiers['Version']
5555

5656
with open('Open3Edatapoints.json', 'w') as json_file:
5757
json.dump(didsDict, json_file, indent=2)
5858

59-
with open('Open3Edatapoints_writables.json', 'w') as json_file:
60-
json.dump(didsWritable, json_file, indent=2)
59+
for dp in variants["dids"]:
60+
didsDictVars[dp] = {}
61+
for v in variants["dids"][dp]:
62+
didsDictVars[dp][v] = variants["dids"][dp][v].getCodecInfo()
63+
cntVars += 1
64+
65+
didsDictVars['Version'] = variants["Version"]
66+
67+
with open('Open3EdatapointsVariants.json', 'w') as json_file:
68+
json.dump(didsDictVars, json_file, indent=2)
6169

6270
print(str(cntDps)+' dids converted to JSON format. See file "Open3Edatapoints.json"')
63-
print(str(cntWrt)+' dids identified as writable. See file "Open3Edatapoints_writables.json"')
71+
print(str(cntVars)+' variant dids converted to JSON format. See file "Open3EdatapointsVariants.json"')
6472
print('Done.')
6573

6674
if __name__ == "__main__":

src/open3e/Open3Eclass.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
11

2+
"""
3+
Copyright 2023 philippoo66, abnoname
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
"""
17+
218
import udsoncan
319
from doipclient import DoIPClient
420
from doipclient.connectors import DoIPClientUDSConnector
@@ -18,7 +34,6 @@
1834
import binascii
1935
import os
2036
import sys
21-
#import time
2237

2338
import open3e.Open3Edatapoints
2439
import open3e.Open3Ecodecs

0 commit comments

Comments
 (0)