ics_2000_python 0.1.0__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.
Files changed (23) hide show
  1. ics_2000_python-0.1.2/PKG-INFO +34 -0
  2. ics_2000_python-0.1.2/README.md +18 -0
  3. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/command.py +2 -2
  4. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/encryption.py +6 -4
  5. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/entities/color_temperature_device.py +15 -1
  6. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/entities/device.py +9 -1
  7. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/entities/dim_device.py +25 -1
  8. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/entities/entity.py +12 -3
  9. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/entities/switch_device.py +27 -0
  10. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/hub.py +35 -7
  11. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/pyproject.toml +4 -2
  12. ics_2000_python-0.1.0/PKG-INFO +0 -14
  13. ics_2000_python-0.1.0/README.md +0 -0
  14. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/LICENSE +0 -0
  15. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/__init__.py +0 -0
  16. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/config.py +0 -0
  17. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/entities/__init__.py +0 -0
  18. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/model/__init__.py +0 -0
  19. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/model/device_config.py +0 -0
  20. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/model/device_data.py +0 -0
  21. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/model/entity_type.py +0 -0
  22. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/model/smart_meter_data.py +0 -0
  23. {ics_2000_python-0.1.0 → ics_2000_python-0.1.2}/ics_2000/model/smart_meter_data_current.py +0 -0
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.1
2
+ Name: ics_2000_python
3
+ Version: 0.1.2
4
+ Summary:
5
+ Home-page: https://github.com/zefanjajobse/ics_2000_python
6
+ Keywords: ics_2000,ics-2000,klikaanklikuit
7
+ Author: iiTzArcur
8
+ Author-email: zefanjaj@gmail.com
9
+ Requires-Python: >=3.12,<4.0
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Requires-Dist: pycryptodome (>=3.21.0,<4.0.0)
13
+ Requires-Dist: requests (>=2.32.3,<3.0.0)
14
+ Project-URL: Repository, https://github.com/zefanjajobse/ics_2000_python
15
+ Description-Content-Type: text/markdown
16
+
17
+ # Python KlikAanKlikUit ICS-2000 library
18
+ This library gives methods to control a ICS-2000 from KlikAanKlikUit. It is made to add support for this device within Home assistant, which implementation is available in [this](https://github.com/zefanjajobse/ics-2000-home-assistant) repository.
19
+
20
+ ## Usage example:
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
+
26
+ hub = Hub("example@email.com", "password")
27
+ hub.login() # Authenticate
28
+ hub.get_devices() # request devices
29
+ for device in hub.devices:
30
+ if type(device) is SwitchDevice or type(device) is DimDevice:
31
+ print(device.name)
32
+ if device.get_on_status(): # returns bool
33
+ device.turn_off(False) # turn a device off
34
+ ```
@@ -0,0 +1,18 @@
1
+ # Python KlikAanKlikUit ICS-2000 library
2
+ This library gives methods to control a ICS-2000 from KlikAanKlikUit. It is made to add support for this device within Home assistant, which implementation is available in [this](https://github.com/zefanjajobse/ics-2000-home-assistant) repository.
3
+
4
+ ## Usage example:
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
+
10
+ hub = Hub("example@email.com", "password")
11
+ hub.login() # Authenticate
12
+ hub.get_devices() # request devices
13
+ for device in hub.devices:
14
+ if type(device) is SwitchDevice or type(device) is DimDevice:
15
+ print(device.name)
16
+ if device.get_on_status(): # returns bool
17
+ device.turn_off(False) # turn a device off
18
+ ```
@@ -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
- pad = lambda s: s + (block_size - len(s) % block_size) * chr(
9
- block_size - len(s) % block_size
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:
@@ -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
- def __init__(self, hub, device_data: DeviceData, device_config: DeviceConfig):
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__(self, hub, device_data: DeviceData, device_config: DeviceConfig):
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
- def __init__(self, hub, device_data: DeviceData, device_config: DeviceConfig):
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
- def __init__(self, hub, device_data: DeviceData, entity_type: EntityType):
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
27
- self.local_address = ""
41
+ """Password of the account connected to the ICS-2000"""
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(self):
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, device_id: int, device_function: int, value: int, entity_type: Entity_Type
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,
@@ -175,7 +203,7 @@ class Hub:
175
203
 
176
204
  def send_command_to_hub(self, command: Command, port):
177
205
  if self.local_address is None:
178
- raise ValueError("Local address is undefined")
206
+ raise ValueError("Local address is not set")
179
207
 
180
208
  if not isinstance(port, int):
181
209
  raise ValueError("Port needs to be an integer")
@@ -294,7 +322,7 @@ class Hub:
294
322
  self,
295
323
  device_id: int,
296
324
  color_temp_function: int,
297
- color_temperature: int,
325
+ color_temperature: float,
298
326
  is_group: bool,
299
327
  send_local: bool,
300
328
  ):
@@ -1,10 +1,12 @@
1
1
  [tool.poetry]
2
2
  name = "ics_2000_python"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  description = ""
5
5
  authors = ["iiTzArcur <zefanjaj@gmail.com>"]
6
6
  readme = "README.md"
7
7
  packages = [{include = "ics_2000"}]
8
+ keywords = ["ics_2000", "ics-2000", "klikaanklikuit"]
9
+ repository = "https://github.com/zefanjajobse/ics_2000_python"
8
10
 
9
11
  [tool.poetry.dependencies]
10
12
  python = "^3.12"
@@ -13,7 +15,7 @@ pycryptodome = "^3.21.0"
13
15
 
14
16
 
15
17
  [tool.poetry.group.test.dependencies]
16
- pytest = "^8.3.3"
18
+ pytest = "^8.3.4"
17
19
  requests-mock = "^1.12.1"
18
20
 
19
21
  [build-system]
@@ -1,14 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: ics_2000_python
3
- Version: 0.1.0
4
- Summary:
5
- Author: iiTzArcur
6
- Author-email: zefanjaj@gmail.com
7
- Requires-Python: >=3.12,<4.0
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.12
10
- Requires-Dist: pycryptodome (>=3.21.0,<4.0.0)
11
- Requires-Dist: requests (>=2.32.3,<3.0.0)
12
- Description-Content-Type: text/markdown
13
-
14
-
File without changes
File without changes