pygazpar 0.1.21__py3-none-any.whl → 1.3.0b2__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- pygazpar/__init__.py +10 -3
- pygazpar/__main__.py +81 -58
- pygazpar/client.py +64 -210
- pygazpar/datasource.py +629 -0
- pygazpar/enum.py +31 -8
- pygazpar/excelparser.py +138 -0
- pygazpar/jsonparser.py +53 -0
- pygazpar/resources/daily_data_sample.json +7802 -0
- pygazpar/resources/hourly_data_sample.json +1 -0
- pygazpar/resources/monthly_data_sample.json +146 -0
- pygazpar/resources/weekly_data_sample.json +614 -0
- pygazpar/resources/yearly_data_sample.json +18 -0
- pygazpar/version.py +3 -0
- pygazpar-0.1.21.dist-info/LICENSE.txt → pygazpar-1.3.0b2.dist-info/LICENSE +21 -21
- pygazpar-1.3.0b2.dist-info/METADATA +220 -0
- pygazpar-1.3.0b2.dist-info/RECORD +17 -0
- {pygazpar-0.1.21.dist-info → pygazpar-1.3.0b2.dist-info}/WHEEL +1 -2
- pygazpar/webdriverwrapper.py +0 -125
- pygazpar/webelementwrapper.py +0 -40
- pygazpar-0.1.21.dist-info/METADATA +0 -149
- pygazpar-0.1.21.dist-info/RECORD +0 -14
- pygazpar-0.1.21.dist-info/entry_points.txt +0 -3
- pygazpar-0.1.21.dist-info/top_level.txt +0 -2
- test/__init__.py +0 -1
- test/test_client.py +0 -50
@@ -0,0 +1,220 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: pygazpar
|
3
|
+
Version: 1.3.0b2
|
4
|
+
Summary: Python library to download gas consumption from a GrDF (French Gas Company) account
|
5
|
+
License: MIT License
|
6
|
+
|
7
|
+
Copyright (c) 2025 Stéphane Senart
|
8
|
+
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
11
|
+
in the Software without restriction, including without limitation the rights
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
14
|
+
furnished to do so, subject to the following conditions:
|
15
|
+
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
17
|
+
copies or substantial portions of the Software.
|
18
|
+
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
25
|
+
SOFTWARE.
|
26
|
+
Author: Stéphane Senart
|
27
|
+
Requires-Python: >=3.9
|
28
|
+
Classifier: Programming Language :: Python :: 3.9
|
29
|
+
Classifier: Programming Language :: Python :: 3.10
|
30
|
+
Classifier: Programming Language :: Python :: 3.11
|
31
|
+
Classifier: Programming Language :: Python :: 3.12
|
32
|
+
Classifier: Programming Language :: Python :: 3.13
|
33
|
+
Requires-Dist: openpyxl (>=3.1.5,<4.0.0)
|
34
|
+
Requires-Dist: pandas (>=2.2.3,<3.0.0)
|
35
|
+
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
36
|
+
Description-Content-Type: text/markdown
|
37
|
+
|
38
|
+
# PyGazpar
|
39
|
+
|
40
|
+
## $\text{\color{green}{!!! This library is working again. CAPTCHA has been removed !!!}}$
|
41
|
+
|
42
|
+
PyGazpar is a Python library for getting natural gas consumption from GrDF French provider.
|
43
|
+
|
44
|
+
Their natural gas meter is called Gazpar. It is wireless and transmit the gas consumption once per day.
|
45
|
+
|
46
|
+
All consumption data is available on the client account at GrDF Web Site (https://monespace.grdf.fr).
|
47
|
+
|
48
|
+
PyGazpar automatically goes through the Web Site and download the consumption data, and make it available in a Python structure.
|
49
|
+
|
50
|
+
## Installation
|
51
|
+
|
52
|
+
### Requirements
|
53
|
+
PyGazpar does not require Selenium and corresponding geckodriver to work.
|
54
|
+
|
55
|
+
With the new GrDF web site, it is possible to load the consumption data far easily than before.
|
56
|
+
|
57
|
+
PyGazpar uses [Poetry](https://python-poetry.org/) for dependency and package management.
|
58
|
+
|
59
|
+
### Create your virtual environment
|
60
|
+
|
61
|
+
```bash
|
62
|
+
$ cd /path/to/my_project_folder/
|
63
|
+
|
64
|
+
$ poetry install
|
65
|
+
```
|
66
|
+
|
67
|
+
### PyGazpar installation
|
68
|
+
|
69
|
+
Use the package manager [pip](https://pip.pypa.io/en/stable/) to install PyGazpar.
|
70
|
+
|
71
|
+
```bash
|
72
|
+
pip install pygazpar
|
73
|
+
```
|
74
|
+
|
75
|
+
You can also download the source code and install it manually.
|
76
|
+
```bash
|
77
|
+
cd /path/to/pygazpar/
|
78
|
+
|
79
|
+
$ poetry install
|
80
|
+
```
|
81
|
+
|
82
|
+
## Usage
|
83
|
+
|
84
|
+
#### Command line:
|
85
|
+
|
86
|
+
1. Standard usage (using Json GrDF API).
|
87
|
+
|
88
|
+
```bash
|
89
|
+
$ pygazpar -u 'your login' -p 'your password' -c 'your PCE identifier' --datasource 'json'
|
90
|
+
```
|
91
|
+
|
92
|
+
2. Alternate usage (using Excel GrDF document).
|
93
|
+
|
94
|
+
```bash
|
95
|
+
$ pygazpar -u 'your login' -p 'your password' -c 'your PCE identifier' -t 'temporary directory where to store Excel file (ex: /tmp)' --datasource 'excel'
|
96
|
+
```
|
97
|
+
|
98
|
+
3. Test usage (using local static data files, do not connect to GrDF site).
|
99
|
+
|
100
|
+
```bash
|
101
|
+
$ pygazpar -u 'your login' -p 'your password' -c 'your PCE identifier' --datasource 'test'
|
102
|
+
```
|
103
|
+
|
104
|
+
#### Library:
|
105
|
+
|
106
|
+
1. Standard usage (using Json GrDF API).
|
107
|
+
|
108
|
+
```python
|
109
|
+
import pygazpar
|
110
|
+
|
111
|
+
client = pygazpar.Client(pygazpar.JsonWebDataSource(
|
112
|
+
username='your login',
|
113
|
+
password='your password')
|
114
|
+
)
|
115
|
+
|
116
|
+
data = client.loadSince(pceIdentifier='your PCE identifier',
|
117
|
+
lastNDays=60,
|
118
|
+
frequencies=[pygazpar.Frequency.DAILY, pygazpar.Frequency.MONTHLY])
|
119
|
+
```
|
120
|
+
See [samples/jsonSample.py](samples/jsonSample.py) file for the full example.
|
121
|
+
|
122
|
+
2. Alternate usage (using Excel GrDF document).
|
123
|
+
|
124
|
+
```python
|
125
|
+
import pygazpar
|
126
|
+
|
127
|
+
client = pygazpar.Client(pygazpar.ExcelWebDataSource(
|
128
|
+
username='your login',
|
129
|
+
password='your password')
|
130
|
+
)
|
131
|
+
|
132
|
+
data = client.loadSince(pceIdentifier='your PCE identifier',
|
133
|
+
lastNDays=60,
|
134
|
+
frequencies=[pygazpar.Frequency.DAILY, pygazpar.Frequency.MONTHLY])
|
135
|
+
```
|
136
|
+
See [samples/excelSample.py](samples/jsonSample.py) file for the full example.
|
137
|
+
|
138
|
+
3. Test usage (using local static data files, do not connect to GrDF site).
|
139
|
+
|
140
|
+
```python
|
141
|
+
import pygazpar
|
142
|
+
|
143
|
+
client = pygazpar.Client(pygazpar.TestDataSource())
|
144
|
+
|
145
|
+
data = client.loadSince(pceIdentifier='your PCE identifier',
|
146
|
+
lastNDays=10,
|
147
|
+
frequencies=[pygazpar.Frequency.DAILY, Frequency.MONTHLY])
|
148
|
+
```
|
149
|
+
See [samples/testSample.py](samples/jsonSample.py) file for the full example.
|
150
|
+
|
151
|
+
#### Output:
|
152
|
+
|
153
|
+
```json
|
154
|
+
data =>
|
155
|
+
{
|
156
|
+
"daily": [
|
157
|
+
{
|
158
|
+
"time_period": "13/10/2022",
|
159
|
+
"start_index_m3": 15724,
|
160
|
+
"end_index_m3": 15725,
|
161
|
+
"volume_m3": 2,
|
162
|
+
"energy_kwh": 17,
|
163
|
+
"converter_factor_kwh/m3": 11.16,
|
164
|
+
"temperature_degC": null,
|
165
|
+
"type": "Mesur\u00e9",
|
166
|
+
"timestamp": "2022-12-13T23:58:35.606763"
|
167
|
+
},
|
168
|
+
...
|
169
|
+
{
|
170
|
+
"time_period": "11/12/2022",
|
171
|
+
"start_index_m3": 16081,
|
172
|
+
"end_index_m3": 16098,
|
173
|
+
"volume_m3": 18,
|
174
|
+
"energy_kwh": 201,
|
175
|
+
"converter_factor_kwh/m3": 11.27,
|
176
|
+
"temperature_degC": -1.47,
|
177
|
+
"type": "Mesur\u00e9",
|
178
|
+
"timestamp": "2022-12-13T23:58:35.606763"
|
179
|
+
}
|
180
|
+
],
|
181
|
+
"monthly": [
|
182
|
+
{
|
183
|
+
"time_period": "Novembre 2022",
|
184
|
+
"start_index_m3": 15750,
|
185
|
+
"end_index_m3": 15950,
|
186
|
+
"volume_m3": 204,
|
187
|
+
"energy_kwh": 2227,
|
188
|
+
"timestamp": "2022-12-13T23:58:35.606763"
|
189
|
+
},
|
190
|
+
{
|
191
|
+
"time_period": "D\u00e9cembre 2022",
|
192
|
+
"start_index_m3": 15950,
|
193
|
+
"end_index_m3": 16098,
|
194
|
+
"volume_m3": 148,
|
195
|
+
"energy_kwh": 1664,
|
196
|
+
"timestamp": "2022-12-13T23:58:35.606763"
|
197
|
+
}
|
198
|
+
]
|
199
|
+
}
|
200
|
+
```
|
201
|
+
|
202
|
+
## Limitation
|
203
|
+
PyGazpar relies on how GrDF Web Site is built.
|
204
|
+
|
205
|
+
Any change in the Web site may break this library.
|
206
|
+
|
207
|
+
We expect in close Future that GrDF makes available an open API from which we can get safely their data.
|
208
|
+
|
209
|
+
## Contributing
|
210
|
+
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
211
|
+
|
212
|
+
Please make sure to update tests as appropriate.
|
213
|
+
|
214
|
+
## License
|
215
|
+
[MIT](https://choosealicense.com/licenses/mit/)
|
216
|
+
|
217
|
+
## Project status
|
218
|
+
PyGazpar has been initiated for integration with [Home Assistant](https://www.home-assistant.io/).
|
219
|
+
|
220
|
+
Corresponding Home Assistant integration custom component is available [here](https://github.com/ssenart/home-assistant-gazpar).
|
@@ -0,0 +1,17 @@
|
|
1
|
+
pygazpar/__init__.py,sha256=lz_ZTlZlLQsSX0r81JkDAEWp9nWfGwPemMCTzdS-ar0,344
|
2
|
+
pygazpar/__main__.py,sha256=Va4qiGX1HgtGFbQtpNrcwuieWm1Y-25ttS0swxhd_G0,2910
|
3
|
+
pygazpar/client.py,sha256=TBuVGBvj8hzb7uqjuIJR-XZYoq-C84rAiNmc3DIRcyQ,2688
|
4
|
+
pygazpar/datasource.py,sha256=lFpD4oQps-YyLH9BhAvyZjpT741R-Bm3Rt7slZafEvA,23417
|
5
|
+
pygazpar/enum.py,sha256=3ZCk4SziXF6pxgP3MuQ1qxYfqB3X5DOV8Rtd0GHsK9w,898
|
6
|
+
pygazpar/excelparser.py,sha256=UaPXC5NgCJFB_Xil6ZTKbP3IB-1Si7TInNctbDx8nbU,6245
|
7
|
+
pygazpar/jsonparser.py,sha256=S8r-I98F2UXIx6YAx4LL_ew_LMEdgRoPM8BxNhbBv8I,1985
|
8
|
+
pygazpar/resources/daily_data_sample.json,sha256=YJovtrNUMs257magTfyxiewLmecySFypcelbGFUUeT8,199583
|
9
|
+
pygazpar/resources/hourly_data_sample.json,sha256=N1F-Xz3GaBn2H1p7uKzhkhKCQV8QVR0t76XD6wmFtXA,3
|
10
|
+
pygazpar/resources/monthly_data_sample.json,sha256=yrr4SqrB2MubeVU2HX_FRDZKHIhC0LXCqkO1iqnFWcg,3351
|
11
|
+
pygazpar/resources/weekly_data_sample.json,sha256=AjNuZkZvdYUi-3A_Vho7MA50bUddVvvrZNXechodrAM,15587
|
12
|
+
pygazpar/resources/yearly_data_sample.json,sha256=-h0Oy-4yV6cfTaZ2oLjzEqPtoYxSyJ_48gQ92_rDWcw,446
|
13
|
+
pygazpar/version.py,sha256=y7qXgBBbnxrWN-de04Csq4SiWLqzQBkxux_ScYpNfug,83
|
14
|
+
pygazpar-1.3.0b2.dist-info/LICENSE,sha256=6dtw1Dy7oQSsLgJsLXI99HychyH0Ml45-JuyidjmiHc,1094
|
15
|
+
pygazpar-1.3.0b2.dist-info/METADATA,sha256=0xmnr1TYzyQpxpcZ2U7QwpAXGizorv5HxkE8pquSC6k,6822
|
16
|
+
pygazpar-1.3.0b2.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
|
17
|
+
pygazpar-1.3.0b2.dist-info/RECORD,,
|
pygazpar/webdriverwrapper.py
DELETED
@@ -1,125 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import logging
|
3
|
-
from selenium import webdriver
|
4
|
-
from .webelementwrapper import WebElementWrapper
|
5
|
-
|
6
|
-
# ------------------------------------------------------------------------------------------------------------
|
7
|
-
class WebDriverWrapper:
|
8
|
-
|
9
|
-
logger = logging.getLogger(__name__)
|
10
|
-
|
11
|
-
# ------------------------------------------------------
|
12
|
-
def __init__(self, firefox_webdriver_executable: str, wait_time: int, tmp_directory: str):
|
13
|
-
|
14
|
-
self.__firefox_webdriver_executable = firefox_webdriver_executable
|
15
|
-
self.__wait_time = wait_time
|
16
|
-
self.__tmp_directory = tmp_directory
|
17
|
-
|
18
|
-
# We remove the geckodriver log file
|
19
|
-
geckodriverLogFile = f"{self.__tmp_directory}/pygazpar_geckodriver.log"
|
20
|
-
if os.path.isfile(geckodriverLogFile):
|
21
|
-
os.remove(geckodriverLogFile)
|
22
|
-
|
23
|
-
# Initialize the Firefox WebDriver
|
24
|
-
options = webdriver.FirefoxOptions()
|
25
|
-
#options.log.level = 'trace'
|
26
|
-
options.headless = True
|
27
|
-
profile = webdriver.FirefoxProfile()
|
28
|
-
profile.set_preference('browser.download.folderList', 2) # custom location
|
29
|
-
profile.set_preference('browser.download.manager.showWhenStarting', False)
|
30
|
-
profile.set_preference('browser.helperApps.alwaysAsk.force', False)
|
31
|
-
profile.set_preference('browser.download.dir', self.__tmp_directory)
|
32
|
-
profile.set_preference('browser.helperApps.neverAsk.saveToDisk', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
33
|
-
|
34
|
-
self.__driver = webdriver.Firefox(executable_path=self.__firefox_webdriver_executable, firefox_profile=profile, options=options, service_log_path=geckodriverLogFile)
|
35
|
-
|
36
|
-
self.__driver.set_window_position(0, 0)
|
37
|
-
self.__driver.set_window_size(1920, 1200)
|
38
|
-
#self.__driver.fullscreen_window()
|
39
|
-
|
40
|
-
self.__driver.implicitly_wait(self.__wait_time)
|
41
|
-
|
42
|
-
|
43
|
-
# ------------------------------------------------------
|
44
|
-
def quit(self):
|
45
|
-
|
46
|
-
WebDriverWrapper.logger.debug(f"quit()...")
|
47
|
-
try:
|
48
|
-
self.__driver.quit()
|
49
|
-
WebDriverWrapper.logger.debug(f"quit() -> Ok")
|
50
|
-
except Exception:
|
51
|
-
WebDriverWrapper.logger.warning(f"quit() -> Error", exc_info=True)
|
52
|
-
self.__driver.save_screenshot(f"{self.__tmp_directory}/error_screenshot.png")
|
53
|
-
raise
|
54
|
-
|
55
|
-
|
56
|
-
# ------------------------------------------------------
|
57
|
-
def get(self, url: str, description: str):
|
58
|
-
|
59
|
-
WebDriverWrapper.logger.debug(f"get('{url}'): {description}...")
|
60
|
-
try:
|
61
|
-
res = self.__driver.get(url)
|
62
|
-
WebDriverWrapper.logger.debug(f"get('{url}'): {description} -> Ok")
|
63
|
-
return res
|
64
|
-
except Exception:
|
65
|
-
WebDriverWrapper.logger.warning(f"get('{url}'): {description} -> Error", exc_info=True)
|
66
|
-
self.__driver.save_screenshot(f"{self.__tmp_directory}/error_screenshot.png")
|
67
|
-
raise
|
68
|
-
|
69
|
-
|
70
|
-
# ------------------------------------------------------
|
71
|
-
def current_url(self):
|
72
|
-
|
73
|
-
WebDriverWrapper.logger.debug(f"current_url()...")
|
74
|
-
try:
|
75
|
-
self.__driver.current_url()
|
76
|
-
WebDriverWrapper.logger.debug(f"current_url() -> Ok")
|
77
|
-
except Exception:
|
78
|
-
WebDriverWrapper.logger.warning(f"current_url() -> Error", exc_info=True)
|
79
|
-
self.__driver.save_screenshot(f"{self.__tmp_directory}/error_screenshot.png")
|
80
|
-
raise
|
81
|
-
|
82
|
-
|
83
|
-
# ------------------------------------------------------
|
84
|
-
def find_element_by_id(self, id: str, description: str, screenshotOnNotFound: bool = True) -> WebElementWrapper:
|
85
|
-
|
86
|
-
WebDriverWrapper.logger.debug(f"find_element_by_id('{id}'): {description}...")
|
87
|
-
try:
|
88
|
-
element = self.__driver.find_element_by_id(id)
|
89
|
-
res = WebElementWrapper(element, description, self.__tmp_directory)
|
90
|
-
WebDriverWrapper.logger.debug(f"find_element_by_id('{id}'): {description} -> Ok")
|
91
|
-
return res
|
92
|
-
except Exception:
|
93
|
-
WebDriverWrapper.logger.warning(f"find_element_by_id('{id}'): {description} -> Not found", exc_info=False)
|
94
|
-
if screenshotOnNotFound:
|
95
|
-
self.__driver.save_screenshot(f"{self.__tmp_directory}/error_screenshot.png")
|
96
|
-
raise
|
97
|
-
|
98
|
-
|
99
|
-
# ------------------------------------------------------
|
100
|
-
def find_element_by_xpath(self, xpath: str, description: str, screenshotOnNotFound: bool = True) -> WebElementWrapper:
|
101
|
-
|
102
|
-
WebDriverWrapper.logger.debug(f"find_element_by_xpath('{xpath}'): {description}...")
|
103
|
-
try:
|
104
|
-
element = self.__driver.find_element_by_xpath(xpath)
|
105
|
-
res = WebElementWrapper(element, description, self.__tmp_directory)
|
106
|
-
WebDriverWrapper.logger.debug(f"find_element_by_xpath('{xpath}'): {description} -> Ok")
|
107
|
-
return res
|
108
|
-
except Exception:
|
109
|
-
WebDriverWrapper.logger.warning(f"find_element_by_xpath('{xpath}'): {description} -> Not found", exc_info=False)
|
110
|
-
if screenshotOnNotFound:
|
111
|
-
self.__driver.save_screenshot(f"{self.__tmp_directory}/error_screenshot.png")
|
112
|
-
raise
|
113
|
-
|
114
|
-
|
115
|
-
# ------------------------------------------------------
|
116
|
-
def save_screenshot(self, filename: str):
|
117
|
-
|
118
|
-
WebDriverWrapper.logger.debug(f"save_screenshot('{filename}')...")
|
119
|
-
try:
|
120
|
-
res = self.__driver.save_screenshot(filename)
|
121
|
-
WebDriverWrapper.logger.debug(f"save_screenshot('{filename}') -> Ok")
|
122
|
-
return res
|
123
|
-
except Exception:
|
124
|
-
WebDriverWrapper.logger.warning(f"save_screenshot('{filename}') -> Error", exc_info=True)
|
125
|
-
|
pygazpar/webelementwrapper.py
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
from selenium.webdriver.remote.webelement import WebElement
|
3
|
-
|
4
|
-
# ------------------------------------------------------------------------------------------------------------
|
5
|
-
class WebElementWrapper:
|
6
|
-
|
7
|
-
logger = logging.getLogger(__name__)
|
8
|
-
|
9
|
-
# ------------------------------------------------------
|
10
|
-
def __init__(self, element: WebElement, description: str, tmp_directory: str):
|
11
|
-
|
12
|
-
self.__element = element
|
13
|
-
self.__description = description
|
14
|
-
self.__tmp_directory = tmp_directory
|
15
|
-
|
16
|
-
|
17
|
-
# ------------------------------------------------------
|
18
|
-
def click(self):
|
19
|
-
|
20
|
-
WebElementWrapper.logger.debug(f"click(): {self.__description}...")
|
21
|
-
try:
|
22
|
-
self.__element.click()
|
23
|
-
WebElementWrapper.logger.debug(f"click() -> Ok")
|
24
|
-
except Exception:
|
25
|
-
WebElementWrapper.logger.warning(f"click(): {self.__description} -> Error", exc_info=True)
|
26
|
-
self.__element.parent.save_screenshot(f"{self.__tmp_directory}/error_screenshot.png")
|
27
|
-
raise
|
28
|
-
|
29
|
-
|
30
|
-
# ------------------------------------------------------
|
31
|
-
def send_keys(self, value: str):
|
32
|
-
|
33
|
-
WebElementWrapper.logger.debug(f"send_keys({value}): {self.__description}...")
|
34
|
-
try:
|
35
|
-
self.__element.send_keys(value)
|
36
|
-
WebElementWrapper.logger.debug(f"send_keys({value}) -> Ok")
|
37
|
-
except Exception:
|
38
|
-
WebElementWrapper.logger.warning(f"send_keys({value}): {self.__description} -> Error", exc_info=True)
|
39
|
-
self.__element.parent.save_screenshot(f"{self.__tmp_directory}/error_screenshot.png")
|
40
|
-
raise
|
@@ -1,149 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: pygazpar
|
3
|
-
Version: 0.1.21
|
4
|
-
Summary: Retrieve gas consumption from GrDF web site (French Gas Company)
|
5
|
-
Home-page: https://github.com/ssenart/PyGazpar
|
6
|
-
Author: Stephane Senart
|
7
|
-
Author-email: stephane.senart@gmail.com
|
8
|
-
License: MIT
|
9
|
-
Download-URL: https://github.com/ssenart/pygazpar/releases/tag/0.1.21
|
10
|
-
Keywords: Energy,Natural Gas,Consumption,GrDF,Gazpar
|
11
|
-
Platform: UNKNOWN
|
12
|
-
Classifier: Development Status :: 3 - Alpha
|
13
|
-
Classifier: Topic :: Software Development :: Build Tools
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
15
|
-
Classifier: Operating System :: OS Independent
|
16
|
-
Classifier: Programming Language :: Python :: 3.7
|
17
|
-
Requires-Python: >=3.7
|
18
|
-
Description-Content-Type: text/markdown
|
19
|
-
Requires-Dist: selenium (==3.141)
|
20
|
-
Requires-Dist: openpyxl (==2.6.3)
|
21
|
-
|
22
|
-
# PyGazpar
|
23
|
-
PyGazpar is a Python library for getting natural gas consumption from GrDF French provider.
|
24
|
-
|
25
|
-
Their natural gas meter is called Gazpar. It is wireless and transmit the gas consumption once per day.
|
26
|
-
|
27
|
-
All consumption data is available on the client account at GrDF Web Site (https://monespace.grdf.fr).
|
28
|
-
|
29
|
-
PyGazpar automatically go through the Web Site and download the consumption data Excel file, and make it available in a Python structure (list of dictionaries).
|
30
|
-
|
31
|
-
## Installation
|
32
|
-
|
33
|
-
### Requirements
|
34
|
-
PyGazpar is working with Selenium Python library to automate navigation through GrDF Web site. Selenium requires a WebDriver that acts as gateway between automatic actions from PyGazpar and a native browser already installed on the system.
|
35
|
-
|
36
|
-
PyGazpar has been developped and tested with Firefox browser (version 68.8) and its corresponding Web Driver geckodriver (version 0.24).
|
37
|
-
|
38
|
-
#### Firefox browser installation
|
39
|
-
Follow instructions [here](https://www.mozilla.org/fr/firefox/new)
|
40
|
-
|
41
|
-
#### Firefox Web Driver (geckodriver) installation
|
42
|
-
Follow instructions [here](https://github.com/mozilla/geckodriver/releases)
|
43
|
-
|
44
|
-
### Create your virtual environment
|
45
|
-
```bash
|
46
|
-
$ pip install virtualenv
|
47
|
-
|
48
|
-
$ cd /path/to/my_project_folder/
|
49
|
-
|
50
|
-
$ virtualenv venv
|
51
|
-
```
|
52
|
-
|
53
|
-
### PyGazpar installation
|
54
|
-
Use the package manager [pip](https://pip.pypa.io/en/stable/) to install PyGazpar.
|
55
|
-
|
56
|
-
```bash
|
57
|
-
pip install pygazpar
|
58
|
-
```
|
59
|
-
|
60
|
-
You can also download the source code and install it manually.
|
61
|
-
|
62
|
-
```bash
|
63
|
-
cd /path/to/pygazpar/
|
64
|
-
python setup.py install
|
65
|
-
```
|
66
|
-
|
67
|
-
## Usage
|
68
|
-
|
69
|
-
### Command line
|
70
|
-
|
71
|
-
```bash
|
72
|
-
$ pygazpar -u 'your login' -p 'your password' -w 'path/to/Selenium Web Driver' -s 30 -t 'temporary directory where to store XSLX file (ex: /tmp)'
|
73
|
-
```
|
74
|
-
|
75
|
-
### Library
|
76
|
-
|
77
|
-
```python
|
78
|
-
import pygazpar
|
79
|
-
|
80
|
-
client = pygazpar.Client('your login',
|
81
|
-
'your password',
|
82
|
-
'path/to/Selenium Web Driver',
|
83
|
-
30,
|
84
|
-
'temporary directory where to store XSLX file (ex: /tmp)')
|
85
|
-
|
86
|
-
client.update()
|
87
|
-
|
88
|
-
data = client.data()
|
89
|
-
```
|
90
|
-
|
91
|
-
### Output
|
92
|
-
|
93
|
-
```json
|
94
|
-
data =>
|
95
|
-
[
|
96
|
-
{
|
97
|
-
"date": "01/07/2019",
|
98
|
-
"start_index_m3": 9802.0,
|
99
|
-
"end_index_m3": 9805.0,
|
100
|
-
"volume_m3": 3.6,
|
101
|
-
"energy_kwh": 40.0,
|
102
|
-
"converter_factor": "11,244",
|
103
|
-
"local_temperature": "",
|
104
|
-
"type": "MES",
|
105
|
-
"timestamp": "2019-08-29T16:56:07.380422"
|
106
|
-
},
|
107
|
-
{
|
108
|
-
"date": "02/07/2019",
|
109
|
-
"start_index_m3": 9805.0,
|
110
|
-
"end_index_m3": 9808.0,
|
111
|
-
"volume_m3": 2.8,
|
112
|
-
"energy_kwh": 31.0,
|
113
|
-
"converter_factor": "11,244",
|
114
|
-
"local_temperature": "21",
|
115
|
-
"type": "MES",
|
116
|
-
"timestamp": "2019-08-29T16:56:07.380422"
|
117
|
-
},
|
118
|
-
{
|
119
|
-
"date": "03/07/2019",
|
120
|
-
"start_index_m3": 9808.0,
|
121
|
-
"end_index_m3": 9811.0,
|
122
|
-
"volume_m3": 2.9,
|
123
|
-
"energy_kwh": 33.0,
|
124
|
-
"converter_factor": "11,244",
|
125
|
-
"local_temperature": "",
|
126
|
-
"type": "MES",
|
127
|
-
"timestamp": "2019-08-29T16:56:07.380422"
|
128
|
-
}
|
129
|
-
]
|
130
|
-
```
|
131
|
-
|
132
|
-
## Limitation
|
133
|
-
PyGazpar relies on how GrDF Web Site is built. It goes through each Web pages and automatically fill forms, click buttons using their internal identifiers.
|
134
|
-
|
135
|
-
Any change in the Web site structure or identifier naming may break this library.
|
136
|
-
|
137
|
-
We expect in close Future that GrDF makes available a standard API from which we can get safely their data.
|
138
|
-
|
139
|
-
## Contributing
|
140
|
-
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
141
|
-
|
142
|
-
Please make sure to update tests as appropriate.
|
143
|
-
|
144
|
-
## License
|
145
|
-
[MIT](https://choosealicense.com/licenses/mit/)
|
146
|
-
|
147
|
-
## Project status
|
148
|
-
PyGazpar has been initiated for integration with [Home Assistant](https://www.home-assistant.io/).
|
149
|
-
|
pygazpar-0.1.21.dist-info/RECORD
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
pygazpar/__init__.py,sha256=RKhEDN5euu4BbAOqzyeiQZLUkHSsoBk5n9QUDDkQHyQ,117
|
2
|
-
pygazpar/__main__.py,sha256=00bphprgyfRf876sYx3qtzI4apY1dMUs77ZhFKu7j2Y,2113
|
3
|
-
pygazpar/client.py,sha256=a2UTxDFHj_EsBBQ8q6nmbCPRrMKjhTmy5NiZ1E8Qfkk,9577
|
4
|
-
pygazpar/enum.py,sha256=0xGz4mF2Pl69bQfHjFMEEt2v4EkBJf8t9-1wigXV1VA,333
|
5
|
-
pygazpar/webdriverwrapper.py,sha256=ZH1yvXTS_zFcmB-1X1U25p4KI9JuMSW1V3-mVAIf144,5569
|
6
|
-
pygazpar/webelementwrapper.py,sha256=5Xy1cx5NjNeeugEmxDXOkw-x6L36c7o0rwXh4IUI6rA,1609
|
7
|
-
test/__init__.py,sha256=lnB6CW_tnw1n4rdcjuebVcQ09mrkKu4pG_11JaRyQcg,34
|
8
|
-
test/test_client.py,sha256=qOlIkPVUsYpKncoKSc2bDRTPqYnw4Ja4dJtHjWpKNms,1646
|
9
|
-
pygazpar-0.1.21.dist-info/LICENSE.txt,sha256=XsCJx_7_BC9tvmE0ZxS1cTNR7ekurog_ea9ybdZ-8tc,1073
|
10
|
-
pygazpar-0.1.21.dist-info/METADATA,sha256=SASgbZKGhhhbCXsIPzlDFLXsWMwY4mZmgtCZ2jmg1ps,4367
|
11
|
-
pygazpar-0.1.21.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
|
12
|
-
pygazpar-0.1.21.dist-info/entry_points.txt,sha256=c_FMZPYlRv1w9EqfgWhlkdJOoje7FcglI0UMm2oRLoI,53
|
13
|
-
pygazpar-0.1.21.dist-info/top_level.txt,sha256=IAEv0wRUdR1eygoQs2efTQ0yUHt9wL-3qOeoHpHnA9Q,14
|
14
|
-
pygazpar-0.1.21.dist-info/RECORD,,
|
test/__init__.py
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
from pygazpar.client import Client
|
test/test_client.py
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
import unittest
|
2
|
-
|
3
|
-
from pygazpar.client import Client
|
4
|
-
|
5
|
-
class ClientTestCase(unittest.TestCase):
|
6
|
-
|
7
|
-
username = ""
|
8
|
-
password = ""
|
9
|
-
webdriver = ""
|
10
|
-
wait_time = 30
|
11
|
-
tmp_directory = ""
|
12
|
-
|
13
|
-
def test_client(self):
|
14
|
-
client = Client(self.username, self.password, self.webdriver, self.wait_time, self.tmp_directory)
|
15
|
-
client.update()
|
16
|
-
|
17
|
-
assert len(client.data()) != 0
|
18
|
-
|
19
|
-
if __name__ == "__main__":
|
20
|
-
|
21
|
-
from argparse import ArgumentParser
|
22
|
-
parser = ArgumentParser()
|
23
|
-
parser.add_argument("-u", "--username",
|
24
|
-
required=True,
|
25
|
-
help="GRDF username (email)")
|
26
|
-
parser.add_argument("-p", "--password",
|
27
|
-
required=True,
|
28
|
-
help="GRDF password")
|
29
|
-
parser.add_argument("-w", "--webdriver",
|
30
|
-
required=True,
|
31
|
-
help="Firefox webdriver executable (geckodriver)")
|
32
|
-
parser.add_argument("-s", "--wait_time",
|
33
|
-
required=False,
|
34
|
-
type=int,
|
35
|
-
default=30,
|
36
|
-
help="Wait time in seconds (see https://selenium-python.readthedocs.io/waits.html for details)")
|
37
|
-
parser.add_argument("-t", "--tmpdir",
|
38
|
-
required=False,
|
39
|
-
default="/tmp",
|
40
|
-
help="tmp directory (default is /tmp)")
|
41
|
-
|
42
|
-
args = parser.parse_args()
|
43
|
-
|
44
|
-
ClientTestCase.username = args.username
|
45
|
-
ClientTestCase.password = args.password
|
46
|
-
ClientTestCase.webdriver = args.webdriver
|
47
|
-
ClientTestCase.wait_time = args.wait_time
|
48
|
-
ClientTestCase.tmp_directory = args.tmpdir
|
49
|
-
|
50
|
-
unittest.main()
|