Files
adminandClaude Opus 5 79843aa2ae SolarManager unter Versionsverwaltung
Erster Stand der Hintergrundprozesse, die auf der Synology unter
/volume1/homes/wagner/SolarManager laufen: der Manager selbst, die Sammler
je Geraet, die MQTT-Bruecke, der Wecker und - neu hinzugezogen - der
AutoAction-Runner, der als Hintergrundprozess hierher gehoert und nicht ins
Web-Verzeichnis.

Zugangsdaten stehen nicht mehr im Quelltext, sondern in config.ini, die
nicht mit eingecheckt wird. Vorlage ist config.ini.example, gelesen wird sie
von konfig.py. Betroffen waren solarManager.py (Datenbank und Wattpilot),
zeit.py, gatherWaterData.py, wecker.py und skoda_testdaten.py, das sich das
Passwort bisher aus dem Quelltext eines anderen Moduls herausgesucht hat.

Die Kia-Anbindung ist mit dem Fahrzeug entfallen: kiaTest.py,
gatherCarData.py und hyundai_kia_connect_api sind nicht mehr dabei, ebenso
gatherInverterData.py, auf das nur noch eine auskommentierte Zeile zeigte.

Die mitgelieferten Bibliotheken bleiben im Repository - die NAS hat kein
pip, sie muessen neben den Skripten liegen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 20:46:59 +02:00

314 lines
14 KiB
Python

import sunspec2.mdef as mdef
import json
import copy
import pytest
def test_to_int():
assert mdef.to_int('4') == 4
assert isinstance(mdef.to_int('4'), int)
assert isinstance(mdef.to_int(4.0), int)
def test_to_str():
assert mdef.to_str(4) == '4'
assert isinstance(mdef.to_str('4'), str)
def test_to_float():
assert mdef.to_float('4') == 4.0
assert isinstance(mdef.to_float('4'), float)
assert mdef.to_float('z') is None
def test_to_number_type():
assert mdef.to_number_type('4') == 4
assert mdef.to_number_type('4.0') == 4.0
assert mdef.to_number_type('z') == 'z'
def test_validate_find_point():
with open('./sunspec2/models/json/model_702.json') as f:
model_json = json.load(f)
assert mdef.validate_find_point(model_json['group'], 'ID') == model_json['group']['points'][0]
assert mdef.validate_find_point(model_json['group'], 'abc') is None
def test_validate_attrs():
with open('./sunspec2/models/json/model_701.json') as f:
model_json = json.load(f)
# model
assert mdef.validate_attrs(model_json, mdef.model_attr) == ''
model_unexp_attr_err = copy.deepcopy(model_json)
model_unexp_attr_err['abc'] = 'def'
assert mdef.validate_attrs(model_unexp_attr_err, mdef.model_attr)[0:37] == 'Unexpected model definition attribute'
model_unexp_type_err = copy.deepcopy(model_json)
model_unexp_type_err['id'] = '701'
assert mdef.validate_attrs(model_unexp_type_err, mdef.model_attr)[0:15] == 'Unexpected type'
model_attr_missing = copy.deepcopy(model_json)
del model_attr_missing['id']
assert mdef.validate_attrs(model_attr_missing, mdef.model_attr)[0:27] == 'Mandatory attribute missing'
# group
assert mdef.validate_attrs(model_json['group'], mdef.group_attr) == ''
group_unexp_attr_err = copy.deepcopy(model_json)['group']
group_unexp_attr_err['abc'] = 'def'
assert mdef.validate_attrs(group_unexp_attr_err, mdef.group_attr)[0:37] == 'Unexpected model definition attribute'
group_unexp_type_err = copy.deepcopy(model_json)['group']
group_unexp_type_err['name'] = 1
assert mdef.validate_attrs(group_unexp_type_err, mdef.group_attr)[0:15] == 'Unexpected type'
group_attr_missing = copy.deepcopy(model_json)['group']
del group_attr_missing['name']
assert mdef.validate_attrs(group_attr_missing, mdef.group_attr)[0:27] == 'Mandatory attribute missing'
# point
assert mdef.validate_attrs(model_json['group']['points'][0], mdef.point_attr) == ''
point_unexp_attr_err = copy.deepcopy(model_json)['group']['points'][0]
point_unexp_attr_err['abc'] = 'def'
assert mdef.validate_attrs(point_unexp_attr_err, mdef.point_attr)[0:37] == 'Unexpected model definition attribute'
point_unexp_type_err = copy.deepcopy(model_json)['group']['points'][0]
point_unexp_type_err['name'] = 1
assert mdef.validate_attrs(point_unexp_type_err, mdef.point_attr)[0:15] == 'Unexpected type'
point_unexp_value_err = copy.deepcopy(model_json)['group']['points'][1]
point_unexp_value_err['access'] = 'z'
assert mdef.validate_attrs(point_unexp_value_err, mdef.point_attr)[0:16] == 'Unexpected value'
point_attr_missing = copy.deepcopy(model_json)['group']['points'][0]
del point_attr_missing['name']
assert mdef.validate_attrs(point_attr_missing, mdef.point_attr)[0:27] == 'Mandatory attribute missing'
# symbol
assert mdef.validate_attrs(model_json['group']['points'][2]['symbols'][0], mdef.symbol_attr) == ''
symbol_unexp_attr_err = copy.deepcopy(model_json)['group']['points'][2]['symbols'][0]
symbol_unexp_attr_err['abc'] = 'def'
assert mdef.validate_attrs(symbol_unexp_attr_err, mdef.symbol_attr)[0:37] == 'Unexpected model definition attribute'
symbol_unexp_type_err = copy.deepcopy(model_json)['group']['points'][2]['symbols'][0]
symbol_unexp_type_err['name'] = 1
assert mdef.validate_attrs(symbol_unexp_type_err, mdef.symbol_attr)[0:15] == 'Unexpected type'
symbol_attr_missing = copy.deepcopy(model_json)['group']['points'][2]['symbols'][0]
del symbol_attr_missing['name']
assert mdef.validate_attrs(symbol_attr_missing, mdef.symbol_attr)[0:27] == 'Mandatory attribute missing'
def test_validate_group_point_dup():
with open('./sunspec2/models/json/model_704.json') as f:
model_json = json.load(f)
assert mdef.validate_group_point_dup(model_json['group']) == ''
dup_group_id_model = copy.deepcopy(model_json)
dup_group_id_group = dup_group_id_model['group']
dup_group_id_group['groups'][0]['name'] = 'PFWInjRvrt'
assert mdef.validate_group_point_dup(dup_group_id_group)[0:18] == 'Duplicate group id'
dup_group_point_id_model = copy.deepcopy(model_json)
dup_group_point_id_group = dup_group_point_id_model['group']
dup_group_point_id_group['groups'][0]['name'] = 'PFWInjEna'
assert mdef.validate_group_point_dup(dup_group_point_id_group)[0:28] == 'Duplicate group and point id'
mand_attr_miss_model = copy.deepcopy(model_json)
mand_attr_miss_group = mand_attr_miss_model['group']
del mand_attr_miss_group['groups'][0]['name']
assert mdef.validate_group_point_dup(mand_attr_miss_group)[0:32] == 'Mandatory name attribute missing'
dup_point_id_model = copy.deepcopy(model_json)
dup_point_id_group = dup_point_id_model['group']
dup_point_id_group['points'][1]['name'] = 'ID'
assert mdef.validate_group_point_dup(dup_point_id_group)[0:30] == 'Duplicate point id ID in group'
mand_attr_miss_point_model = copy.deepcopy(model_json)
mand_attr_miss_point_group = mand_attr_miss_point_model['group']
del mand_attr_miss_point_group['points'][1]['name']
assert mdef.validate_group_point_dup(mand_attr_miss_point_group)[0:55] == 'Mandatory attribute missing in point ' \
'definition element'
def test_validate_symbols():
symbols = [
{'name': 'CAT_A', 'value': 1},
{'name': 'CAT_B', 'value': 2}
]
assert mdef.validate_symbols(symbols, mdef.symbol_attr) == ''
def test_validate_sf():
with open('./sunspec2/models/json/model_702.json') as f:
model_json = json.load(f)
model_point = model_json['group']['points'][2]
model_group = model_json['group']
model_group_arr = [model_group, model_group]
assert mdef.validate_sf(model_point, 'W_SF', model_group_arr) == ''
not_sf_type_model = copy.deepcopy(model_json)
not_sf_type_point = not_sf_type_model['group']['points'][2]
not_sf_type_group = not_sf_type_model['group']
not_sf_type_group_arr = [not_sf_type_group, not_sf_type_group]
for point in not_sf_type_model['group']['points']:
if point['name'] == 'W_SF':
point['type'] = 'abc'
assert mdef.validate_sf(not_sf_type_point, 'W_SF', not_sf_type_group_arr)[0:60] == 'Scale factor W_SF for point ' \
'WMaxRtg is not scale factor ' \
'type'
sf_not_found_model = copy.deepcopy(model_json)
sf_not_found_point = sf_not_found_model['group']['points'][2]
sf_not_found_group = sf_not_found_model['group']
sf_not_found_group_arr = [sf_not_found_group, sf_not_found_group]
assert mdef.validate_sf(sf_not_found_point, 'ABC', sf_not_found_group_arr)[0:44] == 'Scale factor ABC for point ' \
'WMaxRtg not found'
sf_out_range_model = copy.deepcopy(model_json)
sf_out_range_point = sf_out_range_model['group']['points'][2]
sf_out_range_group = sf_out_range_model['group']
sf_out_range_group_arr = [sf_out_range_group, sf_out_range_group]
assert mdef.validate_sf(sf_out_range_point, 11, sf_out_range_group_arr)[0:46] == 'Scale factor 11 for point ' \
'WMaxRtg out of range'
sf_invalid_type_model = copy.deepcopy(model_json)
sf_invalid_type_point = sf_invalid_type_model['group']['points'][2]
sf_invalid_type_group = sf_invalid_type_model['group']
sf_invalid_type_group_arr = [sf_invalid_type_group, sf_invalid_type_group]
assert mdef.validate_sf(sf_invalid_type_point, 4.0, sf_invalid_type_group_arr)[0:51] == 'Scale factor 4.0 for' \
' point WMaxRtg has ' \
'invalid type'
def test_validate_point_def():
with open('./sunspec2/models/json/model_702.json') as f:
model_json = json.load(f)
model_group = model_json['group']
group = model_json['group']
point = model_json['group']['points'][0]
assert mdef.validate_point_def(point, model_group, group) == ''
unk_point_type_model = copy.deepcopy(model_json)
unk_point_type_model_group = unk_point_type_model['group']
unk_point_type_group = unk_point_type_model['group']
unk_point_type_point = unk_point_type_model['group']['points'][0]
unk_point_type_point['type'] = 'abc'
assert mdef.validate_point_def(unk_point_type_point, unk_point_type_model_group,
unk_point_type_group)[0:35] == 'Unknown point type abc for point ID'
dup_symbol_model = copy.deepcopy(model_json)
dup_symbol_model_group = dup_symbol_model['group']
dup_symbol_group = dup_symbol_model['group']
dup_symbol_point = dup_symbol_model['group']['points'][21]
dup_symbol_point['symbols'][0]['name'] = 'CAT_B'
assert mdef.validate_point_def(dup_symbol_point, dup_symbol_model_group,
dup_symbol_group)[0:19] == 'Duplicate symbol id'
mand_attr_missing = copy.deepcopy(model_json)
mand_attr_missing_model_group = mand_attr_missing['group']
mand_attr_missing_group = mand_attr_missing['group']
mand_attr_missing_point = mand_attr_missing['group']['points'][0]
del mand_attr_missing_point['name']
assert mdef.validate_point_def(mand_attr_missing_point, mand_attr_missing_model_group,
mand_attr_missing_group)[0:27] == 'Mandatory attribute missing'
def test_validate_group_def():
with open('./sunspec2/models/json/model_702.json') as f:
model_json = json.load(f)
assert mdef.validate_group_def(model_json['group'], model_json['group']) == ''
def test_validate_model_group_def():
with open('./sunspec2/models/json/model_702.json') as f:
model_json = json.load(f)
assert mdef.validate_model_group_def(model_json, model_json['group']) == ''
missing_id_model = copy.deepcopy(model_json)
missing_id_group = missing_id_model['group']
missing_id_group['points'][0]['name'] = 'abc'
assert mdef.validate_model_group_def(missing_id_model, missing_id_group)[0:41] == 'First point in top-level' \
' group must be ID'
wrong_model_id_model = copy.deepcopy(model_json)
wrong_model_id_group = wrong_model_id_model['group']
wrong_model_id_group['points'][0]['value'] = 0
assert mdef.validate_model_group_def(wrong_model_id_model, wrong_model_id_group)[0:42] == 'Model ID does not ' \
'match top-level group ID'
missing_len_model = copy.deepcopy(model_json)
missing_len_group = missing_len_model['group']
missing_len_group['points'][1]['name'] = 'abc'
assert mdef.validate_model_group_def(missing_len_model, missing_len_group)[0:41] == 'Second point in top-level ' \
'group must be L'
missing_two_p_model = copy.deepcopy(model_json)
missing_two_p_group = missing_two_p_model['group']
missing_two_p_point = missing_two_p_group['points'][0]
del missing_two_p_group['points']
missing_two_p_group['points'] = [missing_two_p_point]
assert mdef.validate_model_group_def(missing_two_p_model, missing_two_p_group)[0:48] == 'Top-level group must' \
' contain at least two ' \
'points'
missing_p_def_model = copy.deepcopy(model_json)
missing_p_def_group = missing_p_def_model['group']
del missing_p_def_group['points']
assert mdef.validate_model_group_def(missing_p_def_model, missing_p_def_group)[0:41] == 'Top-level group' \
' missing point definitions'
def test_validate_model_def():
with open('./sunspec2/models/json/model_702.json') as f:
model_json = json.load(f)
assert mdef.validate_model_def(model_json) == ''
def test_from_json_str():
with open('./sunspec2/models/json/model_63001.json') as f:
model_json = json.load(f)
model_json_str = json.dumps(model_json)
assert isinstance(mdef.from_json_str(model_json_str), dict)
def test_from_json_file():
assert isinstance(mdef.from_json_file('./sunspec2/models/json/model_63001.json'), dict)
def test_to_json_str():
with open('./sunspec2/models/json/model_63001.json') as f:
model_json = json.load(f)
assert isinstance(mdef.to_json_str(model_json), str)
def test_to_json_filename():
assert mdef.to_json_filename('63001') == 'model_63001.json'
def test_to_json_file(tmp_path):
with open('./sunspec2/models/json/model_63001.json') as f:
model_json = json.load(f)
mdef.to_json_file(model_json, filedir=tmp_path)
with open(tmp_path / 'model_63001.json') as f:
model_json = json.load(f)
assert isinstance(model_json, dict)
def test_model_filename_to_id():
assert mdef.model_filename_to_id('model_00077.json') == 77
with pytest.raises(Exception) as exc:
mdef.model_filename_to_id('model_abc.json')
assert 'Error extracting model id from filename' in str(exc.value)