pyJPI 0.1.13__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.
Potentially problematic release.
This version of pyJPI might be problematic. Click here for more details.
- pyjpi-0.1.13/LICENSE +21 -0
- pyjpi-0.1.13/PKG-INFO +145 -0
- pyjpi-0.1.13/README.md +130 -0
- pyjpi-0.1.13/pyJPI.egg-info/PKG-INFO +145 -0
- pyjpi-0.1.13/pyJPI.egg-info/SOURCES.txt +13 -0
- pyjpi-0.1.13/pyJPI.egg-info/dependency_links.txt +1 -0
- pyjpi-0.1.13/pyJPI.egg-info/top_level.txt +1 -0
- pyjpi-0.1.13/pyjpi/__init__.py +28 -0
- pyjpi-0.1.13/pyjpi/library.py +96 -0
- pyjpi-0.1.13/pyproject.toml +26 -0
- pyjpi-0.1.13/setup.cfg +4 -0
- pyjpi-0.1.13/tests/test_jpi_init.py +15 -0
- pyjpi-0.1.13/tests/test_lib_battinfo.py +49 -0
- pyjpi-0.1.13/tests/test_lib_get.py +38 -0
- pyjpi-0.1.13/tests/test_lib_getdevicename.py +45 -0
pyjpi-0.1.13/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 by @CoMPaTech
|
|
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.
|
pyjpi-0.1.13/PKG-INFO
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyJPI
|
|
3
|
+
Version: 0.1.13
|
|
4
|
+
Summary: A library to access Android devices which run JPI
|
|
5
|
+
Author-email: Pierre Wieser <p.wieser@trychlos.org>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/trychlos/pyjpi
|
|
8
|
+
Project-URL: Issues, https://github.com/trychlos/pyjpi/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# pyJPI - An asynchronous Python module to interact with Android devices running JPI
|
|
17
|
+
|
|
18
|
+
[](https://github.com/trychlos/pyjpi)
|
|
19
|
+
[](https://github.com/trychlos/pyjpi/actions)
|
|
20
|
+
[](https://github.com/trychlos/pyjpi/actions)
|
|
21
|
+
|
|
22
|
+
[](https://pypi.python.org/pypi/pyjpi/)
|
|
23
|
+
|
|
24
|
+
<!--
|
|
25
|
+
[](https://coderabbit.ai)
|
|
26
|
+
[](https://github.com/compatech/python-airos/issues/8)
|
|
27
|
+
|
|
28
|
+
[](https://www.codefactor.io/repository/github/plugwise/python-airos)
|
|
29
|
+
[](https://codecov.io/gh/compatech/python-airos)
|
|
30
|
+
|
|
31
|
+
[](https://sonarcloud.io/summary/new_code?id=CoMPaTech_python-airos)
|
|
32
|
+
[](https://sonarcloud.io/summary/new_code?id=CoMPaTech_python-airos)
|
|
33
|
+
[](https://sonarcloud.io/summary/new_code?id=CoMPaTech_python-airos)
|
|
34
|
+
-->
|
|
35
|
+
|
|
36
|
+
## Overview
|
|
37
|
+
|
|
38
|
+
`pyJPI` is an asynchronous Python library designed to programmatically interact with Android devices running JPI.
|
|
39
|
+
|
|
40
|
+
This library is a key component for a core integration with [Home Assistant](https://www.home-assistant.io).
|
|
41
|
+
|
|
42
|
+
More details on the integration can be found on the [JPI](https://www.home-assistant.io/integrations/jpi/) page.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- Asynchronous operations are built with ``aiohttp` for non-blocking I/O, which is perfect for integrations and background tasks.
|
|
47
|
+
- API: `battInfo`
|
|
48
|
+
- API: `get`
|
|
49
|
+
- API: `getDeviceName`
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
You can install `pyJPI` from PyPI using pip:
|
|
54
|
+
|
|
55
|
+
```Bash
|
|
56
|
+
pip install pyjpi
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
Here is a more detailed example of how to use the library to connect, fetch status, and perform an action on an Android device running JPI.
|
|
62
|
+
|
|
63
|
+
```Python
|
|
64
|
+
import aiohttp
|
|
65
|
+
from pyjpi import jpiInit
|
|
66
|
+
|
|
67
|
+
async def main():
|
|
68
|
+
"""Main function to demonstrate library usage."""
|
|
69
|
+
# Create an aiohttp session with SSL verification disabled.
|
|
70
|
+
# Be cautious with this setting; it's useful for self-signed certificates
|
|
71
|
+
# but not recommended for production environments without proper validation.
|
|
72
|
+
session = aiohttp.ClientSession( connector=aiohttp.TCPConnector( verify_ssl=False ))
|
|
73
|
+
|
|
74
|
+
# Get a handle on the pyJPI library.
|
|
75
|
+
handle = await jpiInit( session )
|
|
76
|
+
|
|
77
|
+
# Have an URL somewhere.
|
|
78
|
+
url = "http://myhost.example.com:8080"
|
|
79
|
+
|
|
80
|
+
# Then just has to call the library functions.
|
|
81
|
+
try:
|
|
82
|
+
res = await handle.get( url )
|
|
83
|
+
# returns a dict { resp: ClientResponse, text: str } or False
|
|
84
|
+
res = await handle.getDeviceName( url )
|
|
85
|
+
# returns the device name as set by the manufacturer (a single string) or False
|
|
86
|
+
res = await handle.battInfo( url )
|
|
87
|
+
# returns a dict { level: integer, charging: bool, power: bool }
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
main()
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Available functions
|
|
95
|
+
|
|
96
|
+
- `async jpiInit( session: aiohttp.ClientSession ) -> JPILibrary`:
|
|
97
|
+
|
|
98
|
+
Initializes the library.
|
|
99
|
+
|
|
100
|
+
Returns a handle on it.
|
|
101
|
+
|
|
102
|
+
## Available classes
|
|
103
|
+
|
|
104
|
+
- `JPILibrary`:
|
|
105
|
+
|
|
106
|
+
The class which manages the devices accesses.
|
|
107
|
+
|
|
108
|
+
## Available `JPILibrary` methods
|
|
109
|
+
|
|
110
|
+
- `async get( url: str) -> dict`:
|
|
111
|
+
|
|
112
|
+
Runs a HTTP GET method on the specified URL.
|
|
113
|
+
|
|
114
|
+
Returns a dict with:
|
|
115
|
+
|
|
116
|
+
```Python
|
|
117
|
+
resp: the `aiohttp.ClientResponse`
|
|
118
|
+
text: the resp.text() content
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- `async getDeviceName( url: str) -> str`:
|
|
122
|
+
|
|
123
|
+
Runs a HTTP GET method on f"{url}?action=getDeviceName" url.
|
|
124
|
+
|
|
125
|
+
Returns a string which contains the device name as set by the manufacturer.
|
|
126
|
+
|
|
127
|
+
- `async battInfo( url: str) -> dict`:
|
|
128
|
+
|
|
129
|
+
Runs a HTTP GET method on on f"{url}?action=battInfo" url.
|
|
130
|
+
|
|
131
|
+
Returns a dict with:
|
|
132
|
+
|
|
133
|
+
```Python
|
|
134
|
+
level: an integer with the current battery level in %
|
|
135
|
+
charging: a boolean which says if the battery is currently charging
|
|
136
|
+
power: a boolean which says if the power is on on the device.
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Contributing
|
|
140
|
+
|
|
141
|
+
We welcome contributions as well as additional codeowners to `pyjpi`.
|
|
142
|
+
|
|
143
|
+
## Issues & help
|
|
144
|
+
|
|
145
|
+
In case of support or error, please report your issue request to our [Issues tracker](https://github.com/trychlos/pyjpi/issues).
|
pyjpi-0.1.13/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# pyJPI - An asynchronous Python module to interact with Android devices running JPI
|
|
2
|
+
|
|
3
|
+
[](https://github.com/trychlos/pyjpi)
|
|
4
|
+
[](https://github.com/trychlos/pyjpi/actions)
|
|
5
|
+
[](https://github.com/trychlos/pyjpi/actions)
|
|
6
|
+
|
|
7
|
+
[](https://pypi.python.org/pypi/pyjpi/)
|
|
8
|
+
|
|
9
|
+
<!--
|
|
10
|
+
[](https://coderabbit.ai)
|
|
11
|
+
[](https://github.com/compatech/python-airos/issues/8)
|
|
12
|
+
|
|
13
|
+
[](https://www.codefactor.io/repository/github/plugwise/python-airos)
|
|
14
|
+
[](https://codecov.io/gh/compatech/python-airos)
|
|
15
|
+
|
|
16
|
+
[](https://sonarcloud.io/summary/new_code?id=CoMPaTech_python-airos)
|
|
17
|
+
[](https://sonarcloud.io/summary/new_code?id=CoMPaTech_python-airos)
|
|
18
|
+
[](https://sonarcloud.io/summary/new_code?id=CoMPaTech_python-airos)
|
|
19
|
+
-->
|
|
20
|
+
|
|
21
|
+
## Overview
|
|
22
|
+
|
|
23
|
+
`pyJPI` is an asynchronous Python library designed to programmatically interact with Android devices running JPI.
|
|
24
|
+
|
|
25
|
+
This library is a key component for a core integration with [Home Assistant](https://www.home-assistant.io).
|
|
26
|
+
|
|
27
|
+
More details on the integration can be found on the [JPI](https://www.home-assistant.io/integrations/jpi/) page.
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
- Asynchronous operations are built with ``aiohttp` for non-blocking I/O, which is perfect for integrations and background tasks.
|
|
32
|
+
- API: `battInfo`
|
|
33
|
+
- API: `get`
|
|
34
|
+
- API: `getDeviceName`
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
You can install `pyJPI` from PyPI using pip:
|
|
39
|
+
|
|
40
|
+
```Bash
|
|
41
|
+
pip install pyjpi
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
Here is a more detailed example of how to use the library to connect, fetch status, and perform an action on an Android device running JPI.
|
|
47
|
+
|
|
48
|
+
```Python
|
|
49
|
+
import aiohttp
|
|
50
|
+
from pyjpi import jpiInit
|
|
51
|
+
|
|
52
|
+
async def main():
|
|
53
|
+
"""Main function to demonstrate library usage."""
|
|
54
|
+
# Create an aiohttp session with SSL verification disabled.
|
|
55
|
+
# Be cautious with this setting; it's useful for self-signed certificates
|
|
56
|
+
# but not recommended for production environments without proper validation.
|
|
57
|
+
session = aiohttp.ClientSession( connector=aiohttp.TCPConnector( verify_ssl=False ))
|
|
58
|
+
|
|
59
|
+
# Get a handle on the pyJPI library.
|
|
60
|
+
handle = await jpiInit( session )
|
|
61
|
+
|
|
62
|
+
# Have an URL somewhere.
|
|
63
|
+
url = "http://myhost.example.com:8080"
|
|
64
|
+
|
|
65
|
+
# Then just has to call the library functions.
|
|
66
|
+
try:
|
|
67
|
+
res = await handle.get( url )
|
|
68
|
+
# returns a dict { resp: ClientResponse, text: str } or False
|
|
69
|
+
res = await handle.getDeviceName( url )
|
|
70
|
+
# returns the device name as set by the manufacturer (a single string) or False
|
|
71
|
+
res = await handle.battInfo( url )
|
|
72
|
+
# returns a dict { level: integer, charging: bool, power: bool }
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
76
|
+
main()
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Available functions
|
|
80
|
+
|
|
81
|
+
- `async jpiInit( session: aiohttp.ClientSession ) -> JPILibrary`:
|
|
82
|
+
|
|
83
|
+
Initializes the library.
|
|
84
|
+
|
|
85
|
+
Returns a handle on it.
|
|
86
|
+
|
|
87
|
+
## Available classes
|
|
88
|
+
|
|
89
|
+
- `JPILibrary`:
|
|
90
|
+
|
|
91
|
+
The class which manages the devices accesses.
|
|
92
|
+
|
|
93
|
+
## Available `JPILibrary` methods
|
|
94
|
+
|
|
95
|
+
- `async get( url: str) -> dict`:
|
|
96
|
+
|
|
97
|
+
Runs a HTTP GET method on the specified URL.
|
|
98
|
+
|
|
99
|
+
Returns a dict with:
|
|
100
|
+
|
|
101
|
+
```Python
|
|
102
|
+
resp: the `aiohttp.ClientResponse`
|
|
103
|
+
text: the resp.text() content
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- `async getDeviceName( url: str) -> str`:
|
|
107
|
+
|
|
108
|
+
Runs a HTTP GET method on f"{url}?action=getDeviceName" url.
|
|
109
|
+
|
|
110
|
+
Returns a string which contains the device name as set by the manufacturer.
|
|
111
|
+
|
|
112
|
+
- `async battInfo( url: str) -> dict`:
|
|
113
|
+
|
|
114
|
+
Runs a HTTP GET method on on f"{url}?action=battInfo" url.
|
|
115
|
+
|
|
116
|
+
Returns a dict with:
|
|
117
|
+
|
|
118
|
+
```Python
|
|
119
|
+
level: an integer with the current battery level in %
|
|
120
|
+
charging: a boolean which says if the battery is currently charging
|
|
121
|
+
power: a boolean which says if the power is on on the device.
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Contributing
|
|
125
|
+
|
|
126
|
+
We welcome contributions as well as additional codeowners to `pyjpi`.
|
|
127
|
+
|
|
128
|
+
## Issues & help
|
|
129
|
+
|
|
130
|
+
In case of support or error, please report your issue request to our [Issues tracker](https://github.com/trychlos/pyjpi/issues).
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyJPI
|
|
3
|
+
Version: 0.1.13
|
|
4
|
+
Summary: A library to access Android devices which run JPI
|
|
5
|
+
Author-email: Pierre Wieser <p.wieser@trychlos.org>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/trychlos/pyjpi
|
|
8
|
+
Project-URL: Issues, https://github.com/trychlos/pyjpi/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# pyJPI - An asynchronous Python module to interact with Android devices running JPI
|
|
17
|
+
|
|
18
|
+
[](https://github.com/trychlos/pyjpi)
|
|
19
|
+
[](https://github.com/trychlos/pyjpi/actions)
|
|
20
|
+
[](https://github.com/trychlos/pyjpi/actions)
|
|
21
|
+
|
|
22
|
+
[](https://pypi.python.org/pypi/pyjpi/)
|
|
23
|
+
|
|
24
|
+
<!--
|
|
25
|
+
[](https://coderabbit.ai)
|
|
26
|
+
[](https://github.com/compatech/python-airos/issues/8)
|
|
27
|
+
|
|
28
|
+
[](https://www.codefactor.io/repository/github/plugwise/python-airos)
|
|
29
|
+
[](https://codecov.io/gh/compatech/python-airos)
|
|
30
|
+
|
|
31
|
+
[](https://sonarcloud.io/summary/new_code?id=CoMPaTech_python-airos)
|
|
32
|
+
[](https://sonarcloud.io/summary/new_code?id=CoMPaTech_python-airos)
|
|
33
|
+
[](https://sonarcloud.io/summary/new_code?id=CoMPaTech_python-airos)
|
|
34
|
+
-->
|
|
35
|
+
|
|
36
|
+
## Overview
|
|
37
|
+
|
|
38
|
+
`pyJPI` is an asynchronous Python library designed to programmatically interact with Android devices running JPI.
|
|
39
|
+
|
|
40
|
+
This library is a key component for a core integration with [Home Assistant](https://www.home-assistant.io).
|
|
41
|
+
|
|
42
|
+
More details on the integration can be found on the [JPI](https://www.home-assistant.io/integrations/jpi/) page.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- Asynchronous operations are built with ``aiohttp` for non-blocking I/O, which is perfect for integrations and background tasks.
|
|
47
|
+
- API: `battInfo`
|
|
48
|
+
- API: `get`
|
|
49
|
+
- API: `getDeviceName`
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
You can install `pyJPI` from PyPI using pip:
|
|
54
|
+
|
|
55
|
+
```Bash
|
|
56
|
+
pip install pyjpi
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
Here is a more detailed example of how to use the library to connect, fetch status, and perform an action on an Android device running JPI.
|
|
62
|
+
|
|
63
|
+
```Python
|
|
64
|
+
import aiohttp
|
|
65
|
+
from pyjpi import jpiInit
|
|
66
|
+
|
|
67
|
+
async def main():
|
|
68
|
+
"""Main function to demonstrate library usage."""
|
|
69
|
+
# Create an aiohttp session with SSL verification disabled.
|
|
70
|
+
# Be cautious with this setting; it's useful for self-signed certificates
|
|
71
|
+
# but not recommended for production environments without proper validation.
|
|
72
|
+
session = aiohttp.ClientSession( connector=aiohttp.TCPConnector( verify_ssl=False ))
|
|
73
|
+
|
|
74
|
+
# Get a handle on the pyJPI library.
|
|
75
|
+
handle = await jpiInit( session )
|
|
76
|
+
|
|
77
|
+
# Have an URL somewhere.
|
|
78
|
+
url = "http://myhost.example.com:8080"
|
|
79
|
+
|
|
80
|
+
# Then just has to call the library functions.
|
|
81
|
+
try:
|
|
82
|
+
res = await handle.get( url )
|
|
83
|
+
# returns a dict { resp: ClientResponse, text: str } or False
|
|
84
|
+
res = await handle.getDeviceName( url )
|
|
85
|
+
# returns the device name as set by the manufacturer (a single string) or False
|
|
86
|
+
res = await handle.battInfo( url )
|
|
87
|
+
# returns a dict { level: integer, charging: bool, power: bool }
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
main()
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Available functions
|
|
95
|
+
|
|
96
|
+
- `async jpiInit( session: aiohttp.ClientSession ) -> JPILibrary`:
|
|
97
|
+
|
|
98
|
+
Initializes the library.
|
|
99
|
+
|
|
100
|
+
Returns a handle on it.
|
|
101
|
+
|
|
102
|
+
## Available classes
|
|
103
|
+
|
|
104
|
+
- `JPILibrary`:
|
|
105
|
+
|
|
106
|
+
The class which manages the devices accesses.
|
|
107
|
+
|
|
108
|
+
## Available `JPILibrary` methods
|
|
109
|
+
|
|
110
|
+
- `async get( url: str) -> dict`:
|
|
111
|
+
|
|
112
|
+
Runs a HTTP GET method on the specified URL.
|
|
113
|
+
|
|
114
|
+
Returns a dict with:
|
|
115
|
+
|
|
116
|
+
```Python
|
|
117
|
+
resp: the `aiohttp.ClientResponse`
|
|
118
|
+
text: the resp.text() content
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- `async getDeviceName( url: str) -> str`:
|
|
122
|
+
|
|
123
|
+
Runs a HTTP GET method on f"{url}?action=getDeviceName" url.
|
|
124
|
+
|
|
125
|
+
Returns a string which contains the device name as set by the manufacturer.
|
|
126
|
+
|
|
127
|
+
- `async battInfo( url: str) -> dict`:
|
|
128
|
+
|
|
129
|
+
Runs a HTTP GET method on on f"{url}?action=battInfo" url.
|
|
130
|
+
|
|
131
|
+
Returns a dict with:
|
|
132
|
+
|
|
133
|
+
```Python
|
|
134
|
+
level: an integer with the current battery level in %
|
|
135
|
+
charging: a boolean which says if the battery is currently charging
|
|
136
|
+
power: a boolean which says if the power is on on the device.
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Contributing
|
|
140
|
+
|
|
141
|
+
We welcome contributions as well as additional codeowners to `pyjpi`.
|
|
142
|
+
|
|
143
|
+
## Issues & help
|
|
144
|
+
|
|
145
|
+
In case of support or error, please report your issue request to our [Issues tracker](https://github.com/trychlos/pyjpi/issues).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
pyJPI.egg-info/PKG-INFO
|
|
5
|
+
pyJPI.egg-info/SOURCES.txt
|
|
6
|
+
pyJPI.egg-info/dependency_links.txt
|
|
7
|
+
pyJPI.egg-info/top_level.txt
|
|
8
|
+
pyjpi/__init__.py
|
|
9
|
+
pyjpi/library.py
|
|
10
|
+
tests/test_jpi_init.py
|
|
11
|
+
tests/test_lib_battinfo.py
|
|
12
|
+
tests/test_lib_get.py
|
|
13
|
+
tests/test_lib_getdevicename.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pyjpi
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""JPI Library."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
7
|
+
|
|
8
|
+
import aiohttp
|
|
9
|
+
|
|
10
|
+
from .library import JPILibrary
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def _get_version() -> str:
|
|
14
|
+
"""Returns the version of the package (hopefully)."""
|
|
15
|
+
try:
|
|
16
|
+
return await asyncio.to_thread(version, "pyJPI")
|
|
17
|
+
except PackageNotFoundError:
|
|
18
|
+
return "0+local"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
async def jpiInit(session: aiohttp.ClientSession) -> JPILibrary:
|
|
22
|
+
"""
|
|
23
|
+
Initialize the library.
|
|
24
|
+
Returns an object with an initialized HTTP session.
|
|
25
|
+
This same object will have to be provided later on each called method.
|
|
26
|
+
"""
|
|
27
|
+
package_version = await _get_version()
|
|
28
|
+
return JPILibrary(session, package_version)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A library to interact with JPI devices.
|
|
3
|
+
Rationale: according to HomeAssistant documentation, the integration MUST not interact directly with the physical devices.
|
|
4
|
+
Instead, it MUST use a library published on pyPI repository.
|
|
5
|
+
This file is so the first version of such the interaction library.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
from aiohttp.web import HTTPError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class JPILibrary:
|
|
17
|
+
"""Class for the pyJPI library."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, session, version):
|
|
20
|
+
"""Initialize a HTTP session."""
|
|
21
|
+
self._session = session
|
|
22
|
+
self._initialized = datetime.now()
|
|
23
|
+
self._log = logging.getLogger(__name__)
|
|
24
|
+
self._log.debug("JPILibrary v%s successfully instantiated", version)
|
|
25
|
+
|
|
26
|
+
def _batt_parse_text(self, text: str) -> dict:
|
|
27
|
+
"""
|
|
28
|
+
Parse battery info text into a structured dictionary.
|
|
29
|
+
Input:
|
|
30
|
+
Niveau: 52%
|
|
31
|
+
En charge: NON
|
|
32
|
+
Alim. connectée: NON
|
|
33
|
+
Output:
|
|
34
|
+
{'level': 52, 'charging': False, 'power': False}
|
|
35
|
+
"""
|
|
36
|
+
result = {}
|
|
37
|
+
for line in text.splitlines():
|
|
38
|
+
if not line.strip():
|
|
39
|
+
continue # skip empty lines
|
|
40
|
+
key, value = line.split(":", 1)
|
|
41
|
+
key = key.strip()
|
|
42
|
+
value = value.strip()
|
|
43
|
+
|
|
44
|
+
if key == "Niveau":
|
|
45
|
+
result["level"] = int(value.strip("%"))
|
|
46
|
+
elif key == "En charge":
|
|
47
|
+
result["charging"] = value.upper() == "OUI"
|
|
48
|
+
elif key == "Alim. connectée":
|
|
49
|
+
result["power"] = value.upper() == "OUI"
|
|
50
|
+
return result
|
|
51
|
+
|
|
52
|
+
async def battInfo(self, url: str):
|
|
53
|
+
"""
|
|
54
|
+
Returns the battery informations as a hash:
|
|
55
|
+
level: <int>
|
|
56
|
+
charging: <bool>
|
|
57
|
+
power: <bool>
|
|
58
|
+
"""
|
|
59
|
+
target = f"{url}?action=battInfo"
|
|
60
|
+
resp = await self.get(target)
|
|
61
|
+
result = None
|
|
62
|
+
self._log.debug("battInfo resp=%s", resp)
|
|
63
|
+
if resp:
|
|
64
|
+
result = self._batt_parse_text(resp["text"])
|
|
65
|
+
return result
|
|
66
|
+
|
|
67
|
+
async def getDeviceName(self, url: str):
|
|
68
|
+
"""
|
|
69
|
+
Returns the device name as provided by the manufacturer.
|
|
70
|
+
E.g. Samsung sets that as 'Samsung SM-J320FN' for a Galaxy J3.
|
|
71
|
+
"""
|
|
72
|
+
target = f"{url}?action=getDeviceName"
|
|
73
|
+
resp = await self.get(target)
|
|
74
|
+
self._log.debug("getDeviceName resp=%s", resp)
|
|
75
|
+
device_name = None
|
|
76
|
+
if resp:
|
|
77
|
+
device_name = resp["text"]
|
|
78
|
+
return device_name
|
|
79
|
+
|
|
80
|
+
async def get(self, url: str):
|
|
81
|
+
"""
|
|
82
|
+
Returns an object containing the raw HTTP response from GETting the provided url plus the got text content.
|
|
83
|
+
Uses async I/O to avoid blocking the main event loop.
|
|
84
|
+
Throw an exception in case of an error.
|
|
85
|
+
"""
|
|
86
|
+
resp = None
|
|
87
|
+
result = None
|
|
88
|
+
try:
|
|
89
|
+
resp = await self._session.get(url)
|
|
90
|
+
except HTTPError as e:
|
|
91
|
+
self._log.error("HTTPError exception: %s", e)
|
|
92
|
+
return False
|
|
93
|
+
if resp:
|
|
94
|
+
text = await resp.text()
|
|
95
|
+
result = {"text": text, "resp": resp}
|
|
96
|
+
return result
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 77.0.3"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pyJPI"
|
|
7
|
+
version = "0.1.13"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Pierre Wieser", email="p.wieser@trychlos.org" },
|
|
10
|
+
]
|
|
11
|
+
description = "A library to access Android devices which run JPI"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
]
|
|
18
|
+
license = "MIT"
|
|
19
|
+
license-files = ["LICEN[CS]E*"]
|
|
20
|
+
|
|
21
|
+
[project.urls]
|
|
22
|
+
Homepage = "https://github.com/trychlos/pyjpi"
|
|
23
|
+
Issues = "https://github.com/trychlos/pyjpi/issues"
|
|
24
|
+
|
|
25
|
+
[tool.setuptools_scm]
|
|
26
|
+
write_to = "pyjpi/._version_.py"
|
pyjpi-0.1.13/setup.cfg
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Test jpiInit() calls."""
|
|
2
|
+
|
|
3
|
+
from unittest.mock import MagicMock
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from pyjpi import jpiInit, JPILibrary
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.asyncio
|
|
11
|
+
async def test_jpiInit_returns_library():
|
|
12
|
+
"""Test for jpiInit() function."""
|
|
13
|
+
session = MagicMock() # acts like a ClientSession for this test
|
|
14
|
+
lib = await jpiInit(session)
|
|
15
|
+
assert isinstance(lib, JPILibrary)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Tests for JPILibrary methods."""
|
|
2
|
+
|
|
3
|
+
from unittest.mock import AsyncMock, MagicMock
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from pyjpi import jpiInit
|
|
8
|
+
|
|
9
|
+
from .const import URL
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.mark.asyncio
|
|
13
|
+
async def test_battInfo_parses_and_returns_expected_dict():
|
|
14
|
+
"""Test for 'action=battInfo' query."""
|
|
15
|
+
|
|
16
|
+
class FakeResp:
|
|
17
|
+
"""Fake response context manager."""
|
|
18
|
+
|
|
19
|
+
status = 200
|
|
20
|
+
|
|
21
|
+
def raise_for_status(self): # pylint: disable=C0116
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
async def text(self):
|
|
25
|
+
"""Returns a fake (but with the expected format) answer."""
|
|
26
|
+
return "Niveau: 87%\nEn charge: OUI\nAlim. connectée: NON"
|
|
27
|
+
|
|
28
|
+
async def __aenter__(self):
|
|
29
|
+
return self
|
|
30
|
+
|
|
31
|
+
async def __aexit__(self, *exc):
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
session = MagicMock()
|
|
35
|
+
session.get = AsyncMock(return_value=FakeResp())
|
|
36
|
+
|
|
37
|
+
lib = await jpiInit(session)
|
|
38
|
+
|
|
39
|
+
info = await lib.battInfo(URL)
|
|
40
|
+
|
|
41
|
+
assert isinstance(info, dict)
|
|
42
|
+
assert set(info.keys()) == {"level", "charging", "power"}
|
|
43
|
+
assert isinstance(info["level"], int)
|
|
44
|
+
assert isinstance(info["charging"], bool)
|
|
45
|
+
assert isinstance(info["power"], bool)
|
|
46
|
+
assert info == {"level": 87, "charging": True, "power": False}
|
|
47
|
+
|
|
48
|
+
# keep assertion flexible re: extra kwargs
|
|
49
|
+
assert session.get.await_args.args[0] == f"{URL}?action=battInfo"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Tests for JPILibrary methods."""
|
|
2
|
+
|
|
3
|
+
from unittest.mock import AsyncMock, MagicMock
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from pyjpi import jpiInit
|
|
8
|
+
|
|
9
|
+
from .const import URL
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.mark.asyncio
|
|
13
|
+
async def test_get_uses_session_and_returns_text():
|
|
14
|
+
"""Test for a HTTP GET method."""
|
|
15
|
+
|
|
16
|
+
class FakeResp:
|
|
17
|
+
"""Fake response context manager."""
|
|
18
|
+
|
|
19
|
+
status = 200
|
|
20
|
+
|
|
21
|
+
async def text(self): # pylint: disable=C0116
|
|
22
|
+
return "OK"
|
|
23
|
+
|
|
24
|
+
async def __aenter__(self):
|
|
25
|
+
return self
|
|
26
|
+
|
|
27
|
+
async def __aexit__(self, *exc):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
session = MagicMock()
|
|
31
|
+
session.get = AsyncMock(return_value=FakeResp())
|
|
32
|
+
|
|
33
|
+
lib = await jpiInit(session)
|
|
34
|
+
out = await lib.get(URL)
|
|
35
|
+
assert out["text"] == "OK"
|
|
36
|
+
assert out["resp"].status == 200
|
|
37
|
+
|
|
38
|
+
session.get.assert_awaited_once_with(URL)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Tests for JPILibrary methods."""
|
|
2
|
+
|
|
3
|
+
from unittest.mock import AsyncMock, MagicMock
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from pyjpi import jpiInit
|
|
8
|
+
|
|
9
|
+
from .const import URL
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.mark.asyncio
|
|
13
|
+
async def test_getDeviceName_returns_device_name_string(): # pylint: disable=W0631,C0103
|
|
14
|
+
"""Test for 'action=getDeviceName' query."""
|
|
15
|
+
|
|
16
|
+
class FakeResp:
|
|
17
|
+
"""Fake response context manager."""
|
|
18
|
+
|
|
19
|
+
status = 200
|
|
20
|
+
|
|
21
|
+
def raise_for_status(self): # pylint: disable=C0116
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
async def text(self):
|
|
25
|
+
"""Returns a fake (but with the expected format) answer."""
|
|
26
|
+
return "Pixel-XL (JPI)"
|
|
27
|
+
|
|
28
|
+
async def __aenter__(self):
|
|
29
|
+
return self
|
|
30
|
+
|
|
31
|
+
async def __aexit__(self, *exc):
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
session = MagicMock()
|
|
35
|
+
session.get = AsyncMock(return_value=FakeResp())
|
|
36
|
+
|
|
37
|
+
lib = await jpiInit(session)
|
|
38
|
+
|
|
39
|
+
name = await lib.getDeviceName(URL)
|
|
40
|
+
|
|
41
|
+
assert isinstance(name, str)
|
|
42
|
+
assert name == "Pixel-XL (JPI)"
|
|
43
|
+
|
|
44
|
+
# keep assertion flexible re: extra kwargs
|
|
45
|
+
assert session.get.await_args.args[0] == f"{URL}?action=getDeviceName"
|