pyindrav2h 0.0.2__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 creatingwake
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,121 @@
1
+ Metadata-Version: 2.1
2
+ Name: pyindrav2h
3
+ Version: 0.0.2
4
+ Summary: API client and example CLI to interact with Indra V2H Chargers
5
+ Author-email: Paul Morris <paul@creatingwake.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2024 creatingwake
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://github.com/creatingwake/pyindrav2h
28
+ Keywords: v2h,indra,v2g,v2x
29
+ Classifier: License :: OSI Approved :: MIT License
30
+ Classifier: Programming Language :: Python
31
+ Classifier: Programming Language :: Python :: 3
32
+ Requires-Python: >=3.9
33
+ Description-Content-Type: text/markdown
34
+ License-File: LICENSE
35
+ Requires-Dist: httpx
36
+ Requires-Dist: bs4
37
+ Requires-Dist: lxml
38
+
39
+
40
+ # pyindrav2h
41
+
42
+ A basic API client and example CLI to interact with Indra V2H Chargers. Required by Home Assistant.
43
+
44
+ This is a very early Alpha release, and functionality may change very rapidly.
45
+
46
+ NOTE: Indra Renewable Technologies Limited are aware of this integration. However, this is an unofficial integration and Indra are not able to provide support for direct API integrations. The Indra API will likely change in future which may result in functionality provided by this integration failing at any time.
47
+
48
+
49
+
50
+ ## Installation
51
+
52
+ Install pyindrav2h with pip. Using venv is recommended.
53
+
54
+ ```bash
55
+ pip install pyindrav2h
56
+ ```
57
+ to update pyindrav2h
58
+ ```bash
59
+ pip install pyindrav2h -U
60
+ ```
61
+ On installation a CLI will become available: ```indracli```
62
+ ## Usage/Examples
63
+
64
+ ### CLI
65
+
66
+ ```bash
67
+ usage: indracli [-h] [-u EMAIL] [-p PASSWORD] [-d]
68
+ {statistics,device,all,loadmatch,idle,schedule} ...
69
+
70
+ Indra V2H CLI
71
+
72
+ positional arguments:
73
+ {statistics,device,all,loadmatch,idle,schedule}
74
+ statistics show device statistics
75
+ device show device info
76
+ all show all info
77
+ loadmatch set mode to load matching
78
+ idle set mode to IDLE
79
+ schedule return to scheuduled mode
80
+
81
+ options:
82
+ -h, --help show this help message and exit
83
+ -u EMAIL, --email EMAIL
84
+ -p PASSWORD, --password PASSWORD
85
+ -d, --debug
86
+ ```
87
+
88
+ It is possible to provide a configuration file to provide Indra Smart Portal credentials. If no username/email or password is provided it will be retrieved from ```./.indra.cfg``` or ```~/.indra.cfg```
89
+
90
+ #### Example .indra.cfg Configuration file
91
+ ```
92
+ [indra-account]
93
+ email=useremail@email.com
94
+ password=yourindrapassword
95
+ ```
96
+
97
+ ### Library Usage
98
+
99
+ Intended for use with Indra V2H Home Assistant integration.
100
+ Documentation to follow.
101
+
102
+ ## Support
103
+
104
+ This is a community project that lacks formal support.
105
+
106
+
107
+ For support from the community please join the Indra V2H trial support community: https://indra.v2h.zendesk.com/hc/en-gb/community/topics
108
+
109
+
110
+
111
+ For bugs or feature requests please create a GitHub Issue: https://github.com/creatingwake/pyindrav2h/issues
112
+
113
+ ---
114
+ #### NOTE: Please do not contact Indra Support. Indra are unable to assist with unofficial API integrations.
115
+
116
+
117
+ ## Acknowledgements
118
+
119
+ - [trizmark](https://github.com/trizmark) for help with home assistant API integration examples
120
+
121
+
@@ -0,0 +1,83 @@
1
+
2
+ # pyindrav2h
3
+
4
+ A basic API client and example CLI to interact with Indra V2H Chargers. Required by Home Assistant.
5
+
6
+ This is a very early Alpha release, and functionality may change very rapidly.
7
+
8
+ NOTE: Indra Renewable Technologies Limited are aware of this integration. However, this is an unofficial integration and Indra are not able to provide support for direct API integrations. The Indra API will likely change in future which may result in functionality provided by this integration failing at any time.
9
+
10
+
11
+
12
+ ## Installation
13
+
14
+ Install pyindrav2h with pip. Using venv is recommended.
15
+
16
+ ```bash
17
+ pip install pyindrav2h
18
+ ```
19
+ to update pyindrav2h
20
+ ```bash
21
+ pip install pyindrav2h -U
22
+ ```
23
+ On installation a CLI will become available: ```indracli```
24
+ ## Usage/Examples
25
+
26
+ ### CLI
27
+
28
+ ```bash
29
+ usage: indracli [-h] [-u EMAIL] [-p PASSWORD] [-d]
30
+ {statistics,device,all,loadmatch,idle,schedule} ...
31
+
32
+ Indra V2H CLI
33
+
34
+ positional arguments:
35
+ {statistics,device,all,loadmatch,idle,schedule}
36
+ statistics show device statistics
37
+ device show device info
38
+ all show all info
39
+ loadmatch set mode to load matching
40
+ idle set mode to IDLE
41
+ schedule return to scheuduled mode
42
+
43
+ options:
44
+ -h, --help show this help message and exit
45
+ -u EMAIL, --email EMAIL
46
+ -p PASSWORD, --password PASSWORD
47
+ -d, --debug
48
+ ```
49
+
50
+ It is possible to provide a configuration file to provide Indra Smart Portal credentials. If no username/email or password is provided it will be retrieved from ```./.indra.cfg``` or ```~/.indra.cfg```
51
+
52
+ #### Example .indra.cfg Configuration file
53
+ ```
54
+ [indra-account]
55
+ email=useremail@email.com
56
+ password=yourindrapassword
57
+ ```
58
+
59
+ ### Library Usage
60
+
61
+ Intended for use with Indra V2H Home Assistant integration.
62
+ Documentation to follow.
63
+
64
+ ## Support
65
+
66
+ This is a community project that lacks formal support.
67
+
68
+
69
+ For support from the community please join the Indra V2H trial support community: https://indra.v2h.zendesk.com/hc/en-gb/community/topics
70
+
71
+
72
+
73
+ For bugs or feature requests please create a GitHub Issue: https://github.com/creatingwake/pyindrav2h/issues
74
+
75
+ ---
76
+ #### NOTE: Please do not contact Indra Support. Indra are unable to assist with unofficial API integrations.
77
+
78
+
79
+ ## Acknowledgements
80
+
81
+ - [trizmark](https://github.com/trizmark) for help with home assistant API integration examples
82
+
83
+
@@ -0,0 +1,31 @@
1
+ # pyproject.toml
2
+
3
+ [build-system]
4
+ requires = ["setuptools>=61.0.0", "wheel"]
5
+ build-backend = "setuptools.build_meta"
6
+
7
+ [project]
8
+ name = "pyindrav2h"
9
+ version = "0.0.2"
10
+ description = "API client and example CLI to interact with Indra V2H Chargers"
11
+ readme = "README.md"
12
+ authors = [{ name = "Paul Morris", email = "paul@creatingwake.com" }]
13
+ license = { file = "LICENSE" }
14
+ classifiers = [
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python",
17
+ "Programming Language :: Python :: 3",
18
+ ]
19
+ keywords = ["v2h", "indra", "v2g", "v2x"]
20
+ dependencies = [
21
+ "httpx",
22
+ "bs4",
23
+ "lxml",
24
+ ]
25
+ requires-python = ">=3.9"
26
+
27
+ [project.urls]
28
+ Homepage = "https://github.com/creatingwake/pyindrav2h"
29
+
30
+ [project.scripts]
31
+ indracli = "pyindrav2h.cli:cli"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,7 @@
1
+ __version__ = "0.0.2"
2
+
3
+ V2H_MODES = {
4
+ 'LOAD_MATCH': 'loadmatch',
5
+ 'SCHEDULE': 'clear',
6
+ 'IDLE': 'idle',
7
+ }
@@ -0,0 +1,75 @@
1
+ import logging
2
+ from getpass import getpass
3
+ import argparse
4
+ import configparser
5
+ import asyncio
6
+ import os
7
+ from pyindrav2h.connection import Connection
8
+ from pyindrav2h.v2hclient import v2hClient
9
+
10
+ logging.basicConfig()
11
+ logging.root.setLevel(logging.WARNING)
12
+
13
+ _LOGGER = logging.getLogger(__name__)
14
+
15
+ async def main(args):
16
+ userEmail = args.email or input("Please enter your Indra login email: ")
17
+ userPass = args.password or getpass(prompt="Indra password: ")
18
+
19
+ if args.debug:
20
+ logging.root.setLevel(logging.DEBUG)
21
+
22
+ _LOGGER.debug(f"using {userEmail}, {userPass}")
23
+
24
+ # create connection
25
+ conn = Connection(userEmail, userPass)
26
+ await conn.checkAPICreds()
27
+
28
+ client = v2hClient(conn)
29
+
30
+ # refresh device/stats data
31
+ await client.refresh()
32
+
33
+ if (args.command == "device"):
34
+ print(client.device.showDevice())
35
+ elif (args.command == "statistics"):
36
+ print(client.device.showStats())
37
+ elif (args.command == "all"):
38
+ print(client.device.showAll())
39
+ elif (args.command == "loadmatch"):
40
+ print(await client.device.load_match())
41
+ elif (args.command == "idle"):
42
+ print(await client.device.idle())
43
+ elif (args.command == "schedule"):
44
+ print(await client.device.schedule())
45
+
46
+ def cli():
47
+ config = configparser.ConfigParser()
48
+ config["indra-account"] = {"email": "", "password": ""}
49
+ config.read([".indra.cfg", os.path.expanduser("~/.indra.cfg")])
50
+ parser = argparse.ArgumentParser(prog="indracli", description="Indra V2H CLI")
51
+ parser.add_argument(
52
+ "-u",
53
+ "--email",
54
+ dest="email",
55
+ default=config.get("indra-account", "email"),
56
+ )
57
+ parser.add_argument(
58
+ "-p",
59
+ "--password",
60
+ dest="password",
61
+ default=config.get("indra-account", "password")
62
+ )
63
+ parser.add_argument("-d", "--debug", dest="debug", action="store_true")
64
+ subparsers = parser.add_subparsers(dest="command", required=True)
65
+ subparsers.add_parser("statistics", help="show device statistics")
66
+ subparsers.add_parser("device", help="show device info")
67
+ subparsers.add_parser("all", help="show all info")
68
+ subparsers.add_parser("loadmatch", help="set mode to load matching")
69
+ subparsers.add_parser("idle", help="set mode to IDLE")
70
+ subparsers.add_parser("schedule", help="return to scheuduled mode")
71
+
72
+ args = parser.parse_args()
73
+
74
+ loop = asyncio.get_event_loop()
75
+ loop.run_until_complete(main(args))
@@ -0,0 +1,115 @@
1
+ import logging
2
+ import httpx
3
+ from typing import Text
4
+ from bs4 import BeautifulSoup as bs
5
+
6
+ from .exceptions import V2HException
7
+ from .exceptions import WrongCredentialsException
8
+ from .exceptions import TimeoutException
9
+
10
+ _LOGGER = logging.getLogger(__name__)
11
+
12
+ loginUrl = "https://smartportal.indra.co.uk"
13
+ apiBaseUrl = "https://api.indra.co.uk/api"
14
+
15
+ class Connection:
16
+ def __init__(
17
+ self, userEmail: Text = None, userPass: Text = None, timeout: int = 20
18
+ ) -> None:
19
+ """Initialize connection object."""
20
+ self.timeout = timeout
21
+ self.email = userEmail
22
+ self.password = userPass
23
+ self._headers = {"User-Agent": "Wget/1.14 (linux-gnu)"}
24
+ self._xsrfToken = None
25
+ self._bearerToken = None
26
+ self._cookies = None
27
+ _LOGGER.debug("New connection created")
28
+
29
+ async def checkAPICreds(self):
30
+ self._xsrfToken = await self.getXsrf(loginUrl)
31
+ _LOGGER.debug(f"XSRF token: {self._xsrfToken}")
32
+ self._bearerToken = await self.getAuth("POST", "/login")
33
+ self._headers["Authorization"] = self._bearerToken
34
+ succ = {'success': 'True'}
35
+ if succ['success'] == False:
36
+ raise WrongCredentialsException()
37
+
38
+ async def send(self, method, url, json=None):
39
+ # params = {'api_key': self.email, 'api_secret': self.password}
40
+ async with httpx.AsyncClient(
41
+ headers=self._headers, timeout=self.timeout
42
+ ) as httpclient:
43
+ # theUrl = apiBaseUrl + url
44
+ theUrl = url
45
+ try:
46
+ _LOGGER.debug(f"{method} {url} {theUrl}")
47
+ response = await httpclient.request(method, theUrl, json=json)
48
+ except httpx.ReadTimeout:
49
+ raise TimeoutException()
50
+ else:
51
+ if response.status_code == 200:
52
+ return response.json()
53
+ elif response.status_code == 202:
54
+ return True
55
+ elif response.status_code == 401:
56
+ raise WrongCredentialsException()
57
+ raise V2HException(response.status_code)
58
+
59
+ async def get(self, url, data=None):
60
+ url = apiBaseUrl + url
61
+ return await self.send("GET", url, data)
62
+
63
+ async def post(self, url, data=None):
64
+ url = apiBaseUrl + url
65
+ return await self.send("POST", url, data)
66
+
67
+ async def getXsrf(self, url, method = "GET"):
68
+ # loginResponse = await self.send("GET", url)
69
+ async with httpx.AsyncClient(
70
+ headers=self._headers, timeout=self.timeout
71
+ ) as httpclient:
72
+ theUrl = url
73
+ try:
74
+ _LOGGER.debug(f"{method} {url} {theUrl}")
75
+ response = await httpclient.request(method, theUrl)
76
+ except httpx.ReadTimeout:
77
+ raise TimeoutException()
78
+ else:
79
+ if response.status_code == 200:
80
+ htmlBody = response.content
81
+ self._cookies = response.cookies
82
+ soup = bs(htmlBody, "lxml")
83
+ return soup.find('input', {"name": "__RequestVerificationToken"})['value']
84
+ elif response.status_code == 401:
85
+ raise WrongCredentialsException()
86
+ raise V2HException(response.status_code)
87
+
88
+
89
+ async def getAuth(self, method, url, json=None):
90
+ data = {'user_email': self.email, 'user_password': self.password, '__RequestVerificationToken': self._xsrfToken}
91
+ async with httpx.AsyncClient(
92
+ headers=self._headers, timeout=self.timeout
93
+ ) as httpclient:
94
+ theUrl = loginUrl + url
95
+ try:
96
+ _LOGGER.debug(f"{method} {url} {theUrl}")
97
+ response = await httpclient.post(theUrl, data=data, cookies=self._cookies, follow_redirects=True)
98
+ except httpx.ReadTimeout:
99
+ raise TimeoutException()
100
+ else:
101
+ if response.status_code == 200:
102
+ htmlBody = response.content
103
+ self._cookies = response.cookies
104
+ soup = bs(htmlBody, "lxml")
105
+ jwtToken = soup.find('input', {"name": "JWTToken"})
106
+ if jwtToken:
107
+ return jwtToken['value']
108
+ else:
109
+ raise WrongCredentialsException()
110
+
111
+ # _LOGGER.debug(f"RESPONSE: {response.text}")
112
+ # return response.json()
113
+ elif response.status_code == 401:
114
+ raise WrongCredentialsException()
115
+ raise V2HException(response.status_code)
@@ -0,0 +1,27 @@
1
+ import logging
2
+
3
+ _LOGGER = logging.getLogger(__name__)
4
+
5
+ class V2HException(Exception):
6
+ def __init__(self, code=None, *args, **kwargs):
7
+ self.message = ""
8
+ super().__init__(*args, **kwargs)
9
+ if code is not None:
10
+ self.code = code
11
+ if isinstance(code, str):
12
+ self.message = self.code
13
+ return
14
+ if self.code == 401:
15
+ self.message = "UNAUTHORIZED"
16
+ elif self.code == 404:
17
+ self.message = "NOT_FOUND"
18
+
19
+
20
+ class WrongCredentialsException(V2HException):
21
+ """Class of exceptions for incomplete credentials."""
22
+ pass
23
+
24
+
25
+ class TimeoutException(V2HException):
26
+ """Class of exceptions for incomplete credentials."""
27
+ pass
@@ -0,0 +1,23 @@
1
+ import logging
2
+ from .connection import Connection
3
+ from .v2hdevice import v2hDevice
4
+
5
+ _LOGGER = logging.getLogger(__name__)
6
+
7
+ class v2hClient:
8
+ def __init__(
9
+ self, connection: Connection
10
+ ) -> None:
11
+ self._connection = connection
12
+ self._device = None
13
+
14
+ async def refresh(self):
15
+ if self._device is None:
16
+ self._device = v2hDevice(self._connection)
17
+
18
+ await self._device.refresh_device_info()
19
+ await self._device.refresh_stats()
20
+
21
+ @property
22
+ def device(self):
23
+ return self._device
@@ -0,0 +1,146 @@
1
+ import logging
2
+
3
+ from .connection import Connection
4
+ from . import V2H_MODES
5
+
6
+ _LOGGER = logging.getLogger(__name__)
7
+
8
+ class v2hDevice:
9
+ def __init__(self, connection: Connection) -> None:
10
+ self.connection = connection
11
+ self.data = {}
12
+ self.stats = {}
13
+ self.active = {}
14
+
15
+ async def refresh_device_info(self):
16
+ d = await self.connection.get("/authorize/validate")
17
+ self.data = d
18
+
19
+ async def __set_mode(self, mode):
20
+ s = await self.connection.post("/transactions/" + self.id +
21
+ "/interrupt/" + mode)
22
+ return s
23
+
24
+ async def load_match(self):
25
+ return await self.__set_mode(V2H_MODES['LOAD_MATCH'])
26
+
27
+ async def idle(self):
28
+ return await self.__set_mode(V2H_MODES['IDLE'])
29
+
30
+ async def schedule(self):
31
+ return await self.__set_mode(V2H_MODES['SCHEDULE'])
32
+
33
+ async def refresh_stats(self):
34
+ s = await self.connection.get("/telemetry/devices/" + self.serial +
35
+ "/latest")
36
+ self.stats = s
37
+ _LOGGER.debug(f"Stats: {s}")
38
+ a = await self.connection.get("/transactions/" + self.serial +
39
+ "/00000000-0000-0000-0000-000000000000/active")
40
+ self.active = a
41
+
42
+ @property
43
+ def id(self):
44
+ return self.active["id"]
45
+
46
+ @property
47
+ def serial(self):
48
+ return self.data["devices"][0]["deviceUID"]
49
+
50
+ @property
51
+ def lastOn(self):
52
+ return self.data["lastOn"]
53
+
54
+ @property
55
+ def isActive(self):
56
+ return self.data["devices"][0]["active"]
57
+
58
+ @property
59
+ def updateTime(self):
60
+ return self.stats["time"]
61
+
62
+ @property
63
+ def isBoosting(self):
64
+ return self.stats["isBoosting"]
65
+
66
+ @property
67
+ def mode(self):
68
+ return self.stats["mode"]
69
+
70
+ @property
71
+ def state(self):
72
+ return self.stats["state"]
73
+
74
+ @property
75
+ def activeEnergyFromEv(self):
76
+ return self.stats["data"]["activeEnergyFromEv"]
77
+
78
+ @property
79
+ def activeEnergyToEv(self):
80
+ return self.stats["data"]["activeEnergyToEv"]
81
+
82
+ @property
83
+ def powerToEv(self):
84
+ return self.stats["data"]["powerToEv"]
85
+
86
+ @property
87
+ def houseLoad(self):
88
+ return self.stats["data"]["ctClamp"]
89
+
90
+ @property
91
+ def current(self):
92
+ return self.stats["data"]["current"]
93
+
94
+ @property
95
+ def voltage(self):
96
+ return self.stats["data"]["voltage"]
97
+
98
+ @property
99
+ def freq(self):
100
+ return self.stats["data"]["freq"]
101
+
102
+ @property
103
+ def temperature(self):
104
+ return self.stats["data"]["temp"]
105
+
106
+ @property
107
+ def soc(self):
108
+ return self.stats["data"]["soc"]
109
+
110
+ @property
111
+ def isInterrupted(self):
112
+ return self.active["isInterrupted"]
113
+
114
+
115
+ def showDevice(self):
116
+ ret = ""
117
+
118
+ ret = ret + "--- Device info ---\n"
119
+ ret = ret + f"Device UID: {self.serial}\n"
120
+ ret = ret + f"Last On date: {self.lastOn}\n"
121
+ ret = ret + f"Device active: {self.isActive}"
122
+
123
+ return ret
124
+
125
+ def showStats(self):
126
+ ret = ""
127
+
128
+ ret = ret + "--- Statistics ---\n"
129
+ ret = ret + f"Update time: {self.updateTime}\n"
130
+ ret = ret + f"Boost mode on?: {self.isBoosting}\n"
131
+ ret = ret + f"Mode: {self.mode}\n"
132
+ ret = ret + f"State: {self.state}\n"
133
+ ret = ret + f"Active Energy from EV: {self.activeEnergyFromEv}\n"
134
+ ret = ret + f"Active Energy to EV: {self.activeEnergyToEv}\n"
135
+ ret = ret + f"EV load + / discharge - (W): {self.powerToEv}\n"
136
+ ret = ret + f"House load + / Export - (W): {self.houseLoad}\n"
137
+ ret = ret + f"Current: {self.current}\n"
138
+ ret = ret + f"Voltage: {self.voltage}\n"
139
+ ret = ret + f"Frequency: {self.freq}\n"
140
+ ret = ret + f"Temperature: {self.temperature}\n"
141
+ ret = ret + f"Vehicle SoC: {self.soc}\n"
142
+ ret = ret + f"Schedule active?: {not self.isInterrupted}\n"
143
+ return ret
144
+
145
+ def showAll(self):
146
+ return self.showDevice() + "\n\n" + self.showStats()
@@ -0,0 +1,121 @@
1
+ Metadata-Version: 2.1
2
+ Name: pyindrav2h
3
+ Version: 0.0.2
4
+ Summary: API client and example CLI to interact with Indra V2H Chargers
5
+ Author-email: Paul Morris <paul@creatingwake.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2024 creatingwake
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://github.com/creatingwake/pyindrav2h
28
+ Keywords: v2h,indra,v2g,v2x
29
+ Classifier: License :: OSI Approved :: MIT License
30
+ Classifier: Programming Language :: Python
31
+ Classifier: Programming Language :: Python :: 3
32
+ Requires-Python: >=3.9
33
+ Description-Content-Type: text/markdown
34
+ License-File: LICENSE
35
+ Requires-Dist: httpx
36
+ Requires-Dist: bs4
37
+ Requires-Dist: lxml
38
+
39
+
40
+ # pyindrav2h
41
+
42
+ A basic API client and example CLI to interact with Indra V2H Chargers. Required by Home Assistant.
43
+
44
+ This is a very early Alpha release, and functionality may change very rapidly.
45
+
46
+ NOTE: Indra Renewable Technologies Limited are aware of this integration. However, this is an unofficial integration and Indra are not able to provide support for direct API integrations. The Indra API will likely change in future which may result in functionality provided by this integration failing at any time.
47
+
48
+
49
+
50
+ ## Installation
51
+
52
+ Install pyindrav2h with pip. Using venv is recommended.
53
+
54
+ ```bash
55
+ pip install pyindrav2h
56
+ ```
57
+ to update pyindrav2h
58
+ ```bash
59
+ pip install pyindrav2h -U
60
+ ```
61
+ On installation a CLI will become available: ```indracli```
62
+ ## Usage/Examples
63
+
64
+ ### CLI
65
+
66
+ ```bash
67
+ usage: indracli [-h] [-u EMAIL] [-p PASSWORD] [-d]
68
+ {statistics,device,all,loadmatch,idle,schedule} ...
69
+
70
+ Indra V2H CLI
71
+
72
+ positional arguments:
73
+ {statistics,device,all,loadmatch,idle,schedule}
74
+ statistics show device statistics
75
+ device show device info
76
+ all show all info
77
+ loadmatch set mode to load matching
78
+ idle set mode to IDLE
79
+ schedule return to scheuduled mode
80
+
81
+ options:
82
+ -h, --help show this help message and exit
83
+ -u EMAIL, --email EMAIL
84
+ -p PASSWORD, --password PASSWORD
85
+ -d, --debug
86
+ ```
87
+
88
+ It is possible to provide a configuration file to provide Indra Smart Portal credentials. If no username/email or password is provided it will be retrieved from ```./.indra.cfg``` or ```~/.indra.cfg```
89
+
90
+ #### Example .indra.cfg Configuration file
91
+ ```
92
+ [indra-account]
93
+ email=useremail@email.com
94
+ password=yourindrapassword
95
+ ```
96
+
97
+ ### Library Usage
98
+
99
+ Intended for use with Indra V2H Home Assistant integration.
100
+ Documentation to follow.
101
+
102
+ ## Support
103
+
104
+ This is a community project that lacks formal support.
105
+
106
+
107
+ For support from the community please join the Indra V2H trial support community: https://indra.v2h.zendesk.com/hc/en-gb/community/topics
108
+
109
+
110
+
111
+ For bugs or feature requests please create a GitHub Issue: https://github.com/creatingwake/pyindrav2h/issues
112
+
113
+ ---
114
+ #### NOTE: Please do not contact Indra Support. Indra are unable to assist with unofficial API integrations.
115
+
116
+
117
+ ## Acknowledgements
118
+
119
+ - [trizmark](https://github.com/trizmark) for help with home assistant API integration examples
120
+
121
+
@@ -0,0 +1,15 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/pyindrav2h/__init__.py
5
+ src/pyindrav2h/cli.py
6
+ src/pyindrav2h/connection.py
7
+ src/pyindrav2h/exceptions.py
8
+ src/pyindrav2h/v2hclient.py
9
+ src/pyindrav2h/v2hdevice.py
10
+ src/pyindrav2h.egg-info/PKG-INFO
11
+ src/pyindrav2h.egg-info/SOURCES.txt
12
+ src/pyindrav2h.egg-info/dependency_links.txt
13
+ src/pyindrav2h.egg-info/entry_points.txt
14
+ src/pyindrav2h.egg-info/requires.txt
15
+ src/pyindrav2h.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ indracli = pyindrav2h.cli:cli
@@ -0,0 +1,3 @@
1
+ httpx
2
+ bs4
3
+ lxml
@@ -0,0 +1 @@
1
+ pyindrav2h