Hydro-Quebec-API-Wrapper 3.2.0__tar.gz → 4.0.0__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 (105) hide show
  1. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/.gitlab-ci.yml +5 -5
  2. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/.pre-commit-config.yaml +7 -6
  3. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/Hydro_Quebec_API_Wrapper.egg-info/PKG-INFO +4 -4
  4. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/Hydro_Quebec_API_Wrapper.egg-info/SOURCES.txt +4 -0
  5. hydro_quebec_api_wrapper-4.0.0/Hydro_Quebec_API_Wrapper.egg-info/requires.txt +5 -0
  6. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/PKG-INFO +4 -4
  7. hydro_quebec_api_wrapper-4.0.0/conflict_test_requirements.txt +1 -0
  8. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/diagnostic.py +48 -5
  9. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/hydro_api/client.py +40 -8
  10. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/hydro_api/consts.py +6 -0
  11. hydro_quebec_api_wrapper-4.0.0/hydroqc/peak/basepeak.py +67 -0
  12. hydro_quebec_api_wrapper-3.2.0/hydroqc/peak/dpc/handler.py → hydro_quebec_api_wrapper-4.0.0/hydroqc/peak/basepeakhandler.py +113 -107
  13. hydro_quebec_api_wrapper-4.0.0/hydroqc/peak/consts.py +3 -0
  14. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/peak/cpc/handler.py +74 -178
  15. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/peak/cpc/peak.py +13 -84
  16. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/peak/dpc/consts.py +0 -1
  17. hydro_quebec_api_wrapper-4.0.0/hydroqc/peak/dpc/handler.py +108 -0
  18. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/peak/dpc/peak.py +7 -21
  19. hydro_quebec_api_wrapper-4.0.0/hydroqc/public_client.py +79 -0
  20. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/types/__init__.py +8 -1
  21. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/types/common.py +32 -1
  22. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/webuser.py +7 -4
  23. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/pyproject.toml +5 -2
  24. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/setup.cfg +3 -3
  25. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/test_requirements.txt +8 -8
  26. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/tests/subtest_common.py +17 -46
  27. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/tests/subtest_contract_d.py +1 -1
  28. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/tests/subtest_contract_d_cpc.py +19 -1
  29. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/tests/subtest_contract_dpc.py +9 -4
  30. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/tests/subtest_contract_dt.py +1 -1
  31. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/tests/subtest_contract_m.py +1 -1
  32. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/tests/subtest_contract_m_gdp.py +1 -1
  33. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/tests/test_timerange.py +13 -5
  34. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/tests/tools.py +2 -2
  35. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/tox.ini +1 -1
  36. hydro_quebec_api_wrapper-3.2.0/Hydro_Quebec_API_Wrapper.egg-info/requires.txt +0 -5
  37. hydro_quebec_api_wrapper-3.2.0/conflict_test_requirements.txt +0 -1
  38. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/.coveragerc +0 -0
  39. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/.devcontainer/devcontainer.json +0 -0
  40. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/.flake8 +0 -0
  41. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/.gitignore +0 -0
  42. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/.pylintrc +0 -0
  43. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/Hydro_Quebec_API_Wrapper.egg-info/dependency_links.txt +0 -0
  44. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/Hydro_Quebec_API_Wrapper.egg-info/entry_points.txt +0 -0
  45. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/Hydro_Quebec_API_Wrapper.egg-info/top_level.txt +0 -0
  46. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/LICENSE +0 -0
  47. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/MANIFEST.in +0 -0
  48. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/README.md +0 -0
  49. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/config.default.yaml +0 -0
  50. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/Makefile +0 -0
  51. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/make.bat +0 -0
  52. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/_static/custom.css +0 -0
  53. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/_static/hydroqc-logo-128.png +0 -0
  54. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/_static/hydroqc-logo.svg +0 -0
  55. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/conf.py +0 -0
  56. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/external/description-des-codes-interruption.pdf +0 -0
  57. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/index.rst +0 -0
  58. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/reference/hydroqc.contract.rst +0 -0
  59. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/reference/hydroqc.hydro_api.rst +0 -0
  60. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/reference/hydroqc.peak.cpc.rst +0 -0
  61. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/reference/hydroqc.peak.dpc.rst +0 -0
  62. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/reference/hydroqc.peak.rst +0 -0
  63. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/reference/hydroqc.rst +0 -0
  64. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/reference/hydroqc.types.rst +0 -0
  65. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/reference/modules.rst +0 -0
  66. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/docs/source/todo.rst +0 -0
  67. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/examples/hydro.py +0 -0
  68. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/__init__.py +0 -0
  69. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/account.py +0 -0
  70. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/consts.py +0 -0
  71. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/contract/__init__.py +0 -0
  72. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/contract/common.py +0 -0
  73. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/contract/contract_d.py +0 -0
  74. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/contract/contract_d_cpc.py +0 -0
  75. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/contract/contract_dpc.py +0 -0
  76. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/contract/contract_dt.py +0 -0
  77. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/contract/contract_m.py +0 -0
  78. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/contract/contract_m_gdp.py +0 -0
  79. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/contract/contract_residential.py +0 -0
  80. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/contract/outage.py +0 -0
  81. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/customer.py +0 -0
  82. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/error.py +0 -0
  83. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/hydro_api/__init__.py +0 -0
  84. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/hydro_api/cache.py +0 -0
  85. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/logger.py +0 -0
  86. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/peak/__init__.py +0 -0
  87. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/peak/cpc/__init__.py +0 -0
  88. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/peak/cpc/consts.py +0 -0
  89. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/peak/dpc/__init__.py +0 -0
  90. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/py.typed +0 -0
  91. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/timerange.py +0 -0
  92. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/types/account.py +0 -0
  93. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/types/consump.py +0 -0
  94. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/types/contract.py +0 -0
  95. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/types/cpc.py +0 -0
  96. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/types/dpc.py +0 -0
  97. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/types/dt.py +0 -0
  98. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/types/outage.py +0 -0
  99. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/hydroqc/utils.py +0 -0
  100. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/logo/hydroqc-logo-128.png +0 -0
  101. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/logo/hydroqc-logo.svg +0 -0
  102. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/renovate.json5 +0 -0
  103. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/tests/__init__.py +0 -0
  104. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/tests/conftest.py +0 -0
  105. {hydro_quebec_api_wrapper-3.2.0 → hydro_quebec_api_wrapper-4.0.0}/tests/test_basic.py +0 -0
@@ -31,7 +31,7 @@ test:
31
31
  stage: test
32
32
  tags:
33
33
  - hydroqc
34
- image: registry.gitlab.com/hydroqc/hydroqc-base-container/3.12@sha256:b66812346075aabc0ee6cf452d734c28589bbe97a82c179b0a95d3a95c04ad41
34
+ image: registry.gitlab.com/hydroqc/hydroqc-base-container/3.12@sha256:d8985672e512080c541beeab0da37aa166a41b24ab9f0cf86bed4944cd743fae
35
35
  script:
36
36
  - tox -e pylint,flake8,pydocstyle,typing,black,deps-conflict --parallel auto
37
37
  - cat mypy-txt-report/index.txt
@@ -70,7 +70,7 @@ test:
70
70
  # Run on shared runners to lighten the load on the private runners
71
71
  # tags:
72
72
  # - hydroqc
73
- image: registry.gitlab.com/hydroqc/hydroqc-base-container/3.12@sha256:b66812346075aabc0ee6cf452d734c28589bbe97a82c179b0a95d3a95c04ad41
73
+ image: registry.gitlab.com/hydroqc/hydroqc-base-container/3.12@sha256:d8985672e512080c541beeab0da37aa166a41b24ab9f0cf86bed4944cd743fae
74
74
  script:
75
75
  - >
76
76
  OUTPUT=$(curl
@@ -257,7 +257,7 @@ private_tests_done:
257
257
  stage: private_tests_done
258
258
  tags:
259
259
  - hydroqc
260
- image: registry.gitlab.com/hydroqc/hydroqc-base-container/3.12@sha256:b66812346075aabc0ee6cf452d734c28589bbe97a82c179b0a95d3a95c04ad41
260
+ image: registry.gitlab.com/hydroqc/hydroqc-base-container/3.12@sha256:d8985672e512080c541beeab0da37aa166a41b24ab9f0cf86bed4944cd743fae
261
261
  script:
262
262
  #- apt-get update && apt-get install --no-install-recommends -y python3.10 python3-venv python3-pip --no-install-recommends git
263
263
  - echo "Private Tests done"
@@ -299,7 +299,7 @@ package:
299
299
  stage: test
300
300
  tags:
301
301
  - hydroqc
302
- image: registry.gitlab.com/hydroqc/hydroqc-base-container/3.12@sha256:b66812346075aabc0ee6cf452d734c28589bbe97a82c179b0a95d3a95c04ad41
302
+ image: registry.gitlab.com/hydroqc/hydroqc-base-container/3.12@sha256:d8985672e512080c541beeab0da37aa166a41b24ab9f0cf86bed4944cd743fae
303
303
  script:
304
304
  # Build a package with a version based on the commmit short sha
305
305
  - pip install --upgrade pip
@@ -329,7 +329,7 @@ promote2pypi:
329
329
  stage: pypi
330
330
  tags:
331
331
  - hydroqc
332
- image: registry.gitlab.com/hydroqc/hydroqc-base-container/3.12@sha256:b66812346075aabc0ee6cf452d734c28589bbe97a82c179b0a95d3a95c04ad41
332
+ image: registry.gitlab.com/hydroqc/hydroqc-base-container/3.12@sha256:d8985672e512080c541beeab0da37aa166a41b24ab9f0cf86bed4944cd743fae
333
333
  script:
334
334
  # TODO: try to pull the good package from gitlab pypi repo
335
335
  # then change the version (promote)
@@ -1,6 +1,6 @@
1
1
  repos:
2
2
  - repo: https://github.com/pre-commit/pre-commit-hooks
3
- rev: v4.6.0
3
+ rev: v5.0.0
4
4
  hooks:
5
5
  - id: trailing-whitespace
6
6
  - id: end-of-file-fixer
@@ -16,13 +16,13 @@ repos:
16
16
  - id: check-ast
17
17
 
18
18
  - repo: https://github.com/psf/black
19
- rev: 24.4.2
19
+ rev: 24.10.0
20
20
  hooks:
21
21
  - id: black
22
22
  language_version: python3
23
23
 
24
24
  - repo: https://github.com/PyCQA/flake8/
25
- rev: 7.0.0
25
+ rev: 7.1.1
26
26
  hooks:
27
27
  - id: flake8
28
28
  additional_dependencies: [flake8-docstrings]
@@ -35,12 +35,12 @@ repos:
35
35
  # additional_dependencies: [toml]
36
36
 
37
37
  - repo: https://github.com/PyCQA/doc8
38
- rev: v1.1.1
38
+ rev: v1.1.2
39
39
  hooks:
40
40
  - id: doc8
41
41
 
42
42
  - repo: https://github.com/pre-commit/mirrors-mypy
43
- rev: v1.10.0
43
+ rev: v1.14.1
44
44
  hooks:
45
45
  - id: mypy
46
46
  args: [--strict, --pretty, --show-error-codes]
@@ -49,6 +49,7 @@ repos:
49
49
  - "types-PyYAML==6.0.12.20240311"
50
50
  - "types-python-dateutil==2.9.0.20240316"
51
51
  - "pytest-stub==1.1.0"
52
- - "aiohttp==3.9.5"
52
+ - "aiohttp==3.10.10"
53
53
  - "aiocache==0.12.2"
54
54
  - "pkce==1.0.3"
55
+ - "pytest-asyncio==0.25.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Hydro-Quebec-API-Wrapper
3
- Version: 3.2.0
3
+ Version: 4.0.0
4
4
  Summary: A wrapper library to access hydro quebec API and more
5
5
  Home-page: https://hydroqc.ca
6
6
  Author-email: Hydroqc Team <info@hydroqc.ca>
@@ -15,10 +15,10 @@ Classifier: Operating System :: OS Independent
15
15
  Requires-Python: >=3.10.0
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
- Requires-Dist: aiohttp==3.9.5
18
+ Requires-Dist: aiohttp==3.10.10
19
19
  Requires-Dist: python-dateutil==2.9.0.post0
20
- Requires-Dist: pytz==2024.1
21
- Requires-Dist: aiocache==0.12.2
20
+ Requires-Dist: pytz==2024.2
21
+ Requires-Dist: aiocache==0.12.3
22
22
  Requires-Dist: pkce==1.0.3
23
23
 
24
24
  # HydroQC - Hydro Quebec API wrapper
@@ -46,6 +46,7 @@ hydroqc/customer.py
46
46
  hydroqc/diagnostic.py
47
47
  hydroqc/error.py
48
48
  hydroqc/logger.py
49
+ hydroqc/public_client.py
49
50
  hydroqc/py.typed
50
51
  hydroqc/timerange.py
51
52
  hydroqc/utils.py
@@ -65,6 +66,9 @@ hydroqc/hydro_api/cache.py
65
66
  hydroqc/hydro_api/client.py
66
67
  hydroqc/hydro_api/consts.py
67
68
  hydroqc/peak/__init__.py
69
+ hydroqc/peak/basepeak.py
70
+ hydroqc/peak/basepeakhandler.py
71
+ hydroqc/peak/consts.py
68
72
  hydroqc/peak/cpc/__init__.py
69
73
  hydroqc/peak/cpc/consts.py
70
74
  hydroqc/peak/cpc/handler.py
@@ -0,0 +1,5 @@
1
+ aiohttp==3.10.10
2
+ python-dateutil==2.9.0.post0
3
+ pytz==2024.2
4
+ aiocache==0.12.3
5
+ pkce==1.0.3
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Hydro-Quebec-API-Wrapper
3
- Version: 3.2.0
3
+ Version: 4.0.0
4
4
  Summary: A wrapper library to access hydro quebec API and more
5
5
  Home-page: https://hydroqc.ca
6
6
  Author-email: Hydroqc Team <info@hydroqc.ca>
@@ -15,10 +15,10 @@ Classifier: Operating System :: OS Independent
15
15
  Requires-Python: >=3.10.0
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
- Requires-Dist: aiohttp==3.9.5
18
+ Requires-Dist: aiohttp==3.10.10
19
19
  Requires-Dist: python-dateutil==2.9.0.post0
20
- Requires-Dist: pytz==2024.1
21
- Requires-Dist: aiocache==0.12.2
20
+ Requires-Dist: pytz==2024.2
21
+ Requires-Dist: aiocache==0.12.3
22
22
  Requires-Dist: pkce==1.0.3
23
23
 
24
24
  # HydroQC - Hydro Quebec API wrapper
@@ -0,0 +1 @@
1
+ mqtt-hass-base==4.4.0
@@ -20,6 +20,7 @@ from dateutil.relativedelta import relativedelta
20
20
  from hydroqc.contract import ContractDCPC, ContractDPC, ContractM
21
21
  from hydroqc.error import HydroQcError
22
22
  from hydroqc.logger import get_logger
23
+ from hydroqc.public_client import PublicClient
23
24
  from hydroqc.webuser import WebUser
24
25
 
25
26
  LOG_LEVEL = (
@@ -39,10 +40,12 @@ class Diagnostic:
39
40
  """Contructor."""
40
41
  # Get base settings
41
42
  self.diagnostic_folder = cli_settings.output_folder
43
+ self.diagnostic_folder_public = os.path.join(self.diagnostic_folder, "public")
42
44
  # Prepare output folder
43
45
  if os.path.exists(self.diagnostic_folder):
44
46
  shutil.rmtree(self.diagnostic_folder)
45
47
  os.makedirs(self.diagnostic_folder)
48
+ os.makedirs(self.diagnostic_folder_public)
46
49
  # Prepare logger
47
50
  self._log_file = os.path.join(self.diagnostic_folder, "diagnostic.log")
48
51
  log_level = cli_settings.log_level
@@ -51,6 +54,7 @@ class Diagnostic:
51
54
  file_handler.setFormatter(self.logger.handlers[0].formatter)
52
55
  file_handler.setLevel(self.logger.level)
53
56
  self.logger.addHandler(file_handler)
57
+ self.logger_public = self.logger.getChild("public")
54
58
  http_log_level = cli_settings.http_log_level
55
59
 
56
60
  # Get settings
@@ -69,6 +73,15 @@ class Diagnostic:
69
73
  log_file=self._log_file,
70
74
  diag_folder=self.diagnostic_folder,
71
75
  )
76
+ self.public_client = PublicClient(
77
+ rate_code="DPC",
78
+ # rate_option_code="CPC",
79
+ verify_ssl=True,
80
+ log_level=log_level,
81
+ http_log_level=http_log_level,
82
+ log_file=self._log_file,
83
+ diag_folder=self.diagnostic_folder_public,
84
+ )
72
85
 
73
86
  def zip(self) -> None:
74
87
  """Archive all generated files."""
@@ -87,6 +100,11 @@ class Diagnostic:
87
100
  """Get diagnostic http ID."""
88
101
  return self.webuser._hydro_client._diag_id
89
102
 
103
+ @property
104
+ def public_diag_id(self) -> int:
105
+ """Get diagnostic http ID."""
106
+ return self.public_client._hydro_client._diag_id
107
+
90
108
  async def run(self) -> None: # pylint: disable=too-many-statements
91
109
  """Run diagnostic."""
92
110
  today = datetime.date.today()
@@ -94,6 +112,24 @@ class Diagnostic:
94
112
  day_2 = today - datetime.timedelta(days=2)
95
113
 
96
114
  try:
115
+ self.logger_public.info(
116
+ "%02d - Checking HQ portal status", self.public_diag_id
117
+ )
118
+ hq_status = await self.public_client.check_hq_portal_status()
119
+ self.logger_public.info(
120
+ "%02d - HQ portal available: %s", self.public_diag_id - 1, hq_status
121
+ )
122
+
123
+ self.logger_public.info(
124
+ "%02d - Fetch peak using open data", self.public_diag_id
125
+ )
126
+ await self.public_client.fetch_peak_data()
127
+ self.logger_public.info(
128
+ "%02d - Peak found: %s",
129
+ self.public_diag_id - 1,
130
+ len(self.public_client.peaks),
131
+ )
132
+
97
133
  self.logger.info("%02d - Checking HQ portal status", self.diag_id)
98
134
  hq_status = await self.webuser.check_hq_portal_status()
99
135
  self.logger.info(
@@ -267,6 +303,10 @@ class Diagnostic:
267
303
  await contract_dcpc.peak_handler.refresh_data()
268
304
  self.logger.info("%02d - Got cpc data", self.diag_id - 1)
269
305
 
306
+ self.logger.info("%02d - Trying to get opn data", self.diag_id)
307
+ await contract_dcpc.peak_handler.refresh_open_data()
308
+ self.logger.info("%02d - Got open data", self.diag_id - 1)
309
+
270
310
  # DCP/FlexD
271
311
  elif self.contract.rate == "DPC":
272
312
  self.logger.info("%02d - Trying to get flexD data", self.diag_id)
@@ -289,6 +329,10 @@ class Diagnostic:
289
329
  await contract_dpc.peak_handler.refresh_data()
290
330
  self.logger.info("%02d - Got flexD data", self.diag_id - 1)
291
331
 
332
+ self.logger.info("%02d - Trying to get opn data", self.diag_id)
333
+ await contract_dcpc.peak_handler.refresh_open_data()
334
+ self.logger.info("%02d - Got open data", self.diag_id - 1)
335
+
292
336
  # DT
293
337
  if self.contract.rate == "DT":
294
338
  low_price = contract_dpc.cp_lower_price_consumption
@@ -330,6 +374,9 @@ class Diagnostic:
330
374
  traceback.print_exception(exp, file=fhtb)
331
375
  fhtb.seek(0)
332
376
  self.logger.error(fhtb.read())
377
+ finally:
378
+ await self.webuser.close_session()
379
+ await self.public_client.close_session()
333
380
 
334
381
 
335
382
  def parse_args() -> argparse.Namespace:
@@ -380,14 +427,10 @@ def main() -> None:
380
427
  """Run diagnostic."""
381
428
  cli_settings = parse_args()
382
429
 
383
- loop = asyncio.get_event_loop()
384
430
  diag = Diagnostic(cli_settings)
385
431
 
386
432
  # Fetch data
387
- loop.run_until_complete(diag.run())
388
- close_fut = asyncio.gather(diag.webuser.close_session())
389
- loop.run_until_complete(close_fut)
390
- loop.close()
433
+ asyncio.run(diag.run())
391
434
  # zip
392
435
  diag.zip()
393
436
 
@@ -14,11 +14,11 @@ from datetime import date, datetime, timedelta
14
14
  from importlib.metadata import PackageNotFoundError, version
15
15
  from io import StringIO
16
16
  from json import dumps as json_dumps
17
- from typing import Any
18
- from pkce import generate_pkce_pair
17
+ from typing import Any, cast
19
18
 
20
19
  import aiohttp
21
20
  import yarl
21
+ from pkce import generate_pkce_pair
22
22
 
23
23
  from hydroqc.error import HydroQcError, HydroQcHTTPError
24
24
  from hydroqc.hydro_api.cache import CCached
@@ -43,6 +43,7 @@ from hydroqc.hydro_api.consts import (
43
43
  HOURLY_CONSUMPTION_API_URL,
44
44
  IS_HYDRO_PORTAL_UP_URL,
45
45
  MONTHLY_DATA_URL,
46
+ OPEN_DATA_PEAK_URL,
46
47
  OUTAGES,
47
48
  PERIOD_DATA_URL,
48
49
  PORTRAIT_URL,
@@ -67,6 +68,8 @@ from hydroqc.types import (
67
68
  InfoAccountTyping,
68
69
  ListAccountsContractsTyping,
69
70
  ListContractsTyping,
71
+ OpenDataPeakEvent,
72
+ OpenDataPeakEventOffer,
70
73
  OutageListTyping,
71
74
  PeriodDataTyping,
72
75
  )
@@ -76,6 +79,8 @@ from hydroqc.utils import EST_TIMEZONE
76
79
  class HydroClient:
77
80
  """HydroQc HTTP Client."""
78
81
 
82
+ _cookie_jar: aiohttp.CookieJar
83
+
79
84
  def __init__(
80
85
  self,
81
86
  username: str,
@@ -91,7 +96,6 @@ class HydroClient:
91
96
  self.password: str = password
92
97
  self._timeout: int = timeout
93
98
  self._session: aiohttp.ClientSession | None = session
94
- self._cookie_jar: aiohttp.CookieJar = aiohttp.CookieJar()
95
99
  self._verify_ssl: bool = verify_ssl
96
100
 
97
101
  # OAuth handling variables
@@ -119,7 +123,7 @@ class HydroClient:
119
123
  self.refresh_token = ""
120
124
  self.refresh_token_expiry = datetime.now()
121
125
  self.web_session_expiry = datetime.now()
122
- self._cookie_jar = aiohttp.CookieJar()
126
+ # self._cookie_jar = aiohttp.CookieJar()
123
127
  self._selected_customer = ""
124
128
  self._selected_contract = ""
125
129
 
@@ -234,16 +238,20 @@ class HydroClient:
234
238
  self._logger.debug("JSON received : ", data)
235
239
  raise HydroQcHTTPError("Bad JSON format") from exp
236
240
 
237
- def get_token_data(self) -> IDTokenTyping:
241
+ def get_token_data(self) -> IDTokenTyping | None:
238
242
  """Decode id token data."""
243
+ if not self._id_token:
244
+ return None
239
245
  raw_token_data = self._id_token.split(".")[1]
240
246
  # In some cases padding get lost, adding it to avoid issues with base64 decode
241
247
  raw_token_data += "=" * ((4 - len(raw_token_data) % 4) % 4)
242
248
  token_data: IDTokenTyping = self._load_json(base64.b64decode(raw_token_data))
243
249
  return token_data
244
250
 
245
- def _get_httpsession(self) -> None:
251
+ async def _get_httpsession(self) -> None:
246
252
  """Set http session."""
253
+ if not hasattr(self, "_cookie_jar"):
254
+ self._cookie_jar = aiohttp.CookieJar()
247
255
  if self._session is None:
248
256
  self._logger.debug("Creating new http session")
249
257
  self._session = aiohttp.ClientSession(
@@ -276,7 +284,7 @@ class HydroClient:
276
284
  async def check_portal_status(self) -> bool:
277
285
  """Check if Hydro Quebec portal is UP."""
278
286
  # Get http session
279
- self._get_httpsession()
287
+ await self._get_httpsession()
280
288
  self._logger.info("Checking if the Hydro Quebec portal is UP")
281
289
  api_call_response = await self.http_request(
282
290
  IS_HYDRO_PORTAL_UP_URL,
@@ -301,7 +309,7 @@ class HydroClient:
301
309
  code_verifier, code_challenge = generate_pkce_pair()
302
310
 
303
311
  # Get http session
304
- self._get_httpsession()
312
+ await self._get_httpsession()
305
313
 
306
314
  url = f"{AUTHORIZE_URL}"
307
315
  headers = {
@@ -912,3 +920,27 @@ class HydroClient:
912
920
  return content
913
921
  data_csv: Iterator[Any] = csv.reader(content, delimiter=";")
914
922
  return data_csv
923
+
924
+ @CCached(ttl=300)
925
+ async def get_open_data_peaks(
926
+ self,
927
+ offer: OpenDataPeakEventOffer | None = None,
928
+ ) -> list[OpenDataPeakEvent]:
929
+ """Get the list of peak event from open data url."""
930
+ await self._get_httpsession()
931
+ res = await self.http_request(OPEN_DATA_PEAK_URL, "get")
932
+ content = await res.text()
933
+ try:
934
+ raw_res_json = json.loads(content)
935
+ except json.JSONDecodeError:
936
+ # Try to clean up json removing comments
937
+ cleaned_json = ""
938
+ res_text = await res.text()
939
+ for line in res_text.splitlines():
940
+ if not line.startswith("/") and not line.startswith("#"):
941
+ cleaned_json += line
942
+ raw_res_json = json.loads(cleaned_json)
943
+ res_json = cast(list[OpenDataPeakEvent], raw_res_json.get("evenements", []))
944
+ if not offer:
945
+ return res_json
946
+ return [e for e in res_json if e["offre"] == offer]
@@ -15,6 +15,7 @@ HOST_SERVICES = "https://services-cl.solutions.hydroquebec.com"
15
15
  # HOST_SPRING = "https://cl-ec-lsw.solutions.hydroquebec.com"
16
16
  HOST_RB_SOL = "https://rb.solutions.hydroquebec.com"
17
17
  HOST_OUTAGES = "https://services-bs.solutions.hydroquebec.com"
18
+ HOST_OPEN_DATA = "https://donnees.solutions.hydroquebec.com"
18
19
 
19
20
  # Azure B2C
20
21
  AZB2C_TENANT_ID = "32bf9b91-0a36-4385-b231-d9a8fa3b05ab"
@@ -47,6 +48,11 @@ SECURITY_URL = f"{HOST_SESSION}/config/security.json"
47
48
  SESSION_REFRESH_URL = f"{HOST_SESSION}/oauth2/callback/silent-refresh"
48
49
  LOGIN_URL_6 = f"{HOST_SERVICES}/wsapi/web/prive/api/v3_0/conversion/codeAcces"
49
50
 
51
+ # OPEN DATA PEAK URL
52
+ OPEN_DATA_PEAK_URL = (
53
+ f"{HOST_OPEN_DATA}/donnees-ouvertes/data/json/pointeshivernales.json"
54
+ )
55
+
50
56
  # Initialization uri
51
57
  RELATION_URL = f"{HOST_SERVICES}/wsapi/web/prive/api/v1_0/relations"
52
58
 
@@ -0,0 +1,67 @@
1
+ """Base Peak module."""
2
+
3
+ import datetime
4
+
5
+ from hydroqc.error import HydroQcCPCPeakError
6
+ from hydroqc.timerange import TimeRange
7
+ from hydroqc.utils import EST_TIMEZONE
8
+
9
+
10
+ class BasePeak(TimeRange):
11
+ """Base peak class."""
12
+
13
+ _start_date: datetime.datetime
14
+ _end_date: datetime.datetime
15
+
16
+ def __init__(self, start_date: datetime.datetime, end_date: datetime.datetime):
17
+ """Build base peak."""
18
+ if not start_date.tzinfo:
19
+ self._start_date = start_date.astimezone(EST_TIMEZONE)
20
+ else:
21
+ self._start_date = start_date
22
+ if not end_date.tzinfo:
23
+ self._end_date = end_date.astimezone(EST_TIMEZONE)
24
+ else:
25
+ self._end_date = end_date
26
+
27
+ super().__init__(self.start_date, self.end_date, is_critical=False)
28
+
29
+ @property
30
+ def is_morning(self) -> bool:
31
+ """Return True if it's a morning peak."""
32
+ return self._start_date.hour < 12
33
+
34
+ @property
35
+ def is_evening(self) -> bool:
36
+ """Return True if it's a evening peak."""
37
+ return self._start_date.hour > 12
38
+
39
+ @property
40
+ def day(self) -> datetime.date:
41
+ """Get the day, without time, of the peak."""
42
+ return self._start_date.date()
43
+
44
+ @property
45
+ def date(self) -> datetime.date:
46
+ """DEPRECATED - Get the day, without time, of the peak."""
47
+ return self.day
48
+
49
+ @property
50
+ def start_date(self) -> datetime.datetime:
51
+ """Get the start date of the peak."""
52
+ return self._start_date
53
+
54
+ @property
55
+ def end_date(self) -> datetime.datetime:
56
+ """Get the end date of the peak."""
57
+ return self._end_date
58
+
59
+ @property
60
+ def morning_evening(self) -> str:
61
+ """Return evening or morning peak value."""
62
+ if self.is_morning:
63
+ return "morning"
64
+ if self.is_evening:
65
+ return "evening"
66
+ msg = "Can not determine if is morning or evening peak"
67
+ raise HydroQcCPCPeakError(msg)