frequenz-client-assets 0.2.0__tar.gz → 0.3.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.
Files changed (67) hide show
  1. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/PKG-INFO +29 -29
  2. frequenz_client_assets-0.3.1/RELEASE_NOTES.md +5 -0
  3. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/pyproject.toml +30 -29
  4. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/_client.py +128 -14
  5. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/_lifetime.py +0 -1
  6. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/_location.py +0 -1
  7. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/_microgrid_proto.py +40 -15
  8. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/__init__.py +2 -0
  9. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_category.py +3 -0
  10. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_electrical_component_proto.py +8 -0
  11. frequenz_client_assets-0.3.1/src/frequenz/client/assets/electrical_component/_steam_boiler.py +20 -0
  12. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_types.py +2 -0
  13. frequenz_client_assets-0.3.1/src/frequenz/client/assets/exceptions.py +195 -0
  14. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/metrics/_bounds_proto.py +0 -1
  15. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz_client_assets.egg-info/PKG-INFO +29 -29
  16. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz_client_assets.egg-info/SOURCES.txt +1 -0
  17. frequenz_client_assets-0.3.1/src/frequenz_client_assets.egg-info/requires.txt +58 -0
  18. frequenz_client_assets-0.2.0/RELEASE_NOTES.md +0 -24
  19. frequenz_client_assets-0.2.0/src/frequenz/client/assets/exceptions.py +0 -51
  20. frequenz_client_assets-0.2.0/src/frequenz_client_assets.egg-info/requires.txt +0 -57
  21. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/LICENSE +0 -0
  22. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/MANIFEST.in +0 -0
  23. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/README.md +0 -0
  24. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/setup.cfg +0 -0
  25. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/__init__.py +0 -0
  26. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/_delivery_area.py +0 -0
  27. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/_delivery_area_proto.py +0 -0
  28. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/_lifetime_proto.py +0 -0
  29. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/_location_proto.py +0 -0
  30. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/_microgrid.py +0 -0
  31. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/_microgrid_json.py +0 -0
  32. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/_utils.py +0 -0
  33. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/cli/__init__.py +0 -0
  34. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/cli/__main__.py +0 -0
  35. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/cli/_utils.py +0 -0
  36. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/conftest.py +0 -0
  37. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_battery.py +0 -0
  38. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_breaker.py +0 -0
  39. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_capacitor_bank.py +0 -0
  40. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_chp.py +0 -0
  41. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_connection.py +0 -0
  42. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_connection_json.py +0 -0
  43. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_connection_proto.py +0 -0
  44. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_converter.py +0 -0
  45. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_crypto_miner.py +0 -0
  46. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_electrical_component.py +0 -0
  47. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_electrical_component_json.py +0 -0
  48. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_electrolyzer.py +0 -0
  49. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_ev_charger.py +0 -0
  50. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_grid_connection_point.py +0 -0
  51. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_hvac.py +0 -0
  52. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_inverter.py +0 -0
  53. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_meter.py +0 -0
  54. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_plc.py +0 -0
  55. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_power_transformer.py +0 -0
  56. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_precharger.py +0 -0
  57. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_problematic.py +0 -0
  58. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_static_transfer_switch.py +0 -0
  59. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_uninterruptible_power_supply.py +0 -0
  60. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/electrical_component/_wind_turbine.py +0 -0
  61. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/metrics/__init__.py +0 -0
  62. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/metrics/_bounds.py +0 -0
  63. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/metrics/_metric.py +0 -0
  64. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz/client/assets/py.typed +0 -0
  65. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz_client_assets.egg-info/dependency_links.txt +0 -0
  66. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz_client_assets.egg-info/entry_points.txt +0 -0
  67. {frequenz_client_assets-0.2.0 → frequenz_client_assets-0.3.1}/src/frequenz_client_assets.egg-info/top_level.txt +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frequenz-client-assets
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: Assets API client for Python
5
5
  Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
6
- License: MIT
6
+ License-Expression: MIT
7
7
  Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-client-assets-python/
8
8
  Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-client-assets-python/releases
9
9
  Project-URL: Issues, https://github.com/frequenz-floss/frequenz-client-assets-python/issues
@@ -12,7 +12,6 @@ Project-URL: Support, https://github.com/frequenz-floss/frequenz-client-assets-p
12
12
  Keywords: frequenz,python,lib,library,frequenz-client-assets,client-assets,client,assets,api
13
13
  Classifier: Development Status :: 3 - Alpha
14
14
  Classifier: Intended Audience :: Developers
15
- Classifier: License :: OSI Approved :: MIT License
16
15
  Classifier: Programming Language :: Python :: 3
17
16
  Classifier: Programming Language :: Python :: 3 :: Only
18
17
  Classifier: Topic :: Software Development :: Libraries
@@ -22,50 +21,51 @@ Description-Content-Type: text/markdown
22
21
  License-File: LICENSE
23
22
  Requires-Dist: typing-extensions<5,>=4.13.0
24
23
  Requires-Dist: frequenz-api-assets<0.2.0,>=0.1.0
25
- Requires-Dist: frequenz-api-common<1,>=0.8.0
24
+ Requires-Dist: frequenz-api-common<1,>=0.8.2
26
25
  Requires-Dist: frequenz-client-base<0.12.0,>=0.11.0
27
26
  Requires-Dist: frequenz-client-common<0.4.0,>=0.3.6
28
27
  Requires-Dist: grpcio<2,>=1.73.1
29
28
  Provides-Extra: cli
30
- Requires-Dist: asyncclick==8.3.0.4; extra == "cli"
29
+ Requires-Dist: asyncclick==8.3.0.7; extra == "cli"
31
30
  Provides-Extra: dev-flake8
32
31
  Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
32
+ Requires-Dist: flake8-datetimez==20.10.0; extra == "dev-flake8"
33
33
  Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
34
- Requires-Dist: flake8-pyproject==1.2.3; extra == "dev-flake8"
35
- Requires-Dist: pydoclint==0.7.6; extra == "dev-flake8"
34
+ Requires-Dist: flake8-pyproject==1.2.4; extra == "dev-flake8"
35
+ Requires-Dist: pydoclint==0.8.3; extra == "dev-flake8"
36
36
  Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
37
37
  Provides-Extra: dev-formatting
38
- Requires-Dist: black==25.9.0; extra == "dev-formatting"
39
- Requires-Dist: isort==6.1.0; extra == "dev-formatting"
38
+ Requires-Dist: black==26.3.1; extra == "dev-formatting"
39
+ Requires-Dist: isort==8.0.1; extra == "dev-formatting"
40
40
  Provides-Extra: dev-mkdocs
41
- Requires-Dist: Markdown==3.9; extra == "dev-mkdocs"
42
- Requires-Dist: black==25.9.0; extra == "dev-mkdocs"
43
- Requires-Dist: mike==2.1.3; extra == "dev-mkdocs"
44
- Requires-Dist: mkdocs-gen-files==0.5.0; extra == "dev-mkdocs"
45
- Requires-Dist: mkdocs-literate-nav==0.6.2; extra == "dev-mkdocs"
46
- Requires-Dist: mkdocs-macros-plugin==1.4.1; extra == "dev-mkdocs"
47
- Requires-Dist: mkdocs-material==9.6.23; extra == "dev-mkdocs"
48
- Requires-Dist: mkdocstrings[python]==0.30.1; extra == "dev-mkdocs"
49
- Requires-Dist: mkdocstrings-python==1.18.2; extra == "dev-mkdocs"
50
- Requires-Dist: frequenz-repo-config[lib]==0.13.6; extra == "dev-mkdocs"
41
+ Requires-Dist: Markdown==3.10.2; extra == "dev-mkdocs"
42
+ Requires-Dist: black==26.3.1; extra == "dev-mkdocs"
43
+ Requires-Dist: mike==2.1.4; extra == "dev-mkdocs"
44
+ Requires-Dist: mkdocs-gen-files==0.6.1; extra == "dev-mkdocs"
45
+ Requires-Dist: mkdocs-literate-nav==0.6.3; extra == "dev-mkdocs"
46
+ Requires-Dist: mkdocs-macros-plugin==1.5.0; extra == "dev-mkdocs"
47
+ Requires-Dist: mkdocs-material==9.7.6; extra == "dev-mkdocs"
48
+ Requires-Dist: mkdocstrings[python]==1.0.4; extra == "dev-mkdocs"
49
+ Requires-Dist: mkdocstrings-python==2.0.3; extra == "dev-mkdocs"
50
+ Requires-Dist: frequenz-repo-config[lib]==0.16.0; extra == "dev-mkdocs"
51
51
  Provides-Extra: dev-mypy
52
- Requires-Dist: mypy==1.18.2; extra == "dev-mypy"
52
+ Requires-Dist: mypy==1.20.2; extra == "dev-mypy"
53
53
  Requires-Dist: grpc-stubs==1.53.0.6; extra == "dev-mypy"
54
- Requires-Dist: types-Markdown==3.9.0.20250906; extra == "dev-mypy"
55
- Requires-Dist: types-protobuf==6.32.1.20250918; extra == "dev-mypy"
54
+ Requires-Dist: types-Markdown==3.10.2.20260211; extra == "dev-mypy"
55
+ Requires-Dist: types-protobuf==6.32.1.20260221; extra == "dev-mypy"
56
56
  Requires-Dist: frequenz-client-assets[cli,dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
57
57
  Provides-Extra: dev-noxfile
58
- Requires-Dist: nox==2025.5.1; extra == "dev-noxfile"
59
- Requires-Dist: frequenz-repo-config[lib]==0.13.6; extra == "dev-noxfile"
58
+ Requires-Dist: nox==2026.2.9; extra == "dev-noxfile"
59
+ Requires-Dist: frequenz-repo-config[lib]==0.16.0; extra == "dev-noxfile"
60
60
  Provides-Extra: dev-pylint
61
61
  Requires-Dist: frequenz-client-assets[cli,dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
62
62
  Provides-Extra: dev-pytest
63
- Requires-Dist: pytest==8.4.2; extra == "dev-pytest"
64
- Requires-Dist: pylint==3.3.8; extra == "dev-pytest"
65
- Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.13.6; extra == "dev-pytest"
63
+ Requires-Dist: pytest==9.0.3; extra == "dev-pytest"
64
+ Requires-Dist: pylint==4.0.5; extra == "dev-pytest"
65
+ Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.16.0; extra == "dev-pytest"
66
66
  Requires-Dist: pytest-mock==3.15.1; extra == "dev-pytest"
67
- Requires-Dist: pytest-asyncio==1.2.0; extra == "dev-pytest"
68
- Requires-Dist: async-solipsism==0.8; extra == "dev-pytest"
67
+ Requires-Dist: pytest-asyncio==1.3.0; extra == "dev-pytest"
68
+ Requires-Dist: async-solipsism==0.9; extra == "dev-pytest"
69
69
  Provides-Extra: dev
70
70
  Requires-Dist: frequenz-client-assets[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
71
71
  Dynamic: license-file
@@ -0,0 +1,5 @@
1
+ # Frequenz Assets API Client Release Notes
2
+
3
+ ## New Features
4
+
5
+ Added steam boiler component category.
@@ -3,9 +3,9 @@
3
3
 
4
4
  [build-system]
5
5
  requires = [
6
- "setuptools == 80.9.0",
7
- "setuptools_scm[toml] == 9.2.2",
8
- "frequenz-repo-config[lib] == 0.13.6",
6
+ "setuptools == 82.0.1",
7
+ "setuptools_scm[toml] == 10.0.5",
8
+ "frequenz-repo-config[lib] == 0.16.0",
9
9
  ]
10
10
  build-backend = "setuptools.build_meta"
11
11
 
@@ -13,7 +13,8 @@ build-backend = "setuptools.build_meta"
13
13
  name = "frequenz-client-assets"
14
14
  description = "Assets API client for Python"
15
15
  readme = "README.md"
16
- license = { text = "MIT" }
16
+ license = "MIT"
17
+ license-files = ["LICENSE"]
17
18
  keywords = [
18
19
  "frequenz",
19
20
  "python",
@@ -28,7 +29,6 @@ keywords = [
28
29
  classifiers = [
29
30
  "Development Status :: 3 - Alpha",
30
31
  "Intended Audience :: Developers",
31
- "License :: OSI Approved :: MIT License",
32
32
  "Programming Language :: Python :: 3",
33
33
  "Programming Language :: Python :: 3 :: Only",
34
34
  "Topic :: Software Development :: Libraries",
@@ -38,7 +38,7 @@ requires-python = ">= 3.11, < 4"
38
38
  dependencies = [
39
39
  "typing-extensions >= 4.13.0, < 5",
40
40
  "frequenz-api-assets >= 0.1.0, < 0.2.0",
41
- "frequenz-api-common >= 0.8.0, < 1",
41
+ "frequenz-api-common >= 0.8.2, < 1",
42
42
  "frequenz-client-base >= 0.11.0, < 0.12.0",
43
43
  "frequenz-client-common >= 0.3.6, < 0.4.0",
44
44
  "grpcio >= 1.73.1, < 2",
@@ -53,48 +53,49 @@ name = "Frequenz Energy-as-a-Service GmbH"
53
53
  email = "floss@frequenz.com"
54
54
 
55
55
  [project.optional-dependencies]
56
- cli = ["asyncclick == 8.3.0.4"]
56
+ cli = ["asyncclick == 8.3.0.7"]
57
57
  dev-flake8 = [
58
58
  "flake8 == 7.3.0",
59
+ "flake8-datetimez == 20.10.0",
59
60
  "flake8-docstrings == 1.7.0",
60
- "flake8-pyproject == 1.2.3", # For reading the flake8 config from pyproject.toml
61
- "pydoclint == 0.7.6",
61
+ "flake8-pyproject == 1.2.4", # For reading the flake8 config from pyproject.toml
62
+ "pydoclint == 0.8.3",
62
63
  "pydocstyle == 6.3.0",
63
64
  ]
64
- dev-formatting = ["black == 25.9.0", "isort == 6.1.0"]
65
+ dev-formatting = ["black == 26.3.1", "isort == 8.0.1"]
65
66
  dev-mkdocs = [
66
- "Markdown == 3.9",
67
- "black == 25.9.0",
68
- "mike == 2.1.3",
69
- "mkdocs-gen-files == 0.5.0",
70
- "mkdocs-literate-nav == 0.6.2",
71
- "mkdocs-macros-plugin == 1.4.1",
72
- "mkdocs-material == 9.6.23",
73
- "mkdocstrings[python] == 0.30.1",
74
- "mkdocstrings-python == 1.18.2",
75
- "frequenz-repo-config[lib] == 0.13.6",
67
+ "Markdown == 3.10.2",
68
+ "black == 26.3.1",
69
+ "mike == 2.1.4",
70
+ "mkdocs-gen-files == 0.6.1",
71
+ "mkdocs-literate-nav == 0.6.3",
72
+ "mkdocs-macros-plugin == 1.5.0",
73
+ "mkdocs-material == 9.7.6",
74
+ "mkdocstrings[python] == 1.0.4",
75
+ "mkdocstrings-python == 2.0.3",
76
+ "frequenz-repo-config[lib] == 0.16.0",
76
77
  ]
77
78
  dev-mypy = [
78
- "mypy == 1.18.2",
79
+ "mypy == 1.20.2",
79
80
  "grpc-stubs == 1.53.0.6",
80
- "types-Markdown == 3.9.0.20250906",
81
- "types-protobuf == 6.32.1.20250918",
81
+ "types-Markdown == 3.10.2.20260211",
82
+ "types-protobuf == 6.32.1.20260221",
82
83
  # For checking the noxfile, docs/ script, and tests
83
84
  "frequenz-client-assets[dev-mkdocs,dev-noxfile,dev-pytest,cli]",
84
85
  ]
85
- dev-noxfile = ["nox == 2025.5.1", "frequenz-repo-config[lib] == 0.13.6"]
86
+ dev-noxfile = ["nox == 2026.2.9", "frequenz-repo-config[lib] == 0.16.0"]
86
87
  dev-pylint = [
87
88
  # dev-pytest already defines a dependency to pylint because of the examples
88
89
  # For checking the noxfile, docs/ script, and tests
89
90
  "frequenz-client-assets[dev-mkdocs,dev-noxfile,dev-pytest,cli]",
90
91
  ]
91
92
  dev-pytest = [
92
- "pytest == 8.4.2",
93
- "pylint == 3.3.8", # We need this to check for the examples
94
- "frequenz-repo-config[extra-lint-examples] == 0.13.6",
93
+ "pytest == 9.0.3",
94
+ "pylint == 4.0.5", # We need this to check for the examples
95
+ "frequenz-repo-config[extra-lint-examples] == 0.16.0",
95
96
  "pytest-mock == 3.15.1",
96
- "pytest-asyncio == 1.2.0",
97
- "async-solipsism == 0.8",
97
+ "pytest-asyncio == 1.3.0",
98
+ "async-solipsism == 0.9",
98
99
  ]
99
100
  dev = [
100
101
  "frequenz-client-assets[dev-mkdocs,dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]",
@@ -18,12 +18,23 @@ from frequenz.client.common.microgrid import MicrogridId
18
18
  from frequenz.client.common.microgrid.electrical_components import ElectricalComponentId
19
19
 
20
20
  from ._microgrid import Microgrid
21
- from ._microgrid_proto import microgrid_from_proto
21
+ from ._microgrid_proto import microgrid_from_proto, microgrid_from_proto_with_issues
22
22
  from .electrical_component._connection import ComponentConnection
23
- from .electrical_component._connection_proto import component_connection_from_proto
23
+ from .electrical_component._connection_proto import (
24
+ component_connection_from_proto,
25
+ component_connection_from_proto_with_issues,
26
+ )
24
27
  from .electrical_component._electrical_component import ElectricalComponent
25
- from .electrical_component._electrical_component_proto import electrical_component_proto
26
- from .exceptions import ClientNotConnected
28
+ from .electrical_component._electrical_component_proto import (
29
+ electrical_component_from_proto_with_issues,
30
+ electrical_component_proto,
31
+ )
32
+ from .exceptions import (
33
+ ClientNotConnected,
34
+ InvalidConnectionError,
35
+ InvalidElectricalComponentError,
36
+ InvalidMicrogridError,
37
+ )
27
38
 
28
39
  DEFAULT_GRPC_CALL_TIMEOUT = 60.0
29
40
  """The default timeout for gRPC calls made by this client (in seconds)."""
@@ -88,14 +99,21 @@ class AssetsApiClient(
88
99
  # use the async stub, so we cast the sync stub to the async stub.
89
100
  return self._stub # type: ignore
90
101
 
91
- async def get_microgrid( # noqa: DOC502 (raises ApiClientError indirectly)
92
- self, microgrid_id: MicrogridId
102
+ async def get_microgrid( # noqa: DOC502,DOC503 (raises indirectly)
103
+ self,
104
+ microgrid_id: MicrogridId,
105
+ *,
106
+ raise_on_errors: bool = False,
93
107
  ) -> Microgrid:
94
108
  """
95
109
  Get the details of a microgrid.
96
110
 
97
111
  Args:
98
112
  microgrid_id: The ID of the microgrid to get the details of.
113
+ raise_on_errors: If True, raise an
114
+ [InvalidMicrogridError][frequenz.client.assets.exceptions.InvalidMicrogridError]
115
+ when major validation issues are found instead of just
116
+ logging them.
99
117
 
100
118
  Returns:
101
119
  The details of the microgrid.
@@ -103,6 +121,8 @@ class AssetsApiClient(
103
121
  Raises:
104
122
  ApiClientError: If there are any errors communicating with the Assets API,
105
123
  most likely a subclass of [GrpcError][frequenz.client.base.exception.GrpcError].
124
+ InvalidMicrogridError: If `raise_on_errors` is True and major
125
+ validation issues are found.
106
126
  """
107
127
  response = await call_stub_method(
108
128
  self,
@@ -113,19 +133,48 @@ class AssetsApiClient(
113
133
  method_name="GetMicrogrid",
114
134
  )
115
135
 
136
+ if raise_on_errors:
137
+ major_issues: list[str] = []
138
+ minor_issues: list[str] = []
139
+ microgrid = microgrid_from_proto_with_issues(
140
+ response.microgrid,
141
+ major_issues=major_issues,
142
+ minor_issues=minor_issues,
143
+ )
144
+ if major_issues:
145
+ raise InvalidMicrogridError(
146
+ microgrid=microgrid,
147
+ major_issues=major_issues,
148
+ minor_issues=minor_issues,
149
+ raw_message=response.microgrid,
150
+ )
151
+ return microgrid
152
+
116
153
  return microgrid_from_proto(response.microgrid)
117
154
 
118
155
  async def list_microgrid_electrical_components(
119
- self, microgrid_id: MicrogridId
156
+ self,
157
+ microgrid_id: MicrogridId,
158
+ *,
159
+ raise_on_errors: bool = False,
120
160
  ) -> list[ElectricalComponent]:
121
161
  """
122
162
  Get the electrical components of a microgrid.
123
163
 
124
164
  Args:
125
165
  microgrid_id: The ID of the microgrid to get the electrical components of.
166
+ raise_on_errors: If True, raise an
167
+ `ExceptionGroup[InvalidElectricalComponentError]`
168
+ when major validation issues are found in any component instead
169
+ of just logging them.
126
170
 
127
171
  Returns:
128
172
  The electrical components of the microgrid.
173
+
174
+ Raises:
175
+ ExceptionGroup: If `raise_on_errors` is True and major validation
176
+ issues are found. All exceptions in the group are
177
+ [InvalidElectricalComponentError][frequenz.client.assets.exceptions.InvalidElectricalComponentError].
129
178
  """
130
179
  response = await call_stub_method(
131
180
  self,
@@ -138,6 +187,35 @@ class AssetsApiClient(
138
187
  method_name="ListMicrogridElectricalComponents",
139
188
  )
140
189
 
190
+ if raise_on_errors:
191
+ components: list[ElectricalComponent] = []
192
+ exceptions: list[InvalidElectricalComponentError] = []
193
+ for component_pb in response.components:
194
+ major_issues: list[str] = []
195
+ minor_issues: list[str] = []
196
+ component = electrical_component_from_proto_with_issues(
197
+ component_pb,
198
+ major_issues=major_issues,
199
+ minor_issues=minor_issues,
200
+ )
201
+ if major_issues:
202
+ exceptions.append(
203
+ InvalidElectricalComponentError(
204
+ component=component,
205
+ major_issues=major_issues,
206
+ minor_issues=minor_issues,
207
+ raw_message=component_pb,
208
+ )
209
+ )
210
+ else:
211
+ components.append(component)
212
+ if exceptions:
213
+ raise ExceptionGroup(
214
+ f"{len(exceptions)} electrical component(s) failed validation",
215
+ exceptions,
216
+ )
217
+ return components
218
+
141
219
  return [
142
220
  electrical_component_proto(component) for component in response.components
143
221
  ]
@@ -147,7 +225,9 @@ class AssetsApiClient(
147
225
  microgrid_id: MicrogridId,
148
226
  source_component_ids: Iterable[ElectricalComponentId] = (),
149
227
  destination_component_ids: Iterable[ElectricalComponentId] = (),
150
- ) -> list[ComponentConnection | None]:
228
+ *,
229
+ raise_on_errors: bool = False,
230
+ ) -> list[ComponentConnection]:
151
231
  """
152
232
  Get the electrical component connections of a microgrid.
153
233
 
@@ -158,9 +238,18 @@ class AssetsApiClient(
158
238
  these component IDs. If None or empty, no filtering is applied.
159
239
  destination_component_ids: Only return connections that terminate at
160
240
  these component IDs. If None or empty, no filtering is applied.
241
+ raise_on_errors: If True, raise an
242
+ `ExceptionGroup[InvalidConnectionError]`
243
+ when major validation issues are found in any connection instead
244
+ of just logging them.
161
245
 
162
246
  Returns:
163
247
  The electrical component connections of the microgrid.
248
+
249
+ Raises:
250
+ ExceptionGroup: If `raise_on_errors` is True and major validation
251
+ issues are found. All exceptions in the group are
252
+ [InvalidConnectionError][frequenz.client.assets.exceptions.InvalidConnectionError].
164
253
  """
165
254
  request = assets_pb2.ListMicrogridElectricalComponentConnectionsRequest(
166
255
  microgrid_id=int(microgrid_id),
@@ -177,9 +266,34 @@ class AssetsApiClient(
177
266
  method_name="ListMicrogridElectricalComponentConnections",
178
267
  )
179
268
 
180
- return list(
181
- map(
182
- component_connection_from_proto,
183
- filter(bool, response.connections),
184
- )
185
- )
269
+ if raise_on_errors:
270
+ valid_connections: list[ComponentConnection] = []
271
+ exceptions: list[InvalidConnectionError] = []
272
+ for conn_pb in filter(bool, response.connections):
273
+ major_issues: list[str] = []
274
+ connection = component_connection_from_proto_with_issues(
275
+ conn_pb, major_issues=major_issues
276
+ )
277
+ if major_issues:
278
+ exceptions.append(
279
+ InvalidConnectionError(
280
+ connection=connection,
281
+ major_issues=major_issues,
282
+ minor_issues=[],
283
+ raw_message=conn_pb,
284
+ )
285
+ )
286
+ elif connection is not None:
287
+ valid_connections.append(connection)
288
+ if exceptions:
289
+ raise ExceptionGroup(
290
+ f"{len(exceptions)} connection(s) failed validation",
291
+ exceptions,
292
+ )
293
+ return valid_connections
294
+
295
+ return [
296
+ c
297
+ for c in map(component_connection_from_proto, response.connections)
298
+ if c is not None
299
+ ]
@@ -3,7 +3,6 @@
3
3
 
4
4
  """Lifetime of a microgrid asset."""
5
5
 
6
-
7
6
  from dataclasses import dataclass
8
7
  from datetime import datetime, timezone
9
8
 
@@ -3,7 +3,6 @@
3
3
 
4
4
  """Location information for a microgrid."""
5
5
 
6
-
7
6
  from dataclasses import dataclass
8
7
 
9
8
 
@@ -3,7 +3,6 @@
3
3
 
4
4
  """Loading of MicrogridInfo objects from protobuf messages."""
5
5
 
6
-
7
6
  import logging
8
7
 
9
8
  from frequenz.api.common.v1alpha8.microgrid import microgrid_pb2
@@ -32,6 +31,46 @@ def microgrid_from_proto(message: microgrid_pb2.Microgrid) -> Microgrid:
32
31
  major_issues: list[str] = []
33
32
  minor_issues: list[str] = []
34
33
 
34
+ microgrid = microgrid_from_proto_with_issues(
35
+ message, major_issues=major_issues, minor_issues=minor_issues
36
+ )
37
+
38
+ if major_issues:
39
+ _logger.warning(
40
+ "Found issues in microgrid: %s | Protobuf message:\n%s",
41
+ ", ".join(major_issues),
42
+ message,
43
+ )
44
+
45
+ if minor_issues:
46
+ _logger.debug(
47
+ "Found minor issues in microgrid: %s | Protobuf message:\n%s",
48
+ ", ".join(minor_issues),
49
+ message,
50
+ )
51
+
52
+ return microgrid
53
+
54
+
55
+ def microgrid_from_proto_with_issues(
56
+ message: microgrid_pb2.Microgrid,
57
+ *,
58
+ major_issues: list[str],
59
+ minor_issues: list[str],
60
+ ) -> Microgrid:
61
+ """Convert a protobuf microgrid message to a microgrid object, collecting issues.
62
+
63
+ This function is useful when you want to collect issues during parsing
64
+ rather than logging them immediately.
65
+
66
+ Args:
67
+ message: The protobuf message to convert.
68
+ major_issues: A list to collect major issues found during validation.
69
+ minor_issues: A list to collect minor issues found during validation.
70
+
71
+ Returns:
72
+ The resulting microgrid object.
73
+ """
35
74
  delivery_area: DeliveryArea | None = None
36
75
  if message.HasField("delivery_area"):
37
76
  delivery_area = delivery_area_from_proto(message.delivery_area)
@@ -54,20 +93,6 @@ def microgrid_from_proto(message: microgrid_pb2.Microgrid) -> Microgrid:
54
93
  elif isinstance(status, int):
55
94
  major_issues.append("status is unrecognized")
56
95
 
57
- if major_issues:
58
- _logger.warning(
59
- "Found issues in microgrid: %s | Protobuf message:\n%s",
60
- ", ".join(major_issues),
61
- message,
62
- )
63
-
64
- if minor_issues:
65
- _logger.debug(
66
- "Found minor issues in microgrid: %s | Protobuf message:\n%s",
67
- ", ".join(minor_issues),
68
- message,
69
- )
70
-
71
96
  return Microgrid(
72
97
  id=MicrogridId(message.id),
73
98
  enterprise_id=EnterpriseId(message.enterprise_id),
@@ -50,6 +50,7 @@ from ._problematic import (
50
50
  UnspecifiedComponent,
51
51
  )
52
52
  from ._static_transfer_switch import StaticTransferSwitch
53
+ from ._steam_boiler import SteamBoiler
53
54
  from ._uninterruptible_power_supply import UninterruptiblePowerSupply
54
55
  from ._wind_turbine import WindTurbine
55
56
 
@@ -91,6 +92,7 @@ __all__ = [
91
92
  "MismatchedCategoryComponent",
92
93
  "UnrecognizedComponent",
93
94
  "UnspecifiedComponent",
95
+ "SteamBoiler",
94
96
  "StaticTransferSwitch",
95
97
  "UninterruptiblePowerSupply",
96
98
  "WindTurbine",
@@ -86,3 +86,6 @@ class ElectricalComponentCategory(enum.Enum):
86
86
 
87
87
  WIND_TURBINE = electrical_components_pb2.ELECTRICAL_COMPONENT_CATEGORY_WIND_TURBINE
88
88
  """A wind turbine."""
89
+
90
+ STEAM_BOILER = electrical_components_pb2.ELECTRICAL_COMPONENT_CATEGORY_STEAM_BOILER
91
+ """A steam boiler."""
@@ -57,6 +57,7 @@ from ._power_transformer import PowerTransformer
57
57
  from ._precharger import Precharger
58
58
  from ._problematic import UnrecognizedComponent, UnspecifiedComponent
59
59
  from ._static_transfer_switch import StaticTransferSwitch
60
+ from ._steam_boiler import SteamBoiler
60
61
  from ._types import ElectricalComponentType
61
62
  from ._uninterruptible_power_supply import UninterruptiblePowerSupply
62
63
  from ._wind_turbine import WindTurbine
@@ -172,6 +173,10 @@ def component_base_from_proto_with_issues(
172
173
  )
173
174
 
174
175
 
176
+ # This function is just a big match statement to dispatch to the right component
177
+ # class based on the category and subcategory, so it's fine to have many branches
178
+ # here.
179
+ # pylint: disable-next=too-many-branches
175
180
  def electrical_component_from_proto_with_issues(
176
181
  message: electrical_components_pb2.ElectricalComponent,
177
182
  *,
@@ -215,6 +220,7 @@ def electrical_component_from_proto_with_issues(
215
220
  | ElectricalComponentCategory.PRECHARGER
216
221
  | ElectricalComponentCategory.BREAKER
217
222
  | ElectricalComponentCategory.PLC
223
+ | ElectricalComponentCategory.STEAM_BOILER
218
224
  | ElectricalComponentCategory.STATIC_TRANSFER_SWITCH
219
225
  | ElectricalComponentCategory.UNINTERRUPTIBLE_POWER_SUPPLY
220
226
  | ElectricalComponentCategory.CAPACITOR_BANK
@@ -412,6 +418,7 @@ def _trivial_category_to_class(
412
418
  | Precharger
413
419
  | Breaker
414
420
  | Plc
421
+ | SteamBoiler
415
422
  | StaticTransferSwitch
416
423
  | UninterruptiblePowerSupply
417
424
  | CapacitorBank
@@ -429,6 +436,7 @@ def _trivial_category_to_class(
429
436
  ElectricalComponentCategory.PRECHARGER: Precharger,
430
437
  ElectricalComponentCategory.BREAKER: Breaker,
431
438
  ElectricalComponentCategory.PLC: Plc,
439
+ ElectricalComponentCategory.STEAM_BOILER: SteamBoiler,
432
440
  ElectricalComponentCategory.STATIC_TRANSFER_SWITCH: StaticTransferSwitch,
433
441
  ElectricalComponentCategory.UNINTERRUPTIBLE_POWER_SUPPLY: UninterruptiblePowerSupply,
434
442
  ElectricalComponentCategory.CAPACITOR_BANK: CapacitorBank,
@@ -0,0 +1,20 @@
1
+ # License: MIT
2
+ # Copyright © 2026 Frequenz Energy-as-a-Service GmbH
3
+
4
+ """Steam boiler component."""
5
+
6
+ import dataclasses
7
+ from typing import Literal
8
+
9
+ from ._category import ElectricalComponentCategory
10
+ from ._electrical_component import ElectricalComponent
11
+
12
+
13
+ @dataclasses.dataclass(frozen=True, kw_only=True)
14
+ class SteamBoiler(ElectricalComponent):
15
+ """A steam boiler."""
16
+
17
+ category: Literal[ElectricalComponentCategory.STEAM_BOILER] = (
18
+ ElectricalComponentCategory.STEAM_BOILER
19
+ )
20
+ """The category of this component."""
@@ -26,6 +26,7 @@ from ._problematic import (
26
26
  UnspecifiedComponent,
27
27
  )
28
28
  from ._static_transfer_switch import StaticTransferSwitch
29
+ from ._steam_boiler import SteamBoiler
29
30
  from ._uninterruptible_power_supply import UninterruptiblePowerSupply
30
31
  from ._wind_turbine import WindTurbine
31
32
 
@@ -60,6 +61,7 @@ ElectricalComponentType: TypeAlias = (
60
61
  | Precharger
61
62
  | Breaker
62
63
  | Plc
64
+ | SteamBoiler
63
65
  | StaticTransferSwitch
64
66
  | UninterruptiblePowerSupply
65
67
  | CapacitorBank