-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
128 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,5 @@ build/ | |
pyecodevices.egg-info | ||
__pycache__ | ||
publish | ||
.vscode/ | ||
.vscode/ | ||
test.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,9 @@ | ||
# Changelog | ||
|
||
## 1.2.0 | ||
|
||
- Make async requests | ||
|
||
## 1.1.0 | ||
|
||
- Use XML API of the Eco-Devices to get more information | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,104 +1,161 @@ | ||
"""Get information from GCE Eco-Devices.""" | ||
import requests | ||
import asyncio | ||
import socket | ||
|
||
import aiohttp | ||
import async_timeout | ||
import xmltodict | ||
|
||
|
||
class EcoDevices: | ||
"""Class representing the Eco-Devices and its XML API.""" | ||
|
||
def __init__(self, host, port=80, username=None, password=None, timeout=3): | ||
def __init__( | ||
self, | ||
host: str, | ||
port: int = 80, | ||
username: str = None, | ||
password: str = None, | ||
request_timeout: int = 10, | ||
session: aiohttp.client.ClientSession = None | ||
) -> None: | ||
"""Init a EcoDevice API.""" | ||
self._host = host | ||
self._port = port | ||
self._username = username | ||
self._password = password | ||
self._timeout = timeout | ||
self._request_timeout = request_timeout | ||
self._api_url = f"http://{host}:{port}/status.xml" | ||
|
||
def _request(self): | ||
if self._username is not None and self._password is not None: | ||
r = requests.get( | ||
self._api_url, | ||
params={}, | ||
auth=(self._username, self._password), | ||
timeout=self._timeout, | ||
) | ||
else: | ||
r = requests.get(self._api_url, params={}, timeout=self._timeout) | ||
r.raise_for_status() | ||
xml_content = xmltodict.parse(r.text) | ||
response = xml_content.get("response", None) | ||
if response: | ||
return response | ||
else: | ||
raise Exception( | ||
"Eco-Devices XML request error, url: %s`r%s", | ||
r.request.url, | ||
response, | ||
self._session = session | ||
self._close_session = False | ||
|
||
async def _request(self) -> dict: | ||
"""Make a request to get Eco-Devices data.""" | ||
auth = None | ||
if self._username and self._password: | ||
auth = aiohttp.BasicAuth(self._username, self._password) | ||
|
||
if self._session is None: | ||
self._session = aiohttp.ClientSession() | ||
self._close_session = True | ||
|
||
try: | ||
with async_timeout.timeout(self._request_timeout): | ||
response = await self._session.request( | ||
"GET", | ||
self._api_url, | ||
auth=auth, | ||
data=None, | ||
json=None, | ||
params=None, | ||
headers={}, | ||
ssl=False, | ||
) | ||
except asyncio.TimeoutError as exception: | ||
raise CannotConnectError( | ||
"Timeout occurred while connecting to Eco-Devices." | ||
) from exception | ||
except (aiohttp.ClientError, socket.gaierror) as exception: | ||
raise CannotConnectError( | ||
"Error occurred while communicating with Eco-Devices." | ||
) from exception | ||
if response.status == 401: | ||
raise InvalidAuthError( | ||
"Authentication failed with Eco-Devices." | ||
) | ||
|
||
if response.status: | ||
contents = await response.text() | ||
response.close() | ||
xml_content = xmltodict.parse(contents) | ||
data = xml_content.get("response", None) | ||
if data: | ||
return data | ||
raise CannotConnectError("Eco-Devices XML request error:", data) | ||
|
||
@property | ||
def host(self): | ||
def host(self) -> str: | ||
"""Return the hostname.""" | ||
return self._host | ||
|
||
@property | ||
def mac_address(self): | ||
async def mac_address(self) -> str: | ||
"""Return the mac address.""" | ||
return self._request().get("config_mac") | ||
data = await self._request() | ||
return data["config_mac"] | ||
|
||
@property | ||
def firmware(self): | ||
"""Return the firmware.""" | ||
return self._request().get("version") | ||
async def firmware(self) -> str: | ||
"""Return the mac address.""" | ||
data = await self._request() | ||
return data["version"] | ||
|
||
def ping(self) -> bool: | ||
async def ping(self) -> bool: | ||
"""Return true if Eco-Devices answer to API request.""" | ||
try: | ||
self._request() | ||
if await self._request(): | ||
return True | ||
except Exception: | ||
pass | ||
return False | ||
|
||
def global_get(self): | ||
async def global_get(self) -> dict: | ||
"""Return all values from API.""" | ||
return self._request() | ||
return await self._request() | ||
|
||
def get_t1(self): | ||
async def get_t1(self) -> dict: | ||
"""Get values from teleinformation 1 input.""" | ||
data = self._request() | ||
data = await self._request() | ||
return { | ||
"current": data.get("T1_PAPP"), | ||
"type_heures": data.get("T1_PTEC"), | ||
"souscription": data.get("T1_ISOUSC"), | ||
"intensite_max": data.get("T1_IMAX"), | ||
} | ||
|
||
def get_t2(self): | ||
async def get_t2(self) -> dict: | ||
"""Get values from teleinformation 1 input.""" | ||
data = self._request() | ||
data = await self._request() | ||
return { | ||
"current": data.get("T2_PAPP"), | ||
"type_heures": data.get("T2_PTEC"), | ||
"souscription": data.get("T2_ISOUSC"), | ||
"intensite_max": data.get("T2_IMAX"), | ||
} | ||
|
||
def get_c1(self): | ||
async def get_c1(self) -> dict: | ||
"""Get values from meter 1 input.""" | ||
data = self._request() | ||
data = await self._request() | ||
return { | ||
"daily": data.get("c0day"), | ||
"total": data.get("count0"), | ||
"fuel": data.get("c0_fuel"), | ||
} | ||
|
||
def get_c2(self): | ||
async def get_c2(self) -> dict: | ||
"""Get values from meter 2 input.""" | ||
data = self._request() | ||
data = await self._request() | ||
return { | ||
"daily": data.get("c1day"), | ||
"total": data.get("count1"), | ||
"fuel": data.get("c1_fuel"), | ||
} | ||
|
||
async def close(self) -> None: | ||
"""Close open client session.""" | ||
if self._session and self._close_session: | ||
await self._session.close() | ||
|
||
async def __aenter__(self): | ||
"""Async enter.""" | ||
return self | ||
|
||
async def __aexit__(self, *_exc_info) -> None: | ||
"""Async exit.""" | ||
await self.close() | ||
|
||
|
||
class CannotConnectError(Exception): | ||
"""Exception to indicate an error in connection.""" | ||
|
||
|
||
class InvalidAuthError(Exception): | ||
"""Exception to indicate an error in authentication.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters