ics_2000_python 0.1.4__tar.gz → 0.1.6__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.4 → ics_2000_python-0.1.6}/PKG-INFO +1 -1
  2. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/command.py +3 -2
  3. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/config.py +0 -2
  4. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/encryption.py +1 -0
  5. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/entities/blind_device.py +7 -10
  6. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/entities/color_temperature_device.py +2 -1
  7. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/entities/device.py +3 -2
  8. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/entities/dim_device.py +2 -1
  9. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/entities/entity.py +2 -2
  10. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/hub.py +69 -15
  11. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/model/device_config.py +9 -9
  12. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/model/device_data.py +2 -5
  13. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/model/entity_type.py +1 -1
  14. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/model/smart_meter_data.py +1 -2
  15. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/model/smart_meter_data_current.py +1 -4
  16. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/pyproject.toml +1 -1
  17. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/LICENSE +0 -0
  18. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/README.md +0 -0
  19. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/__init__.py +0 -0
  20. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/entities/__init__.py +0 -0
  21. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/entities/switch_device.py +1 -1
  22. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/exceptions.py +0 -0
  23. {ics_2000_python-0.1.4 → ics_2000_python-0.1.6}/ics_2000/model/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ics_2000_python
3
- Version: 0.1.4
3
+ Version: 0.1.6
4
4
  Summary:
5
5
  Keywords: ics_2000,ics-2000,klikaanklikuit
6
6
  Author: iiTzArcur
@@ -1,7 +1,8 @@
1
1
  import json
2
- from typing import List
3
2
  import socket
3
+
4
4
  import requests
5
+
5
6
  from .config import API_URL
6
7
  from .encryption import encrypt
7
8
  from .model.entity_type import Entity_Type
@@ -16,7 +17,7 @@ class Command:
16
17
  value: int | float,
17
18
  aes_key: str,
18
19
  entity_type: Entity_Type,
19
- device_functions: List[int | float] = [],
20
+ device_functions: list[int | float] = [],
20
21
  ):
21
22
  self.hub_mac = hub_mac
22
23
  self.device_id = device_id
@@ -1,7 +1,5 @@
1
-
2
1
  from .model.device_config import DeviceConfig
3
2
 
4
-
5
3
  API_URL = "https://trustsmartcloud2.com/ics2000_api"
6
4
  device_configs: dict[int, DeviceConfig] = {
7
5
  1: DeviceConfig(
@@ -1,4 +1,5 @@
1
1
  import base64
2
+
2
3
  from Crypto.Cipher import AES
3
4
  from Crypto.Util.Padding import unpad
4
5
 
@@ -4,21 +4,17 @@ from .device import Device
4
4
 
5
5
 
6
6
  class BlindDevice(Device):
7
- def __init__(self, hub, device_data: device_data.DeviceData, device_config: DeviceConfig):
7
+ def __init__(
8
+ self, hub, device_data: device_data.DeviceData, device_config: DeviceConfig
9
+ ):
8
10
  super().__init__(hub, device_data, device_config)
9
11
 
10
12
  if device_config.index_open is None:
11
- raise ValueError(
12
- f"index_open not defined for '{self.device_data.name}'"
13
- )
13
+ raise ValueError(f"index_open not defined for '{self.device_data.name}'")
14
14
  if device_config.index_close is None:
15
- raise ValueError(
16
- f"index_close not defined for '{self.device_data.name}'"
17
- )
15
+ raise ValueError(f"index_close not defined for '{self.device_data.name}'")
18
16
  if device_config.index_my is None:
19
- raise ValueError(
20
- f"index_my not defined for '{self.device_data.name}'"
21
- )
17
+ raise ValueError(f"index_my not defined for '{self.device_data.name}'")
22
18
 
23
19
  def open(self, send_local: bool = False) -> None:
24
20
  """Turn off the device.
@@ -55,6 +51,7 @@ class BlindDevice(Device):
55
51
  self.is_group,
56
52
  send_local,
57
53
  )
54
+
58
55
  def stop(self, send_local: bool = False) -> None:
59
56
  """Turn off the device.
60
57
 
@@ -1,7 +1,8 @@
1
1
  from typing import TYPE_CHECKING
2
+
3
+ from ..model.device_config import DeviceConfig
2
4
  from ..model.device_data import DeviceData
3
5
  from .dim_device import DimDevice
4
- from ..model.device_config import DeviceConfig
5
6
 
6
7
  if TYPE_CHECKING:
7
8
  from ..hub import Hub
@@ -1,7 +1,8 @@
1
1
  from typing import TYPE_CHECKING
2
- from .entity import Entity
3
- from ..model.device_data import DeviceData
2
+
4
3
  from ..model.device_config import DeviceConfig
4
+ from ..model.device_data import DeviceData
5
+ from .entity import Entity
5
6
 
6
7
  if TYPE_CHECKING:
7
8
  from ..hub import Hub
@@ -1,7 +1,8 @@
1
1
  from typing import TYPE_CHECKING
2
+
3
+ from ..model.device_config import DeviceConfig
2
4
  from ..model.device_data import DeviceData
3
5
  from .switch_device import SwitchDevice
4
- from ..model.device_config import DeviceConfig
5
6
 
6
7
  if TYPE_CHECKING:
7
8
  from ..hub import Hub
@@ -1,4 +1,4 @@
1
- from typing import List, TYPE_CHECKING
1
+ from typing import TYPE_CHECKING
2
2
 
3
3
  from ..model.device_data import DeviceData
4
4
  from ..model.entity_type import EntityType
@@ -25,7 +25,7 @@ class Entity:
25
25
  self.device_data = device_data
26
26
  self.is_group = entity_type == "group"
27
27
 
28
- def get_status(self) -> List[int]:
28
+ def get_status(self) -> list[int]:
29
29
  return self._hub.get_device_status(self.entity_id)
30
30
 
31
31
  def get_hub(self) -> "Hub":
@@ -1,11 +1,19 @@
1
- from datetime import datetime
2
1
  import json
2
+ import logging
3
+ from datetime import datetime
4
+
3
5
  import requests
4
6
 
5
- from ics_2000.exceptions import InvalidAuthException, InvalidHomeException, InvalidMacOrAuthException, NoHomeSelectedException
7
+ from ics_2000.exceptions import (
8
+ InvalidAuthException,
9
+ InvalidHomeException,
10
+ InvalidMacOrAuthException,
11
+ NoHomeSelectedException,
12
+ )
13
+
6
14
  from .command import Command
7
- from .encryption import decrypt
8
15
  from .config import API_URL, device_configs
16
+ from .encryption import decrypt
9
17
  from .entities.blind_device import BlindDevice
10
18
  from .entities.color_temperature_device import ColorTemperatureDevice
11
19
  from .entities.device import Device
@@ -15,6 +23,8 @@ from .model.device_config import DeviceConfig
15
23
  from .model.device_data import DeviceData
16
24
  from .model.entity_type import Entity_Type
17
25
 
26
+ logger = logging.getLogger(__name__)
27
+
18
28
 
19
29
  class Hub:
20
30
  """KlikAanKlikUit ICS-2000 Control Station connection info
@@ -73,7 +83,9 @@ class Hub:
73
83
  data = response.json()
74
84
  if data is not None:
75
85
  self.homes = data.get("homes", [])
76
- return dict([(home.get("home_id"), home.get("home_name"))for home in self.homes])
86
+ return dict(
87
+ [(home.get("home_id"), home.get("home_name")) for home in self.homes]
88
+ )
77
89
 
78
90
  def select_home(self, home_id: str) -> None:
79
91
  home = next(home for home in self.homes if home.get("home_id") == home_id)
@@ -95,7 +107,7 @@ class Hub:
95
107
  "email": self.email,
96
108
  "mac": self.mac,
97
109
  "password_hash": self.password,
98
- "home_id": "",
110
+ "home_id": self.home_id,
99
111
  },
100
112
  )
101
113
  data = []
@@ -158,7 +170,11 @@ class Hub:
158
170
 
159
171
  elif device_config.on_off_function is not None:
160
172
  self.devices.append(SwitchDevice(self, device_data, device_config))
161
- elif device_config.index_open is not None and device_config.index_close is not None and device_config.index_my is not None:
173
+ elif (
174
+ device_config.index_open is not None
175
+ and device_config.index_close is not None
176
+ and device_config.index_my is not None
177
+ ):
162
178
  self.devices.append(BlindDevice(self, device_data, device_config))
163
179
  else:
164
180
  self.devices.append(Device(self, device_data, device_config))
@@ -256,13 +272,17 @@ class Hub:
256
272
  "home_id": "",
257
273
  "entity_id": ids_string,
258
274
  }
259
- status_list = requests.post(
275
+ response = requests.post(
260
276
  f"{API_URL}/entity.php",
261
277
  params,
262
- ).json()
278
+ )
279
+ if response.status_code != 200:
280
+ raise Exception(
281
+ f"Unknown error while fetching device statuses, {response.status_code} response: {response.content}"
282
+ )
283
+ status_list = response.json()
263
284
 
264
285
  if len(status_list) == 0:
265
- print(params)
266
286
  raise Exception(
267
287
  f"Unknown error while fetching device statuses, response json: {status_list}"
268
288
  )
@@ -285,14 +305,48 @@ class Hub:
285
305
 
286
306
  def get_all_device_statuses(self):
287
307
  status_list = self.get_raw_device_statuses(False, True)
308
+
309
+ if not isinstance(status_list, list):
310
+ logger.warning(
311
+ "ICS-2000: invalid device status response; type=%s",
312
+ type(status_list).__name__,
313
+ )
314
+ return
315
+
288
316
  for device in status_list:
289
- json_status = device["status"]
290
- if "module" in json_status.keys():
291
- self.device_statuses[device["id"]] = json_status["module"]["functions"]
292
- elif "group" in json_status.keys():
293
- self.device_statuses[device["id"]] = json_status["group"]["functions"]
317
+ if not isinstance(device, dict):
318
+ logger.debug("ICS-2000: skipping malformed device record")
319
+ continue
320
+
321
+ device_id = device.get("id")
322
+ json_status = device.get("status")
323
+
324
+ if device_id is None or not isinstance(json_status, dict):
325
+ logger.debug(
326
+ "ICS-2000: skipping device with invalid status; id=%s",
327
+ device_id,
328
+ )
329
+ continue
330
+
331
+ if "module" in json_status:
332
+ section = json_status["module"]
333
+ elif "group" in json_status:
334
+ section = json_status["group"]
294
335
  else:
295
- Exception("Module or group data not found")
336
+ logger.debug(
337
+ "ICS-2000: unsupported status structure; id=%s",
338
+ device_id,
339
+ )
340
+ continue
341
+
342
+ if not isinstance(section, dict) or "functions" not in section:
343
+ logger.debug(
344
+ "ICS-2000: missing device functions; id=%s",
345
+ device_id,
346
+ )
347
+ continue
348
+
349
+ self.device_statuses[device_id] = section["functions"]
296
350
 
297
351
  def change_status(
298
352
  self,
@@ -5,15 +5,15 @@ class DeviceConfig:
5
5
  def __init__(
6
6
  self,
7
7
  model_name: str,
8
- disabled: Optional[bool] = None,
9
- on_off_function: Optional[int] = None,
10
- dim_function: Optional[int] = None,
11
- color_temperature_function: Optional[int] = None,
12
- max_brightness: Optional[int] = None,
13
- max_color_temperature: Optional[int] = None,
14
- index_open: Optional[int] = None,
15
- index_close: Optional[int] = None,
16
- index_my: Optional[int] = None,
8
+ disabled: bool | None = None,
9
+ on_off_function: int | None = None,
10
+ dim_function: int | None = None,
11
+ color_temperature_function: int | None = None,
12
+ max_brightness: int | None = None,
13
+ max_color_temperature: int | None = None,
14
+ index_open: int | None = None,
15
+ index_close: int | None = None,
16
+ index_my: int | None = None,
17
17
  ):
18
18
  self.disabled = disabled
19
19
  self.model_name = model_name
@@ -1,6 +1,3 @@
1
- from typing import Dict
2
-
3
-
4
1
  class DeviceData:
5
2
  def __init__(
6
3
  self,
@@ -9,8 +6,8 @@ class DeviceData:
9
6
  name: str,
10
7
  device: int,
11
8
  isGroup: bool,
12
- status: Dict[str, Dict[str, int]],
13
- data: Dict[str, Dict[str, int]],
9
+ status: dict[str, dict[str, int]],
10
+ data: dict[str, dict[str, int]],
14
11
  ):
15
12
  self.home_id = home_id
16
13
  self.id = id
@@ -8,4 +8,4 @@ class Entity_Type(enum.Enum):
8
8
  Scene = "scene"
9
9
 
10
10
 
11
- EntityType = Union[Literal["module"], Literal["group"], Literal["scene"]]
11
+ EntityType = Literal["module", "group", "scene"]
@@ -1,4 +1,3 @@
1
- from typing import Union
2
1
  from datetime import datetime
3
2
 
4
3
  Precision = str
@@ -7,7 +6,7 @@ Precision = str
7
6
  class SmartMeterData:
8
7
  def __init__(
9
8
  self,
10
- date: Union[datetime, str, int],
9
+ date: datetime | str | int,
11
10
  power_consumed_low_tariff: float,
12
11
  power_consumed: float,
13
12
  power_produced_low_tariff: float,
@@ -1,6 +1,3 @@
1
- from typing import List
2
-
3
-
4
1
  class SmartMeterDataCurrent:
5
2
  def __init__(
6
3
  self,
@@ -11,7 +8,7 @@ class SmartMeterDataCurrent:
11
8
  current_consumption: float,
12
9
  current_production: float,
13
10
  gas: float,
14
- raw_data_array: List[float],
11
+ raw_data_array: list[float],
15
12
  ):
16
13
  self.power_consumed_low_tariff = power_consumed_low_tariff
17
14
  self.power_consumed = power_consumed
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "ics_2000_python"
3
- version = "0.1.4"
3
+ version = "0.1.6"
4
4
  description = ""
5
5
  authors = ["iiTzArcur <zefanjaj@gmail.com>"]
6
6
  readme = "README.md"
File without changes
@@ -1,5 +1,5 @@
1
- from ..model.device_data import DeviceData
2
1
  from ..model.device_config import DeviceConfig
2
+ from ..model.device_data import DeviceData
3
3
  from .device import Device
4
4
 
5
5