kiosker-python-api 1.2.1__py2.py3-none-any.whl
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.
kiosker/__init__.py
ADDED
kiosker/api.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
from .data import Status, Result, Blackout, ScreensaverState
|
|
3
|
+
|
|
4
|
+
API_PATH = '/api/v1'
|
|
5
|
+
|
|
6
|
+
class KioskerAPI:
|
|
7
|
+
def __init__(self, host, token, port=8081, ssl=False):
|
|
8
|
+
if ssl:
|
|
9
|
+
self.conf_host = f'https://{host}:{port}'
|
|
10
|
+
else:
|
|
11
|
+
self.conf_host = f'http://{host}:{port}'
|
|
12
|
+
|
|
13
|
+
self.conf_headers = {'accept': 'application/json',
|
|
14
|
+
'Authorization': f'Bearer {token}'}
|
|
15
|
+
|
|
16
|
+
def _get(self, path: str):
|
|
17
|
+
r = httpx.get(f'{self.conf_host}{API_PATH}{path}', headers=self.conf_headers)
|
|
18
|
+
if r.status_code == 200:
|
|
19
|
+
return r.json()
|
|
20
|
+
elif r.status_code == 401:
|
|
21
|
+
raise RuntimeError("Unauthorized")
|
|
22
|
+
elif r.status_code == 403:
|
|
23
|
+
raise RuntimeError("IP not allowed")
|
|
24
|
+
else:
|
|
25
|
+
r.raise_for_status()
|
|
26
|
+
|
|
27
|
+
def _post(self, path: str, json=None):
|
|
28
|
+
if json is None:
|
|
29
|
+
json = {}
|
|
30
|
+
r = httpx.post(f'{self.conf_host}{API_PATH}{path}', headers=self.conf_headers, json=json)
|
|
31
|
+
if r.status_code == 200:
|
|
32
|
+
return r.json()
|
|
33
|
+
elif r.status_code == 401:
|
|
34
|
+
raise RuntimeError("Unauthorized")
|
|
35
|
+
elif r.status_code == 403:
|
|
36
|
+
raise RuntimeError("IP not allowed")
|
|
37
|
+
elif r.status_code == 400:
|
|
38
|
+
raise RuntimeError("Bad request")
|
|
39
|
+
else:
|
|
40
|
+
r.raise_for_status()
|
|
41
|
+
|
|
42
|
+
def status(self):
|
|
43
|
+
status_data = self._get('/status').get('status')
|
|
44
|
+
return Status.from_dict(status_data)
|
|
45
|
+
|
|
46
|
+
def ping(self):
|
|
47
|
+
response_json = self._get('/ping')
|
|
48
|
+
result = Result.from_dict(response_json)
|
|
49
|
+
if result.error is False:
|
|
50
|
+
return True
|
|
51
|
+
else:
|
|
52
|
+
raise RuntimeError(f'Ping error: {result.reason}')
|
|
53
|
+
|
|
54
|
+
# Navigation
|
|
55
|
+
def navigate_home(self):
|
|
56
|
+
return Result.from_dict(self._post('/navigate/home'))
|
|
57
|
+
|
|
58
|
+
def navigate_refresh(self):
|
|
59
|
+
return Result.from_dict(self._post('/navigate/refresh'))
|
|
60
|
+
|
|
61
|
+
def navigate_forward(self):
|
|
62
|
+
return Result.from_dict(self._post('/navigate/forward'))
|
|
63
|
+
|
|
64
|
+
def navigate_backward(self):
|
|
65
|
+
return Result.from_dict(self._post('/navigate/backward'))
|
|
66
|
+
|
|
67
|
+
def navigate_url(self, url: str):
|
|
68
|
+
return Result.from_dict(self._post('/navigate/url', json={'url': url}))
|
|
69
|
+
|
|
70
|
+
# Print
|
|
71
|
+
def print(self):
|
|
72
|
+
return Result.from_dict(self._post('/print'))
|
|
73
|
+
|
|
74
|
+
# Clear
|
|
75
|
+
def clear_cookies(self):
|
|
76
|
+
return Result.from_dict(self._post('/clear/cookies'))
|
|
77
|
+
|
|
78
|
+
def clear_cache(self):
|
|
79
|
+
return Result.from_dict(self._post('/clear/cache'))
|
|
80
|
+
|
|
81
|
+
# Screensaver
|
|
82
|
+
def screensaver_interact(self):
|
|
83
|
+
return Result.from_dict(self._post('/screensaver/interact'))
|
|
84
|
+
|
|
85
|
+
def screensaver_set_disabled_state(self, disabled: bool):
|
|
86
|
+
return Result.from_dict(self._post('/screensaver/state', json={'disabled': disabled}))
|
|
87
|
+
|
|
88
|
+
def screensaver_get_state(self):
|
|
89
|
+
screensaver_status_data = self._get('/screensaver/state').get('screensaver')
|
|
90
|
+
return ScreensaverState.from_dict(screensaver_status_data)
|
|
91
|
+
|
|
92
|
+
# Blackout
|
|
93
|
+
def blackout_set(self, blackout: Blackout):
|
|
94
|
+
return Result.from_dict(self._post('/blackout', json=blackout.to_dict()))
|
|
95
|
+
|
|
96
|
+
def blackout_get(self):
|
|
97
|
+
blackout_data = self._get('/blackout/state').get('blackout')
|
|
98
|
+
if blackout_data is None:
|
|
99
|
+
return None
|
|
100
|
+
return Blackout.from_dict(blackout_data)
|
|
101
|
+
|
|
102
|
+
def blackout_clear(self):
|
|
103
|
+
return Result.from_dict(self._post('/blackout', json={'visible': False}))
|
kiosker/data.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class Status:
|
|
7
|
+
battery_level: int
|
|
8
|
+
battery_state: str
|
|
9
|
+
model: str
|
|
10
|
+
os_version: str
|
|
11
|
+
last_interaction: datetime
|
|
12
|
+
last_update: datetime
|
|
13
|
+
device_id: str
|
|
14
|
+
last_motion: Optional[datetime]
|
|
15
|
+
screensaver_pause: Optional[bool]
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def from_dict(cls, status_data):
|
|
19
|
+
return cls(battery_level=status_data['batteryLevel'], battery_state=status_data['batteryState'], model=status_data['model'], os_version=status_data['osVersion'], last_interaction=datetime.fromisoformat(status_data['lastInteraction']), last_motion=datetime.fromisoformat(status_data['lastMotion']) if status_data.get('lastMotion') else None, last_update=datetime.fromisoformat(status_data['date']), device_id=status_data['deviceId'], screensaver_pause=status_data['screensaverPause'] if status_data.get('screensaverPause') else None)
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class Result:
|
|
23
|
+
error: bool
|
|
24
|
+
reason: Optional[str]
|
|
25
|
+
function: Optional[str]
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def from_dict(cls, result_data):
|
|
29
|
+
return cls(error=result_data['error'], reason=result_data['reason'] if result_data.get('reason') else None , function=result_data.get('function') if result_data.get('function') else None)
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class ScreensaverState:
|
|
33
|
+
visible: bool
|
|
34
|
+
disabled: bool
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def from_dict(cls, state_data):
|
|
38
|
+
return cls(visible=state_data['visible'], disabled=state_data['disabled'])
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class Blackout:
|
|
43
|
+
visible: bool
|
|
44
|
+
background: Optional[str] = None
|
|
45
|
+
foreground: Optional[str] = None
|
|
46
|
+
expire: Optional[int] = None
|
|
47
|
+
text: Optional[str] = None
|
|
48
|
+
icon: Optional[str] = None
|
|
49
|
+
dismissible: Optional[bool] = False
|
|
50
|
+
buttonBackground: Optional[str] = None
|
|
51
|
+
buttonForeground: Optional[str] = None
|
|
52
|
+
buttonText: Optional[str] = None
|
|
53
|
+
sound: Optional[str] = None
|
|
54
|
+
|
|
55
|
+
def to_dict(self):
|
|
56
|
+
return {
|
|
57
|
+
'visible': self.visible,
|
|
58
|
+
'background': self.background,
|
|
59
|
+
'foreground': self.foreground,
|
|
60
|
+
'expire': self.expire,
|
|
61
|
+
'text': self.text,
|
|
62
|
+
'icon': self.icon,
|
|
63
|
+
'dismissible': self.dismissible,
|
|
64
|
+
'buttonBackground': self.buttonBackground,
|
|
65
|
+
'buttonForeground': self.buttonForeground,
|
|
66
|
+
'buttonText': self.buttonText,
|
|
67
|
+
'sound': self.sound,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def from_dict(cls, blackout_data):
|
|
72
|
+
return cls(
|
|
73
|
+
visible=blackout_data['visible'],
|
|
74
|
+
background=blackout_data['background'],
|
|
75
|
+
foreground=blackout_data['foreground'],
|
|
76
|
+
expire=blackout_data['expire'],
|
|
77
|
+
text=blackout_data.get('text'),
|
|
78
|
+
icon=blackout_data.get('icon'),
|
|
79
|
+
dismissible=blackout_data.get('dismissible', False),
|
|
80
|
+
buttonBackground=blackout_data.get('buttonBackground'),
|
|
81
|
+
buttonForeground=blackout_data.get('buttonForeground'),
|
|
82
|
+
buttonText=blackout_data.get('buttonText'),
|
|
83
|
+
sound=blackout_data.get('sound'),
|
|
84
|
+
)
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kiosker-python-api
|
|
3
|
+
Version: 1.2.1
|
|
4
|
+
Summary: A python wrapper for the Kiosker API
|
|
5
|
+
Project-URL: Homepage, https://kiosker.io
|
|
6
|
+
Project-URL: Documentation, https://docs.kiosker.io
|
|
7
|
+
Project-URL: Repository, https://github.com/Top-North/kiosker-python.git
|
|
8
|
+
Project-URL: Issues, https://github.com/Top-North/kiosker-python/issues
|
|
9
|
+
Author-email: Martin Claesson <martin@topnorth.se>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: api,kiosk,kiosker,kiosker pro
|
|
13
|
+
Requires-Dist: httpx
|
|
14
|
+
Provides-Extra: test
|
|
15
|
+
Requires-Dist: pytest; extra == 'test'
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# Python wrapper for Kiosker API
|
|
19
|
+
Python wrapper for Kiosker API-integration.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
### Installation
|
|
24
|
+
|
|
25
|
+
```shell
|
|
26
|
+
pip3 install kiosker-python
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
### Setup
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from kiosker.api import KioskerAPI
|
|
35
|
+
api = KioskerAPI('10.0.1.100', 'token')
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
### Functions
|
|
41
|
+
|
|
42
|
+
#### Get Status
|
|
43
|
+
```python
|
|
44
|
+
status = api.status()
|
|
45
|
+
|
|
46
|
+
print('Status:')
|
|
47
|
+
print(f'Device ID: {status.device_id}')
|
|
48
|
+
print(f'Model: {status.model}')
|
|
49
|
+
print(f'OS version: {status.os_version}')
|
|
50
|
+
print(f'Battery level: {status.battery_level}%')
|
|
51
|
+
print(f'Battery state: {status.battery_state}')
|
|
52
|
+
print(f'Last interaction: {status.last_interaction}')
|
|
53
|
+
print(f'Last motion: {status.last_motion}')
|
|
54
|
+
print(f'Last status update: {status.last_update}')
|
|
55
|
+
print(f'Screensaver pause: {status.screensaver_pause}')
|
|
56
|
+
```
|
|
57
|
+
**Description**: Retrieves the current status of the kiosk.
|
|
58
|
+
|
|
59
|
+
#### Ping the API
|
|
60
|
+
```python
|
|
61
|
+
result = api.ping()
|
|
62
|
+
print(f"Ping successful: {result}")
|
|
63
|
+
```
|
|
64
|
+
**Description**: Checks if the API is reachable. Returns `True` if successful, otherwise raises an error.
|
|
65
|
+
|
|
66
|
+
#### Navigate to a URL
|
|
67
|
+
```python
|
|
68
|
+
result = api.navigate_url('https://example.com')
|
|
69
|
+
print(f"Navigation result: {result}")
|
|
70
|
+
```
|
|
71
|
+
**Description**: Navigates the kiosk to the specified URL.
|
|
72
|
+
|
|
73
|
+
#### Refresh the Page
|
|
74
|
+
```python
|
|
75
|
+
result = api.navigate_refresh()
|
|
76
|
+
print(f"Refresh result: {result}")
|
|
77
|
+
```
|
|
78
|
+
**Description**: Refreshes the current page on the kiosk.
|
|
79
|
+
|
|
80
|
+
#### Navigate Home
|
|
81
|
+
```python
|
|
82
|
+
result = api.navigate_home()
|
|
83
|
+
print(f"Home navigation result: {result}")
|
|
84
|
+
```
|
|
85
|
+
**Description**: Navigates the kiosk to the home page.
|
|
86
|
+
|
|
87
|
+
#### Navigate Forward
|
|
88
|
+
```python
|
|
89
|
+
result = api.navigate_forward()
|
|
90
|
+
print(f"Navigate forward result: {result}")
|
|
91
|
+
```
|
|
92
|
+
**Description**: Navigates forward in the browser's history.
|
|
93
|
+
|
|
94
|
+
#### Navigate Backward
|
|
95
|
+
```python
|
|
96
|
+
result = api.navigate_backward()
|
|
97
|
+
print(f"Navigate backward result: {result}")
|
|
98
|
+
```
|
|
99
|
+
**Description**: Navigates backward in the browser's history.
|
|
100
|
+
|
|
101
|
+
#### Print
|
|
102
|
+
```python
|
|
103
|
+
result = api.print()
|
|
104
|
+
print(f"Print result: {result}")
|
|
105
|
+
```
|
|
106
|
+
**Description**: Sends a print command to the kiosk.
|
|
107
|
+
|
|
108
|
+
#### Clear Cookies
|
|
109
|
+
```python
|
|
110
|
+
result = api.clear_cookies()
|
|
111
|
+
print(f"Cookies cleared: {result}")
|
|
112
|
+
```
|
|
113
|
+
**Description**: Clears all cookies stored on the kiosk.
|
|
114
|
+
|
|
115
|
+
#### Clear Cache
|
|
116
|
+
```python
|
|
117
|
+
result = api.clear_cache()
|
|
118
|
+
print(f"Cache cleared: {result}")
|
|
119
|
+
```
|
|
120
|
+
**Description**: Clears the cache on the kiosk.
|
|
121
|
+
|
|
122
|
+
#### Interact with Screensaver
|
|
123
|
+
```python
|
|
124
|
+
result = api.screensaver_interact()
|
|
125
|
+
print(f"Screensaver interaction result: {result}")
|
|
126
|
+
```
|
|
127
|
+
**Description**: Simulates user interaction with the screensaver to prevent it from activating.
|
|
128
|
+
|
|
129
|
+
#### Set Screensaver State
|
|
130
|
+
```python
|
|
131
|
+
result = api.screensaver_set_state(disabled=True)
|
|
132
|
+
print(f"Screensaver disabled: {result}")
|
|
133
|
+
```
|
|
134
|
+
**Description**: Enables or disables the screensaver.
|
|
135
|
+
|
|
136
|
+
#### Get Screensaver State
|
|
137
|
+
```python
|
|
138
|
+
state = api.screensaver_get_state()
|
|
139
|
+
print(f"Screensaver state: {state}")
|
|
140
|
+
```
|
|
141
|
+
**Description**: Retrieves the current state of the screensaver (enabled or disabled).
|
|
142
|
+
|
|
143
|
+
#### Set Blackout
|
|
144
|
+
```python
|
|
145
|
+
from kiosker.data import Blackout
|
|
146
|
+
|
|
147
|
+
blackout = Blackout(
|
|
148
|
+
visible=True, # Required: show blackout screen
|
|
149
|
+
text="Maintenance in progress", # Optional: text to display
|
|
150
|
+
background="#000000", # Optional: background color (hex)
|
|
151
|
+
foreground="#FFFFFF", # Optional: foreground/text color (hex)
|
|
152
|
+
icon="warning", # Optional: icon name (SF Symbol)
|
|
153
|
+
expire=60, # Optional: time in seconds before blackout expires
|
|
154
|
+
dismissible=True, # Optional: allow user to dismiss blackout with a button
|
|
155
|
+
buttonBackground="#FF0000", # Optional: button background color (hex)
|
|
156
|
+
buttonForeground="#FFFFFF", # Optional: button text color (hex)
|
|
157
|
+
buttonText="OK", # Optional: button label
|
|
158
|
+
sound="1003" # Optional: sound to play (SystemSoundID)
|
|
159
|
+
)
|
|
160
|
+
result = api.blackout_set(blackout)
|
|
161
|
+
print(f"Blackout set: {result}")
|
|
162
|
+
```
|
|
163
|
+
**Description**: Sets a blackout screen with customizable text, colors, expiration time, and optional button/sound options.
|
|
164
|
+
|
|
165
|
+
#### Get Blackout State
|
|
166
|
+
```python
|
|
167
|
+
blackout_state = api.blackout_get()
|
|
168
|
+
print(f"Blackout state: {blackout_state}")
|
|
169
|
+
```
|
|
170
|
+
**Description**: Retrieves the current state of the blackout screen.
|
|
171
|
+
|
|
172
|
+
#### Clear Blackout
|
|
173
|
+
```python
|
|
174
|
+
result = api.blackout_clear()
|
|
175
|
+
print(f"Blackout cleared: {result}")
|
|
176
|
+
```
|
|
177
|
+
**Description**: Clears the blackout screen.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
### Objects
|
|
182
|
+
|
|
183
|
+
#### `Status`
|
|
184
|
+
Represents the current status of the kiosk.
|
|
185
|
+
|
|
186
|
+
**Attributes**:
|
|
187
|
+
- `battery_level` (int): Battery percentage.
|
|
188
|
+
- `battery_state` (str): Current battery state (e.g., charging, discharging).
|
|
189
|
+
- `model` (str): Device model.
|
|
190
|
+
- `os_version` (str): Operating system version.
|
|
191
|
+
- `last_interaction` (datetime): Timestamp of the last user interaction.
|
|
192
|
+
- `last_motion` (Optional[datetime]): Timestamp of the last detected motion.
|
|
193
|
+
- `last_update` (datetime): Timestamp of the last status update.
|
|
194
|
+
- `device_id` (str): Unique identifier for the device.
|
|
195
|
+
- `screensaver_pause` (Optional[bool]): Whether the screensaver is paused.
|
|
196
|
+
|
|
197
|
+
#### `Result`
|
|
198
|
+
Represents the result of an API operation.
|
|
199
|
+
|
|
200
|
+
**Attributes**:
|
|
201
|
+
- `error` (bool): Indicates if an error occurred.
|
|
202
|
+
- `reason` (Optional[str]): Reason for the error, if any.
|
|
203
|
+
- `function` (Optional[str]): Name of the function that caused the error.
|
|
204
|
+
|
|
205
|
+
#### `Blackout`
|
|
206
|
+
Represents a blackout screen configuration.
|
|
207
|
+
|
|
208
|
+
**Attributes**:
|
|
209
|
+
- `visible` (bool): Whether the blackout screen is visible.
|
|
210
|
+
- `text` (Optional[str]): Text to display on the blackout screen.
|
|
211
|
+
- `background` (str): Background color in hex format.
|
|
212
|
+
- `foreground` (str): Foreground color in hex format.
|
|
213
|
+
- `icon` (Optional[str]): Icon to display on the blackout screen.
|
|
214
|
+
- `expire` (int): Time in seconds before the blackout screen expires.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
### Development
|
|
219
|
+
1. Clone the project
|
|
220
|
+
|
|
221
|
+
2. Create a virtual environment
|
|
222
|
+
```shell
|
|
223
|
+
python3 -m venv venv
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
3. Activate the virtual environment
|
|
227
|
+
```shell
|
|
228
|
+
source venv/bin/activate
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
4. Install dependencies
|
|
232
|
+
```shell
|
|
233
|
+
pip install wheel setuptools twine pytest httpx
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
5. Run tests
|
|
237
|
+
```shell
|
|
238
|
+
HOST="0.0.0.0" TOKEN="" pytest -s
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
6. Build the library
|
|
242
|
+
```shell
|
|
243
|
+
python -m build --wheel
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
7. Upload to test
|
|
247
|
+
```shell
|
|
248
|
+
twine upload --repository testpypi dist/*
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
### API Documentation
|
|
254
|
+
- [Docs](https://docs.kiosker.io/#/api)
|
|
255
|
+
- [Definition](https://swagger.kiosker.io)
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
### Get Kiosker for iOS on the App Store
|
|
260
|
+
- [Kiosker](https://apps.apple.com/us/app/kiosker-fullscreen-web-kiosk/id1481691530?uo=4&at=11l6hc&ct=fnd)
|
|
261
|
+
- [Kiosker Pro](https://apps.apple.com/us/app/kiosker-pro-web-kiosk/id1446738885?uo=4&at=11l6hc&ct=fnd)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
kiosker/__init__.py,sha256=2IUwkYlNimXNczxs09J33WoNGdRTEDFD23VtYl6TDg4,70
|
|
2
|
+
kiosker/api.py,sha256=bqa5UWzZbFXYb34dev71jCb2SfHmNS0CU66cikrlLuE,3561
|
|
3
|
+
kiosker/data.py,sha256=ku8monJFlLxR4RQpqA18NituZRuNSDVFbYyAE85AY7Q,3114
|
|
4
|
+
kiosker_python_api-1.2.1.dist-info/METADATA,sha256=2EQ3rsW45s8RSO4g5f5-5ksXnqPUd6TdoF1oIBgVYhY,7140
|
|
5
|
+
kiosker_python_api-1.2.1.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
|
|
6
|
+
kiosker_python_api-1.2.1.dist-info/licenses/LICENSE,sha256=mjD-_yvrBLGI96FVJteQTq936wbgJmFImKQ2Het_8HQ,1069
|
|
7
|
+
kiosker_python_api-1.2.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Top North AB
|
|
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.
|