ics_2000_python 0.1.1__tar.gz → 0.1.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.
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/PKG-INFO +5 -1
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/README.md +4 -0
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/command.py +2 -2
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/encryption.py +6 -4
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/entities/color_temperature_device.py +15 -1
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/entities/device.py +9 -1
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/entities/dim_device.py +25 -1
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/entities/entity.py +12 -3
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/entities/switch_device.py +27 -0
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/hub.py +33 -5
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/pyproject.toml +2 -2
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/LICENSE +0 -0
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/__init__.py +0 -0
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/config.py +0 -0
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/entities/__init__.py +0 -0
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/model/__init__.py +0 -0
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/model/device_config.py +0 -0
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/model/device_data.py +0 -0
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/model/entity_type.py +0 -0
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/model/smart_meter_data.py +0 -0
- {ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/model/smart_meter_data_current.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ics_2000_python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary:
|
|
5
5
|
Home-page: https://github.com/zefanjajobse/ics_2000_python
|
|
6
6
|
Keywords: ics_2000,ics-2000,klikaanklikuit
|
|
@@ -19,6 +19,10 @@ This library gives methods to control a ICS-2000 from KlikAanKlikUit. It is made
|
|
|
19
19
|
|
|
20
20
|
## Usage example:
|
|
21
21
|
```py
|
|
22
|
+
from ics_2000.entities.dim_device import DimDevice
|
|
23
|
+
from ics_2000.entities.switch_device import SwitchDevice
|
|
24
|
+
from ics_2000.hub import Hub
|
|
25
|
+
|
|
22
26
|
hub = Hub("example@email.com", "password")
|
|
23
27
|
hub.login() # Authenticate
|
|
24
28
|
hub.get_devices() # request devices
|
|
@@ -3,6 +3,10 @@ This library gives methods to control a ICS-2000 from KlikAanKlikUit. It is made
|
|
|
3
3
|
|
|
4
4
|
## Usage example:
|
|
5
5
|
```py
|
|
6
|
+
from ics_2000.entities.dim_device import DimDevice
|
|
7
|
+
from ics_2000.entities.switch_device import SwitchDevice
|
|
8
|
+
from ics_2000.hub import Hub
|
|
9
|
+
|
|
6
10
|
hub = Hub("example@email.com", "password")
|
|
7
11
|
hub.login() # Authenticate
|
|
8
12
|
hub.get_devices() # request devices
|
|
@@ -13,10 +13,10 @@ class Command:
|
|
|
13
13
|
hub_mac: str,
|
|
14
14
|
device_id: int,
|
|
15
15
|
device_function: int,
|
|
16
|
-
value: int,
|
|
16
|
+
value: int | float,
|
|
17
17
|
aes_key: str,
|
|
18
18
|
entity_type: Entity_Type,
|
|
19
|
-
device_functions: List[int] = [],
|
|
19
|
+
device_functions: List[int | float] = [],
|
|
20
20
|
):
|
|
21
21
|
self.hub_mac = hub_mac
|
|
22
22
|
self.device_id = device_id
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
from Crypto.Cipher import AES
|
|
3
3
|
from Crypto.Util.Padding import unpad
|
|
4
|
-
from Crypto.Random import get_random_bytes
|
|
5
4
|
|
|
6
5
|
encoding = "utf-8"
|
|
7
6
|
block_size = 16
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def pad(s):
|
|
10
|
+
return s + (block_size - len(s) % block_size) * chr(
|
|
11
|
+
block_size - len(s) % block_size
|
|
12
|
+
)
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
def decrypt(string: str, aes: str) -> str:
|
{ics_2000_python-0.1.1 → ics_2000_python-0.1.2}/ics_2000/entities/color_temperature_device.py
RENAMED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
1
2
|
from ..model.device_data import DeviceData
|
|
2
3
|
from .dim_device import DimDevice
|
|
3
4
|
from ..model.device_config import DeviceConfig
|
|
4
5
|
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ..hub import Hub
|
|
8
|
+
|
|
5
9
|
|
|
6
10
|
class ColorTemperatureDevice(DimDevice):
|
|
7
|
-
|
|
11
|
+
"""Class used for color changeable, dimmable devices"""
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self, hub: "Hub", device_data: DeviceData, device_config: DeviceConfig
|
|
15
|
+
):
|
|
8
16
|
super().__init__(hub, device_data, device_config)
|
|
9
17
|
|
|
10
18
|
if device_config.color_temperature_function is None:
|
|
@@ -15,6 +23,9 @@ class ColorTemperatureDevice(DimDevice):
|
|
|
15
23
|
def change_color_temperature(
|
|
16
24
|
self, color_temperature: float, send_local: bool = True
|
|
17
25
|
) -> None:
|
|
26
|
+
if self.device_config.color_temperature_function is None:
|
|
27
|
+
return
|
|
28
|
+
|
|
18
29
|
return self.get_hub().change_color_temperature(
|
|
19
30
|
self.entity_id,
|
|
20
31
|
self.device_config.color_temperature_function,
|
|
@@ -24,5 +35,8 @@ class ColorTemperatureDevice(DimDevice):
|
|
|
24
35
|
)
|
|
25
36
|
|
|
26
37
|
def get_color_temperature(self) -> float:
|
|
38
|
+
if self.device_config.color_temperature_function is None:
|
|
39
|
+
return 0
|
|
40
|
+
|
|
27
41
|
status = self.get_hub().get_device_status(self.entity_id)
|
|
28
42
|
return status[self.device_config.color_temperature_function]
|
|
@@ -1,14 +1,22 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
1
2
|
from .entity import Entity
|
|
2
3
|
from ..model.device_data import DeviceData
|
|
3
4
|
from ..model.device_config import DeviceConfig
|
|
4
5
|
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ..hub import Hub
|
|
8
|
+
|
|
5
9
|
|
|
6
10
|
class Device(Entity):
|
|
7
|
-
def __init__(
|
|
11
|
+
def __init__(
|
|
12
|
+
self, hub: "Hub", device_data: DeviceData, device_config: DeviceConfig
|
|
13
|
+
):
|
|
8
14
|
super().__init__(
|
|
9
15
|
hub, device_data, "group" in device_data.data and "group" or "module"
|
|
10
16
|
)
|
|
11
17
|
self.device_config = device_config
|
|
18
|
+
"""List of device functions"""
|
|
12
19
|
self.disabled = (
|
|
13
20
|
device_config.disabled if device_config.disabled is not None else False
|
|
14
21
|
)
|
|
22
|
+
"""If the device can be changed"""
|
|
@@ -1,16 +1,33 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
1
2
|
from ..model.device_data import DeviceData
|
|
2
3
|
from .switch_device import SwitchDevice
|
|
3
4
|
from ..model.device_config import DeviceConfig
|
|
4
5
|
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ..hub import Hub
|
|
8
|
+
|
|
5
9
|
|
|
6
10
|
class DimDevice(SwitchDevice):
|
|
7
|
-
|
|
11
|
+
"""Class used for all dimmable devices"""
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self, hub: "Hub", device_data: DeviceData, device_config: DeviceConfig
|
|
15
|
+
):
|
|
8
16
|
super().__init__(hub, device_data, device_config)
|
|
9
17
|
|
|
10
18
|
if device_config.dim_function is None:
|
|
11
19
|
raise ValueError(f"Dim function not defined for '{self.device_data.name}'")
|
|
12
20
|
|
|
13
21
|
def dim(self, dim_level: int, send_local: bool = True) -> None:
|
|
22
|
+
"""Change the dim level of the device.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
dim_level: new dim level (0-255).
|
|
26
|
+
send_local: Use the ip_address set on the hub to talk to the device
|
|
27
|
+
"""
|
|
28
|
+
if self.device_config.dim_function is None:
|
|
29
|
+
return
|
|
30
|
+
|
|
14
31
|
return self.get_hub().dim_device(
|
|
15
32
|
self.entity_id,
|
|
16
33
|
self.device_config.dim_function,
|
|
@@ -20,5 +37,12 @@ class DimDevice(SwitchDevice):
|
|
|
20
37
|
)
|
|
21
38
|
|
|
22
39
|
def get_dim_level(self) -> int:
|
|
40
|
+
"""Get the dim level of a device
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Current dim level (0-255)."""
|
|
44
|
+
if self.device_config.dim_function is None:
|
|
45
|
+
return 0
|
|
46
|
+
|
|
23
47
|
status = self.get_hub().get_device_status(self.entity_id)
|
|
24
48
|
return status[self.device_config.dim_function]
|
|
@@ -1,13 +1,22 @@
|
|
|
1
|
-
from typing import List
|
|
1
|
+
from typing import List, TYPE_CHECKING
|
|
2
|
+
|
|
2
3
|
from ..model.device_data import DeviceData
|
|
3
4
|
from ..model.entity_type import EntityType
|
|
4
5
|
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ..hub import Hub
|
|
8
|
+
|
|
5
9
|
|
|
6
10
|
class Entity:
|
|
7
|
-
|
|
11
|
+
"""Class used for all devices"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, hub: "Hub", device_data: DeviceData, entity_type: EntityType):
|
|
8
14
|
self._hub = hub
|
|
15
|
+
"""Main hub"""
|
|
9
16
|
self.entity_id = int(device_data.id)
|
|
17
|
+
"""ID"""
|
|
10
18
|
self.name = device_data.data[entity_type].get("name", "")
|
|
19
|
+
"""Name of the device"""
|
|
11
20
|
self.device_type = (
|
|
12
21
|
device_data.data[entity_type].get("device", "")
|
|
13
22
|
if "device" in device_data.data[entity_type]
|
|
@@ -19,7 +28,7 @@ class Entity:
|
|
|
19
28
|
def get_status(self) -> List[int]:
|
|
20
29
|
return self._hub.get_device_status(self.entity_id)
|
|
21
30
|
|
|
22
|
-
def get_hub(self):
|
|
31
|
+
def get_hub(self) -> "Hub":
|
|
23
32
|
return self._hub
|
|
24
33
|
|
|
25
34
|
def change_status(self, device_function: int, value: int, send_local: bool) -> None:
|
|
@@ -4,6 +4,8 @@ from .device import Device
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class SwitchDevice(Device):
|
|
7
|
+
"""Class used for all switch devices"""
|
|
8
|
+
|
|
7
9
|
def __init__(self, hub, device_data: DeviceData, device_config: DeviceConfig):
|
|
8
10
|
super().__init__(hub, device_data, device_config)
|
|
9
11
|
|
|
@@ -13,6 +15,14 @@ class SwitchDevice(Device):
|
|
|
13
15
|
)
|
|
14
16
|
|
|
15
17
|
def turn_on_off(self, on: bool, send_local: bool = True) -> None:
|
|
18
|
+
"""Change the state of the device.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
on: new state of the device, True == on.
|
|
22
|
+
send_local: Use the ip_address set on the hub to talk to the device
|
|
23
|
+
"""
|
|
24
|
+
if self.device_config.on_off_function is None:
|
|
25
|
+
return
|
|
16
26
|
self.get_hub().turn_device_on_off(
|
|
17
27
|
self.entity_id,
|
|
18
28
|
on,
|
|
@@ -22,6 +32,13 @@ class SwitchDevice(Device):
|
|
|
22
32
|
)
|
|
23
33
|
|
|
24
34
|
def turn_on(self, send_local: bool = True) -> None:
|
|
35
|
+
"""Turn on the device.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
send_local: Use the ip_address set on the hub to talk to the device
|
|
39
|
+
"""
|
|
40
|
+
if self.device_config.on_off_function is None:
|
|
41
|
+
return
|
|
25
42
|
self.get_hub().turn_device_on_off(
|
|
26
43
|
self.entity_id,
|
|
27
44
|
True,
|
|
@@ -31,6 +48,13 @@ class SwitchDevice(Device):
|
|
|
31
48
|
)
|
|
32
49
|
|
|
33
50
|
def turn_off(self, send_local: bool = True) -> None:
|
|
51
|
+
"""Turn off the device.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
send_local: Use the ip_address set on the hub to talk to the device
|
|
55
|
+
"""
|
|
56
|
+
if self.device_config.on_off_function is None:
|
|
57
|
+
return
|
|
34
58
|
self.get_hub().turn_device_on_off(
|
|
35
59
|
self.entity_id,
|
|
36
60
|
False,
|
|
@@ -40,5 +64,8 @@ class SwitchDevice(Device):
|
|
|
40
64
|
)
|
|
41
65
|
|
|
42
66
|
def get_on_status(self) -> bool:
|
|
67
|
+
"""Returns true if device is turned on"""
|
|
68
|
+
if self.device_config.on_off_function is None:
|
|
69
|
+
return False
|
|
43
70
|
status = self.get_status()
|
|
44
71
|
return status[self.device_config.on_off_function] == 1
|
|
@@ -14,21 +14,40 @@ from .model.entity_type import Entity_Type
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class Hub:
|
|
17
|
+
"""KlikAanKlikUit ICS-2000 Control Station connection info
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
email: Email address of the account connected to the ICS-2000
|
|
22
|
+
password: Password of the account connected to the ICS-2000
|
|
23
|
+
"""
|
|
24
|
+
|
|
17
25
|
def __init__(self, email: str, password: str):
|
|
18
26
|
self.devices: list[
|
|
19
27
|
Device | DimDevice | SwitchDevice | ColorTemperatureDevice
|
|
20
28
|
] = []
|
|
29
|
+
"""List of available devices within the authenticated home"""
|
|
21
30
|
self.aes_key: str | None = None
|
|
31
|
+
"""Authentication key"""
|
|
22
32
|
self.mac: str | None = None
|
|
33
|
+
"""MAC-Address of the KlikAanKlikUit ICS-2000"""
|
|
23
34
|
self.home_name: str | None = None
|
|
35
|
+
"""Name of the home given within KlikAanKlikUit"""
|
|
24
36
|
self.home_id: str | None = None
|
|
37
|
+
"""ID of the home"""
|
|
25
38
|
self.email = email
|
|
39
|
+
"""Email address of the account connected to the ICS-2000"""
|
|
26
40
|
self.password = password
|
|
41
|
+
"""Password of the account connected to the ICS-2000"""
|
|
27
42
|
self.local_address: str | None = None
|
|
43
|
+
"""Local IP address when you want to control it locally"""
|
|
28
44
|
self.device_statuses: dict[int, list[int]] = {}
|
|
45
|
+
"""Statuses of the devices"""
|
|
29
46
|
self.update_date = datetime.min
|
|
47
|
+
"""When the device statuses were last updated"""
|
|
30
48
|
|
|
31
|
-
def login(self):
|
|
49
|
+
def login(self) -> None:
|
|
50
|
+
"""Logs in to the account given"""
|
|
32
51
|
test = requests.post(
|
|
33
52
|
f"{API_URL}/account.php",
|
|
34
53
|
{
|
|
@@ -91,7 +110,10 @@ class Hub:
|
|
|
91
110
|
|
|
92
111
|
return data
|
|
93
112
|
|
|
94
|
-
def get_devices(
|
|
113
|
+
def get_devices(
|
|
114
|
+
self,
|
|
115
|
+
) -> list[Device | DimDevice | SwitchDevice | ColorTemperatureDevice]:
|
|
116
|
+
"""Gets all devices connected to the hub"""
|
|
95
117
|
if self.home_id is None:
|
|
96
118
|
raise Exception("no home")
|
|
97
119
|
|
|
@@ -129,14 +151,19 @@ class Hub:
|
|
|
129
151
|
self.devices.append(SwitchDevice(self, device_data, device_config))
|
|
130
152
|
else:
|
|
131
153
|
self.devices.append(Device(self, device_data, device_config))
|
|
154
|
+
return self.devices
|
|
132
155
|
|
|
133
156
|
def create_command(
|
|
134
|
-
self,
|
|
157
|
+
self,
|
|
158
|
+
device_id: int,
|
|
159
|
+
device_function: int,
|
|
160
|
+
value: int | float,
|
|
161
|
+
entity_type: Entity_Type,
|
|
135
162
|
) -> Command:
|
|
136
163
|
if self.aes_key is None or self.mac is None:
|
|
137
164
|
raise Exception("no mac or auth key")
|
|
138
165
|
|
|
139
|
-
device_functions: list[int] = []
|
|
166
|
+
device_functions: list[int | float] = []
|
|
140
167
|
|
|
141
168
|
# if entity_type == Entity_Type.Group:
|
|
142
169
|
# device_functions = self.device_statuses.get(device_id, [])
|
|
@@ -159,6 +186,7 @@ class Hub:
|
|
|
159
186
|
is_group: bool,
|
|
160
187
|
send_local: bool,
|
|
161
188
|
) -> None:
|
|
189
|
+
"""Change to the enabled state of the device"""
|
|
162
190
|
command = self.create_command(
|
|
163
191
|
device_id,
|
|
164
192
|
on_function,
|
|
@@ -294,7 +322,7 @@ class Hub:
|
|
|
294
322
|
self,
|
|
295
323
|
device_id: int,
|
|
296
324
|
color_temp_function: int,
|
|
297
|
-
color_temperature:
|
|
325
|
+
color_temperature: float,
|
|
298
326
|
is_group: bool,
|
|
299
327
|
send_local: bool,
|
|
300
328
|
):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "ics_2000_python"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = ""
|
|
5
5
|
authors = ["iiTzArcur <zefanjaj@gmail.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -15,7 +15,7 @@ pycryptodome = "^3.21.0"
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
[tool.poetry.group.test.dependencies]
|
|
18
|
-
pytest = "^8.3.
|
|
18
|
+
pytest = "^8.3.4"
|
|
19
19
|
requests-mock = "^1.12.1"
|
|
20
20
|
|
|
21
21
|
[build-system]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|