livisi 0.0.24__py3-none-any.whl → 0.9.0__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.
@@ -0,0 +1,53 @@
1
+ """Code to represent a livisi device."""
2
+
3
+ from __future__ import annotations
4
+ from typing import Any
5
+
6
+ from dataclasses import dataclass
7
+
8
+ from .livisi_const import CONTROLLER_DEVICE_TYPES
9
+
10
+
11
+ @dataclass
12
+ class LivisiDevice:
13
+ """Stores the livisi device data."""
14
+
15
+ id: str
16
+ type: str
17
+ tags: dict[str, str]
18
+ config: dict[str, Any]
19
+ state: dict[str, Any]
20
+ manufacturer: str
21
+ version: str
22
+ cls: str
23
+ product: str
24
+ desc: str
25
+ capabilities: dict[str, str]
26
+ capability_config: dict[str, dict[str, Any]]
27
+ room: str
28
+ battery_low: bool
29
+ update_available: bool
30
+ updated: bool
31
+ unreachable: bool
32
+
33
+ @property
34
+ def name(self) -> str:
35
+ """Get name from config."""
36
+ return self.config.get("name")
37
+
38
+ @property
39
+ def tag_category(self) -> str:
40
+ """Get tag type category from config."""
41
+ if self.tags is None:
42
+ return None
43
+ return self.tags.get("typeCategory")
44
+
45
+ @property
46
+ def tag_type(self) -> str:
47
+ """Get tag type from config."""
48
+ return self.tags.get("type")
49
+
50
+ @property
51
+ def is_shc(self) -> bool:
52
+ """Indicate whether this device is the controller."""
53
+ return self.type in CONTROLLER_DEVICE_TYPES
@@ -0,0 +1,124 @@
1
+ """Errors for the Livisi Smart Home component."""
2
+
3
+ # Taken from https://developer.services-smarthome.de/api_reference/errorcodes/
4
+ ERROR_CODES = {
5
+ # General Errors
6
+ 1000: "An unknown error has occurred.",
7
+ 1001: "Service unavailable.",
8
+ 1002: "Service timeout.",
9
+ 1003: "Internal API error.",
10
+ 1004: "SHC invalid operation.",
11
+ 1005: "Missing argument or wrong value.",
12
+ 1006: "Service too busy.",
13
+ 1007: "Unsupported request.",
14
+ 1008: "Precondition failed.",
15
+ # Authentication and Authorization Errors
16
+ 2000: "An unknown error has occurred during Authentication or Authorization process.",
17
+ 2001: "Access not allowed.",
18
+ 2002: "Invalid token request.",
19
+ 2003: "Invalid client credentials.",
20
+ 2004: "The token signature is invalid.",
21
+ 2005: "Failed to initialize user session.",
22
+ 2006: "A connection already exists for the current session.",
23
+ 2007: "The lifetime of the token has expired.",
24
+ 2008: "Login attempted from a different client provider.",
25
+ 2009: "Invalid user credentials.",
26
+ 2010: "Controller access not allowed.",
27
+ 2011: "Insufficient permissions.",
28
+ 2012: "Session not found.",
29
+ 2013: "Account temporary locked.",
30
+ # Entities Errors
31
+ 3000: "The requested entity does not exist.",
32
+ 3001: "The provided request content is invalid and can't be parsed.",
33
+ 3002: "No change performed.",
34
+ 3003: "The provided entity already exists.",
35
+ 3004: "The provided interaction is not valid.",
36
+ 3005: "Too many entities of this type.",
37
+ # Products Errors
38
+ 3500: "Premium Services can't be directly enabled.",
39
+ 3501: "Cannot remove a product that was paid.",
40
+ # Actions Errors
41
+ 4000: "The triggered action is invalid.",
42
+ 4001: "Invalid parameter.",
43
+ 4002: "Permission to trigger action not allowed.",
44
+ 4003: "Unsupported action type.",
45
+ # Configuration Errors
46
+ 5000: "The configuration could not be updated.",
47
+ 5001: "Could not obtain exclusive access on the configuration.",
48
+ 5002: "Communication with the SHC failed.",
49
+ 5003: "The owner did not accept the TaC latest version.",
50
+ 5004: "One SHC already registered.",
51
+ 5005: "The user has no SHC.",
52
+ 5006: "Controller offline.",
53
+ 5009: "Registration failure.",
54
+ # SmartCodes Errors
55
+ 6000: "SmartCode request not allowed.",
56
+ 6001: "The SmartCode cannot be redeemed.",
57
+ 6002: "Restricted access.",
58
+ }
59
+
60
+
61
+ class LivisiException(Exception):
62
+ """Base class for Livisi exceptions."""
63
+
64
+ def __init__(self, message: str = "", *args: object) -> None:
65
+ """Initialize the exception with a message."""
66
+ self.message = message
67
+ super().__init__(message, *args)
68
+
69
+
70
+ class ShcUnreachableException(LivisiException):
71
+ """Unable to connect to the Smart Home Controller."""
72
+
73
+ def __init__(
74
+ self,
75
+ message: str = "Unable to connect to the Smart Home Controller.",
76
+ *args: object,
77
+ ) -> None:
78
+ """Generate error with default message."""
79
+ super().__init__(message, *args)
80
+
81
+
82
+ class WrongCredentialException(LivisiException):
83
+ """The user credentials were wrong."""
84
+
85
+ def __init__(
86
+ self, message: str = "The user credentials are wrong.", *args: object
87
+ ) -> None:
88
+ """Generate error with default message."""
89
+ super().__init__(message, *args)
90
+
91
+
92
+ class IncorrectIpAddressException(LivisiException):
93
+ """The IP address provided by the user is incorrect."""
94
+
95
+ def __init__(
96
+ self,
97
+ message: str = "The IP address provided by the user is incorrect.",
98
+ *args: object,
99
+ ) -> None:
100
+ """Generate error with default message."""
101
+ super().__init__(message, *args)
102
+
103
+
104
+ class TokenExpiredException(LivisiException):
105
+ """The authentication token is expired."""
106
+
107
+ def __init__(
108
+ self, message: str = "The authentication token is expired.", *args: object
109
+ ) -> None:
110
+ """Generate error with default message."""
111
+ super().__init__(message, *args)
112
+
113
+
114
+ class ErrorCodeException(LivisiException):
115
+ """The request sent an errorcode (other than token expired) as response."""
116
+
117
+ def __init__(self, error_code: int, message: str = None, *args: object) -> None:
118
+ """Generate error with code."""
119
+ self.error_code = error_code
120
+ if (message is None) and (error_code in ERROR_CODES):
121
+ message = ERROR_CODES[error_code]
122
+ elif message is None:
123
+ message = f"Unknown error code from shc: {error_code}"
124
+ super().__init__(message, *args)
@@ -0,0 +1,23 @@
1
+ """Helper code to parse json to python dataclass (simple and non recursive)."""
2
+ from dataclasses import fields
3
+ import json
4
+ import re
5
+
6
+
7
+ def parse_dataclass(jsondata, clazz):
8
+ """Convert keys to snake_case and parse to dataclass."""
9
+
10
+ if isinstance(jsondata, str | bytes | bytearray):
11
+ parsed_json = json.loads(jsondata)
12
+ elif isinstance(jsondata, dict):
13
+ parsed_json = jsondata
14
+ else:
15
+ parsed_json = {}
16
+
17
+ # Convert keys to snake_case
18
+ parsed_json = {
19
+ re.sub("([A-Z])", r"_\1", k).lower(): v for k, v in parsed_json.items()
20
+ }
21
+ # Only include keys that are fields in the dataclass
22
+ data_dict = {f.name: parsed_json.get(f.name) for f in fields(clazz)}
23
+ return clazz(**data_dict)
@@ -0,0 +1,86 @@
1
+ """Code for communication with the Livisi application websocket."""
2
+
3
+ from collections.abc import Callable
4
+ import urllib.parse
5
+
6
+ from json import JSONDecodeError
7
+ import websockets.client
8
+
9
+ from .livisi_json_util import parse_dataclass
10
+ from .livisi_const import CLASSIC_WEBSOCKET_PORT, V2_WEBSOCKET_PORT, LOGGER
11
+ from .livisi_websocket_event import LivisiWebsocketEvent
12
+
13
+
14
+ class LivisiWebsocket:
15
+ """Represents the websocket class."""
16
+
17
+ def __init__(self, aiolivisi) -> None:
18
+ """Initialize the websocket."""
19
+ self.aiolivisi = aiolivisi
20
+ self.connection_url: str = None
21
+ self._websocket = None
22
+ self._disconnecting = False
23
+
24
+ def is_connected(self):
25
+ """Return whether the webservice is currently connected."""
26
+ return self._websocket is not None
27
+
28
+ async def connect(self, on_data, on_close) -> None:
29
+ """Connect to the socket."""
30
+ if self.aiolivisi.controller.is_v2:
31
+ port = V2_WEBSOCKET_PORT
32
+ token = urllib.parse.quote(self.aiolivisi.token)
33
+ else:
34
+ port = CLASSIC_WEBSOCKET_PORT
35
+ token = self.aiolivisi.token
36
+ ip_address = self.aiolivisi.host
37
+ self.connection_url = f"ws://{ip_address}:{port}/events?token={token}"
38
+
39
+ while not self._disconnecting:
40
+ try:
41
+ async with websockets.client.connect(
42
+ self.connection_url, ping_interval=10, ping_timeout=10
43
+ ) as websocket:
44
+ LOGGER.info("WebSocket connection established.")
45
+ self._websocket = websocket
46
+ await self.consumer_handler(websocket, on_data)
47
+ except Exception as e:
48
+ LOGGER.exception("Error handling websocket connection", exc_info=e)
49
+ if not self._disconnecting:
50
+ LOGGER.warning("WebSocket disconnected unexpectedly, retrying...")
51
+ await on_close()
52
+ finally:
53
+ self._websocket = None
54
+
55
+ async def disconnect(self) -> None:
56
+ """Close the websocket."""
57
+ self._disconnecting = True
58
+ if self._websocket is not None:
59
+ await self._websocket.close(code=1000, reason="Handle disconnect request")
60
+ LOGGER.info("WebSocket connection closed.")
61
+ self._websocket = None
62
+ self._disconnecting = False
63
+
64
+ async def consumer_handler(self, websocket, on_data: Callable):
65
+ """Parse data transmitted via the websocket."""
66
+ try:
67
+ async for message in websocket:
68
+ LOGGER.debug("Received WebSocket message: %s", message)
69
+
70
+ try:
71
+ event_data = parse_dataclass(message, LivisiWebsocketEvent)
72
+ except JSONDecodeError:
73
+ LOGGER.warning("Cannot decode WebSocket message", exc_info=True)
74
+ continue
75
+
76
+ if event_data.properties is None or event_data.properties == {}:
77
+ LOGGER.warning("Received event with no properties, skipping.")
78
+ continue
79
+
80
+ # Remove the URL prefix and use just the ID (which is unique)
81
+ event_data.source = event_data.source.removeprefix("/device/")
82
+ event_data.source = event_data.source.removeprefix("/capability/")
83
+
84
+ on_data(event_data)
85
+ except Exception as e:
86
+ LOGGER.error("Unhandled error in WebSocket consumer handler", exc_info=e)
@@ -0,0 +1,13 @@
1
+ """LivisiWebsocketEvent."""
2
+ from dataclasses import dataclass
3
+
4
+
5
+ @dataclass
6
+ class LivisiWebsocketEvent:
7
+ """Encapuses a livisi event sent via the websocket."""
8
+
9
+ namespace: str
10
+ type: str | None
11
+ source: str
12
+ timestamp: str | None
13
+ properties: dict | None
File without changes
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.2
2
+ Name: livisi
3
+ Version: 0.9.0
4
+ Summary: Connection library for the abandoned Livisi Smart Home system
5
+ Author-email: Felix Rotthowe <felix@planbnet.org>
6
+ License: Apache-2.0
7
+ Project-URL: Source, https://github.com/planbnet/livisi
8
+ Project-URL: Tracker, https://github.com/planbnet/livisi/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: colorlog>=6.8.2
16
+ Requires-Dist: aiohttp>=3.8.5
17
+ Requires-Dist: websockets>=11.0.3
18
+
19
+ # livisi
20
+
21
+ # Asynchronous library to communicate with LIVISI Smart Home Controller
22
+ Requires Python 3.10+ (might work with versions down to 3.8 but I never tested it) and uses asyncio and aiohttp.
23
+
24
+ This library started as a fork of the unmaintained aiolivisi lib and was developed inside the [unofficial livisi integration for Home Assistant](https://github.com/planbnet/livisi_unofficial)
25
+
26
+ The versions starting with `0.0.` are still compatible to the old aiolivisi code, while `1.0.0` will introduce lots of breaking changes besides support for more devices and improved connection stability and error handling.
@@ -0,0 +1,15 @@
1
+ livisi/__init__.py,sha256=Dh8B_PRVSmch_ClhFCVyV7O2doRCfREHvKGob6MYuRk,2225
2
+ livisi/livisi_connector.py,sha256=VagZFl46y49d3gqrr9iv0YSKLviHRLvAAxcMW4IO734,18095
3
+ livisi/livisi_const.py,sha256=6YqoPdlKX7ogfC_E_ea8opA0JeYIweXRRfpfu-QXTqc,779
4
+ livisi/livisi_controller.py,sha256=XyJ58XMXIxw5anIwHJ5MRVlNUBZyi3RjP8AO8HnYcXo,296
5
+ livisi/livisi_device.py,sha256=Qeh8kdVWY57S1aS5S4ATfi8t2a2k0Bx6njScRC0XKek,1230
6
+ livisi/livisi_errors.py,sha256=N-xEF42KfsCVUghdJYuM8yvpUiI_op1i1mpBiKcrM5Y,4511
7
+ livisi/livisi_event.py,sha256=Z3VN1nW737O1xMt1yj62lC0KTiiXFIlRPEog33IsJpw,456
8
+ livisi/livisi_json_util.py,sha256=6sQk8ycMUIAKL_8rD3dW_uHWRNa6QMcky-PvcEnM_88,735
9
+ livisi/livisi_websocket.py,sha256=KFY_n7w0grwnczjUDrdijK4RQi-8MOX81uLe6Nw-mSM,3465
10
+ livisi/livisi_websocket_event.py,sha256=pbjyiKid9gOWMcWiw5jq0dbo2DQ7dAQnxM0D_UJBltw,273
11
+ livisi-0.9.0.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ livisi-0.9.0.dist-info/METADATA,sha256=iIyRLOhx8GWwodC5quOjI1tPK2irJyNjkuYzS0PaWVA,1263
13
+ livisi-0.9.0.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
14
+ livisi-0.9.0.dist-info/top_level.txt,sha256=ctiU5MMpBSwoQR7mJWIuyB1ND1_g004Xa3vNmMsSiCs,7
15
+ livisi-0.9.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (76.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
livisi/aiolivisi.py DELETED
@@ -1,270 +0,0 @@
1
- """Code to handle the communication with Livisi Smart home controllers."""
2
- from __future__ import annotations
3
- from typing import Any
4
- import uuid
5
-
6
- from aiohttp.client import ClientSession
7
-
8
- from .errors import (
9
- IncorrectIpAddressException,
10
- ShcUnreachableException,
11
- WrongCredentialException,
12
- TokenExpiredException,
13
- )
14
-
15
- from .const import (
16
- AUTH_GRANT_TYPE,
17
- AUTH_PASSWORD,
18
- AUTH_USERNAME,
19
- AUTHENTICATION_HEADERS,
20
- CLASSIC_PORT,
21
- LOCATION,
22
- CAPABILITY_MAP,
23
- CAPABILITY_CONFIG,
24
- REQUEST_TIMEOUT,
25
- USERNAME,
26
- )
27
-
28
- ERRORS = {1: Exception}
29
-
30
-
31
- class AioLivisi:
32
- """Handles the communication with the Livisi Smart Home controller."""
33
-
34
- instance = None
35
-
36
- def __init__(
37
- self, web_session: ClientSession = None, auth_headers: dict[str, Any] = None
38
- ) -> None:
39
- self._web_session: ClientSession = web_session
40
- self._auth_headers: dict[str, Any] = auth_headers
41
- self._token: str = ""
42
- self._livisi_connection_data: dict[str, str] = None
43
-
44
- async def async_set_token(
45
- self, livisi_connection_data: dict[str, str] = None
46
- ) -> None:
47
- """Set the JWT from the LIVISI Smart Home Controller."""
48
- access_data: dict = {}
49
- try:
50
- if self._livisi_connection_data is not None:
51
- self._livisi_connection_data = livisi_connection_data
52
- access_data = await self.async_get_jwt_token(livisi_connection_data)
53
- self.token = access_data["access_token"]
54
- self._auth_headers = {
55
- "authorization": f"Bearer {self.token}",
56
- "Content-type": "application/json",
57
- "Accept": "*/*",
58
- }
59
- except Exception as error:
60
- if len(access_data) == 0:
61
- raise IncorrectIpAddressException from error
62
- elif access_data["errorcode"] == 2009:
63
- raise WrongCredentialException from error
64
- else:
65
- raise ShcUnreachableException from error
66
-
67
- async def async_send_authorized_request(
68
- self,
69
- method,
70
- url: str,
71
- payload=None,
72
- ) -> dict:
73
- """Make a request to the Livisi Smart Home controller."""
74
- ip_address = self._livisi_connection_data["ip_address"]
75
- path = f"http://{ip_address}:{CLASSIC_PORT}/{url}"
76
- return await self.async_send_request(method, path, payload, self._auth_headers)
77
-
78
- async def async_send_unauthorized_request(
79
- self,
80
- method,
81
- url: str,
82
- headers,
83
- payload=None,
84
- ) -> dict:
85
- """Send a request without JWT token."""
86
- return await self.async_send_request(method, url, payload, headers)
87
-
88
- async def async_get_jwt_token(self, livisi_connection_data: dict[str, str]):
89
- """Send a request for getting the JWT token."""
90
- login_credentials = {
91
- AUTH_USERNAME: USERNAME,
92
- AUTH_PASSWORD: livisi_connection_data["password"],
93
- AUTH_GRANT_TYPE: "password",
94
- }
95
- headers = AUTHENTICATION_HEADERS
96
- self._livisi_connection_data = livisi_connection_data
97
- ip_address = self._livisi_connection_data["ip_address"]
98
- return await self.async_send_request(
99
- "post",
100
- url=f"http://{ip_address}:{CLASSIC_PORT}/auth/token",
101
- payload=login_credentials,
102
- headers=headers,
103
- )
104
-
105
- async def async_send_request(
106
- self, method, url: str, payload=None, headers=None
107
- ) -> dict:
108
- """Send a request to the Livisi Smart Home controller."""
109
- try:
110
- response = await self.__async_send_request(method, url, payload, headers)
111
- except Exception:
112
- response = await self.__async_send_request(method, url, payload, headers)
113
- if "errorcode" in response:
114
- if response["errorcode"] == 2007:
115
- raise TokenExpiredException
116
- return response
117
-
118
- async def __async_send_request(
119
- self, method, url: str, payload=None, headers=None
120
- ) -> dict:
121
- async with self._web_session.request(
122
- method,
123
- url,
124
- json=payload,
125
- headers=headers,
126
- ssl=False,
127
- timeout=REQUEST_TIMEOUT,
128
- ) as res:
129
- data = await res.json()
130
- return data
131
-
132
- async def async_get_controller(self) -> dict[str, Any]:
133
- """Get Livisi Smart Home controller data."""
134
- return await self.async_get_controller_status()
135
-
136
- async def async_get_controller_status(self) -> dict[str, Any]:
137
- """Get Livisi Smart Home controller status."""
138
- shc_info = await self.async_send_authorized_request("get", url="status")
139
- return shc_info
140
-
141
- async def async_get_devices(
142
- self,
143
- ) -> list[dict[str, Any]]:
144
- """Send a request for getting the devices."""
145
- devices = await self.async_send_authorized_request("get", url="device")
146
- capabilities = await self.async_send_authorized_request("get", url="capability")
147
-
148
- capability_map = {}
149
- capability_config = {}
150
-
151
- for capability in capabilities:
152
- if "device" in capability:
153
- device_id = capability["device"].split("/")[-1]
154
- if device_id not in capability_map:
155
- capability_map[device_id] = {}
156
- capability_config[device_id] = {}
157
- capability_map[device_id][capability["type"]] = (
158
- "/capability/" + capability["id"]
159
- )
160
- if "config" in capability:
161
- capability_config[device_id][capability["type"]] = capability["config"]
162
-
163
- for device in devices:
164
- device_id = device["id"]
165
- device[CAPABILITY_MAP] = capability_map.get(device_id, {})
166
- device[CAPABILITY_CONFIG] = capability_config.get(device_id, {})
167
-
168
- for device in devices.copy():
169
- if LOCATION in device and device.get(LOCATION) is not None:
170
- device[LOCATION] = device[LOCATION].removeprefix("/location/")
171
- return devices
172
-
173
- async def async_get_device_state(self, capability) -> dict[str, Any] | None:
174
- """Get the state of the device."""
175
- url = f"{capability}/state"
176
- try:
177
- return await self.async_send_authorized_request("get", url)
178
- except Exception:
179
- return None
180
-
181
- async def async_pss_set_state(self, capability_id, is_on: bool) -> dict[str, Any]:
182
- """Set the PSS state."""
183
- set_state_payload: dict[str, Any] = {
184
- "id": uuid.uuid4().hex,
185
- "type": "SetState",
186
- "namespace": "core.RWE",
187
- "target": capability_id,
188
- "params": {"onState": {"type": "Constant", "value": is_on}},
189
- }
190
- return await self.async_send_authorized_request(
191
- "post", "action", payload=set_state_payload
192
- )
193
-
194
- async def async_set_onstate(self, capability_id, is_on: bool) -> dict[str, Any]:
195
- """Set the onState for devices that support it."""
196
- set_state_payload: dict[str, Any] = {
197
- "id": uuid.uuid4().hex,
198
- "type": "SetState",
199
- "namespace": "core.RWE",
200
- "target": capability_id,
201
- "params": {"onState": {"type": "Constant", "value": is_on}},
202
- }
203
- return await self.async_send_authorized_request(
204
- "post", "action", payload=set_state_payload
205
- )
206
-
207
- async def async_variable_set_value(
208
- self, capability_id, value: bool
209
- ) -> dict[str, Any]:
210
- """Set the boolean variable state."""
211
- set_value_payload: dict[str, Any] = {
212
- "id": uuid.uuid4().hex,
213
- "type": "SetState",
214
- "namespace": "core.RWE",
215
- "target": capability_id,
216
- "params": {"value": {"type": "Constant", "value": value}},
217
- }
218
- return await self.async_send_authorized_request(
219
- "post", "action", payload=set_value_payload
220
- )
221
-
222
- async def async_vrcc_set_temperature(
223
- self, capability_id, target_temperature: float, is_avatar: bool
224
- ) -> dict[str, Any]:
225
- """Set the Virtual Climate Control state."""
226
- if is_avatar:
227
- params = "setpointTemperature"
228
- else:
229
- params = "pointTemperature"
230
- set_state_payload: dict[str, Any] = {
231
- "id": uuid.uuid4().hex,
232
- "type": "SetState",
233
- "namespace": "core.RWE",
234
- "target": capability_id,
235
- "params": {params: {"type": "Constant", "value": target_temperature}},
236
- }
237
- return await self.async_send_authorized_request(
238
- "post", "action", payload=set_state_payload
239
- )
240
-
241
- async def async_get_all_rooms(self) -> dict[str, Any]:
242
- """Get all the rooms from LIVISI configuration."""
243
- return await self.async_send_authorized_request("get", "location")
244
-
245
- @property
246
- def livisi_connection_data(self):
247
- """Return the connection data."""
248
- return self._livisi_connection_data
249
-
250
- @livisi_connection_data.setter
251
- def livisi_connection_data(self, new_value):
252
- self._livisi_connection_data = new_value
253
-
254
- @property
255
- def token(self):
256
- """Return the token."""
257
- return self._token
258
-
259
- @token.setter
260
- def token(self, new_value):
261
- self._token = new_value
262
-
263
- @property
264
- def web_session(self):
265
- """Return the web session."""
266
- return self._web_session
267
-
268
- @web_session.setter
269
- def web_session(self, new_value):
270
- self._web_session = new_value
livisi/const.py DELETED
@@ -1,40 +0,0 @@
1
- from typing import Final
2
-
3
-
4
- CLASSIC_PORT: Final = 8080
5
- AVATAR_PORT: Final = 9090
6
- USERNAME: Final = "admin"
7
- AUTH_USERNAME: Final = "username"
8
- AUTH_PASSWORD: Final = "password"
9
- AUTH_GRANT_TYPE: Final = "grant_type"
10
- REQUEST_TIMEOUT: Final = 2000
11
-
12
- ON_STATE: Final = "onState"
13
- VALUE: Final = "value"
14
- POINT_TEMPERATURE: Final = "pointTemperature"
15
- SET_POINT_TEMPERATURE: Final = "setpointTemperature"
16
- TEMPERATURE: Final = "temperature"
17
- HUMIDITY: Final = "humidity"
18
- LUMINANCE: Final = "luminance"
19
- IS_REACHABLE: Final = "isReachable"
20
- IS_OPEN: Final = "isOpen"
21
- LOCATION: Final = "location"
22
-
23
- KEY_INDEX: Final = "index"
24
- KEY_PRESS_TYPE: Final = "type"
25
- KEY_PRESS_SHORT: Final = "ShortPress"
26
- KEY_PRESS_LONG: Final = "LongPress"
27
-
28
-
29
- CAPABILITY_MAP: Final = "capabilityMap"
30
- CAPABILITY_CONFIG: Final = "capabilityConfig"
31
-
32
- EVENT_STATE_CHANGED = "StateChanged"
33
- EVENT_BUTTON_PRESSED = "ButtonPressed"
34
- EVENT_MOTION_DETECTED = "MotionDetected"
35
-
36
- AUTHENTICATION_HEADERS: Final = {
37
- "Authorization": "Basic Y2xpZW50SWQ6Y2xpZW50UGFzcw==",
38
- "Content-type": "application/json",
39
- "Accept": "application/json",
40
- }