aioccl 2024.12.6__tar.gz → 2024.12.12__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aioccl
3
- Version: 2024.12.6
3
+ Version: 2024.12.12
4
4
  Summary: A Python library for CCL API server
5
5
  Home-page: https://github.com/fkiscd/aioccl
6
6
  Download-URL: https://github.com/fkiscd/aioccl
@@ -17,7 +17,6 @@ Classifier: Programming Language :: Python :: 3.12
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
19
  Requires-Dist: aiohttp>3
20
- Requires-Dist: aiohttp_cors>=0.7.0
21
20
 
22
21
  # aioCCL
23
22
  A Python library for CCL API server
@@ -0,0 +1,194 @@
1
+ """CCL device mapping."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import time
7
+ from typing import Callable, TypedDict
8
+
9
+ from .sensor import CCLSensor, CCL_SENSORS
10
+
11
+ _LOGGER = logging.getLogger(__name__)
12
+
13
+ CCL_DEVICE_INFO_TYPES = ("serial_no", "mac_address", "model", "fw_ver")
14
+
15
+
16
+ class CCLDevice:
17
+ """Mapping for a CCL device."""
18
+
19
+ def __init__(self, passkey: str):
20
+ """Initialize a CCL device."""
21
+
22
+ class Info(TypedDict):
23
+ """Store device information."""
24
+ fw_ver: str | None
25
+ last_update_time: float | None
26
+ mac_address: str | None
27
+ model: str | None
28
+ passkey: str
29
+ serial_no: str | None
30
+
31
+ self._info: Info = {
32
+ "fw_ver": None,
33
+ "last_update_time": None,
34
+ "mac_address": None,
35
+ "model": None,
36
+ "passkey": passkey,
37
+ "serial_no": None,
38
+ }
39
+
40
+ self._binary_sensors: dict[str, CCLSensor] | None = {}
41
+ self._sensors: dict[str, CCLSensor] | None = {}
42
+ self._update_callbacks = {}
43
+
44
+ self._new_binary_sensor_callbacks = set()
45
+ self._new_sensors: list[CCLSensor] | None = []
46
+ self._new_sensor_callbacks = set()
47
+
48
+ @property
49
+ def passkey(self) -> str:
50
+ """Return the passkey."""
51
+ return self._info["passkey"]
52
+
53
+ @property
54
+ def device_id(self) -> str | None:
55
+ """Return the device ID."""
56
+ if self.mac_address is None:
57
+ return None
58
+ return self.mac_address.replace(":", "").lower()[-6:]
59
+
60
+ @property
61
+ def last_update_time(self) -> str | None:
62
+ """Return the last update time."""
63
+ return self._info["last_update_time"]
64
+
65
+ @property
66
+ def name(self) -> str | None:
67
+ """Return the display name."""
68
+ if self.device_id is not None:
69
+ return self.model + " - " + self.device_id
70
+ return self._info["model"]
71
+
72
+ @property
73
+ def mac_address(self) -> str | None:
74
+ """Return the MAC address."""
75
+ return self._info["mac_address"]
76
+
77
+ @property
78
+ def model(self) -> str | None:
79
+ """Return the model."""
80
+ return self._info["model"]
81
+
82
+ @property
83
+ def fw_ver(self) -> str | None:
84
+ """Return the firmware version."""
85
+ return self._info["fw_ver"]
86
+
87
+ @property
88
+ def binary_sensors(self) -> dict[str, CCLSensor] | None:
89
+ """Store binary sensor data under this device."""
90
+ return self._binary_sensors
91
+
92
+ @property
93
+ def sensors(self) -> dict[str, CCLSensor] | None:
94
+ """Store sensor data under this device."""
95
+ return self._sensors
96
+
97
+ def update_info(self, new_info: dict[str, None | str]) -> None:
98
+ """Add or update device info."""
99
+ for key, value in new_info.items():
100
+ if key in self._info:
101
+ self._info[key] = str(value)
102
+ self._info["last_update_time"] = time.monotonic()
103
+
104
+ def update_sensors(self, sensors: dict[str, None | str | int | float]) -> None:
105
+ """Add or update all sensor values."""
106
+ for key, value in sensors.items():
107
+ if CCL_SENSORS.get(key).binary:
108
+ if key not in self.binary_sensors:
109
+ self._binary_sensors[key] = CCLSensor(key)
110
+ self._new_sensors.append(self.binary_sensors[key])
111
+ self._binary_sensors[key].value = value
112
+ else:
113
+ if key not in self.sensors:
114
+ self._sensors[key] = CCLSensor(key)
115
+ self._new_sensors.append(self.sensors[key])
116
+ self._sensors[key].value = value
117
+
118
+ add_count = self._publish_new_sensors()
119
+ _LOGGER.debug(
120
+ "Added %s new sensors for device %s at %s.",
121
+ add_count,
122
+ self.device_id,
123
+ self.last_update_time,
124
+ )
125
+
126
+ update_count = self._publish_updates()
127
+ _LOGGER.debug(
128
+ "Updated %s sensors in total for device %s at %s.",
129
+ update_count,
130
+ self.device_id,
131
+ self.last_update_time,
132
+ )
133
+
134
+ def register_update_cb(self, sensor_key, callback: Callable[[], None]) -> None:
135
+ """Register callback, called when Sensor changes state."""
136
+ self._update_callbacks[sensor_key] = callback
137
+
138
+ def remove_update_cb(self, sensor_key, callback: Callable[[], None]) -> None:
139
+ """Remove previously registered callback."""
140
+ self._update_callbacks.pop(sensor_key, None)
141
+
142
+ def _publish_updates(self) -> int:
143
+ """Schedule call all registered callbacks."""
144
+ count = 0
145
+ for sensor_key, callback in self._update_callbacks.items():
146
+ try:
147
+ callback()
148
+ count += 1
149
+ except Exception as err: # pylint: disable=broad-exception-caught
150
+ _LOGGER.warning(
151
+ "Error while updating sensor %s for device %s: %s",
152
+ sensor_key,
153
+ self.device_id,
154
+ err,
155
+ )
156
+ return count
157
+
158
+ def register_new_binary_sensor_cb(self, callback: Callable[[], None]) -> None:
159
+ """Register callback, called when Sensor changes state."""
160
+ self._new_binary_sensor_callbacks.add(callback)
161
+
162
+ def remove_new_binary_sensor_cb(self, callback: Callable[[], None]) -> None:
163
+ """Remove previously registered callback."""
164
+ self._new_binary_sensor_callbacks.discard(callback)
165
+
166
+ def register_new_sensor_cb(self, callback: Callable[[], None]) -> None:
167
+ """Register callback, called when Sensor changes state."""
168
+ self._new_sensor_callbacks.add(callback)
169
+
170
+ def remove_new_sensor_cb(self, callback: Callable[[], None]) -> None:
171
+ """Remove previously registered callback."""
172
+ self._new_sensor_callbacks.discard(callback)
173
+
174
+ def _publish_new_sensors(self) -> None:
175
+ """Schedule call all registered callbacks."""
176
+ count = 0
177
+ for sensor in self._new_sensors[:]:
178
+ try:
179
+ if sensor.binary:
180
+ for callback in self._new_binary_sensor_callbacks:
181
+ callback(sensor)
182
+ else:
183
+ for callback in self._new_sensor_callbacks:
184
+ callback(sensor)
185
+ self._new_sensors.remove(sensor)
186
+ count += 1
187
+ except Exception as err: # pylint: disable=broad-exception-caught
188
+ _LOGGER.warning(
189
+ "Error while adding sensor %s for device %s: %s",
190
+ sensor.key,
191
+ self.device_id,
192
+ err,
193
+ )
194
+ return count
@@ -34,7 +34,7 @@ class CCLSensor:
34
34
  @property
35
35
  def compartment(self) -> None | str:
36
36
  """Decide which compartment it belongs to."""
37
- if isinstance(CCL_SENSORS[self._key].compartment, CCLDeviceCompartment):
37
+ if CCL_SENSORS[self._key].compartment in CCLDeviceCompartment:
38
38
  return CCL_SENSORS[self._key].compartment.value
39
39
  return None
40
40
 
@@ -23,60 +23,60 @@ class CCLServer:
23
23
  def register(device: CCLDevice) -> None:
24
24
  """Register a device with a passkey."""
25
25
  CCLServer.devices.setdefault(device.passkey, device)
26
- _LOGGER.debug("Device registered: %s", device)
27
-
26
+ _LOGGER.debug("Device registered: %s", device.passkey)
27
+
28
28
  @staticmethod
29
29
  async def handler(request: web.BaseRequest | web.Request) -> web.Response:
30
30
  """Handle POST requests for data updating."""
31
- _body: dict[str, None | str | int | float] = {}
32
- _device: CCLDevice = None
33
- _info: dict[str, None | str] = {}
34
- _passkey: str = ''
35
- _sensors: dict[str, None | str | int | float] = {}
36
- _status: None | int = None
37
- _text: None | str = None
38
-
31
+ body: dict[str, None | str | int | float] = {}
32
+ device: CCLDevice = None
33
+ info: dict[str, None | str] = {}
34
+ passkey: str = ""
35
+ sensors: dict[str, None | str | int | float] = {}
36
+ status: None | int = None
37
+ text: None | str = None
38
+
39
+ _LOGGER.debug("Request received: %s", passkey)
39
40
  try:
40
- _passkey = request.path[-8:]
41
- for passkey in CCLServer.devices:
42
- if passkey == _passkey:
43
- _device = CCLServer.devices[_passkey]
41
+ passkey = request.path[-8:]
42
+ for ref_passkey, ref_device in CCLServer.devices.items():
43
+ if passkey == ref_passkey:
44
+ device = ref_device
44
45
  break
45
- assert isinstance(_device, CCLDevice), 404
46
+ assert isinstance(device, CCLDevice), 404
46
47
 
47
48
  assert request.content_type == "application/json", 400
48
49
  assert 0 < request.content_length <= 5000, 400
49
50
 
50
- _body = await request.json()
51
+ body = await request.json()
51
52
 
52
53
  except Exception as err: # pylint: disable=broad-exception-caught
53
- _status = err.args[0]
54
- if _status == 400:
55
- _text = "400 Bad Request"
56
- elif _status == 404:
57
- _text = "404 Not Found"
54
+ status = err.args[0]
55
+ if status == 400:
56
+ text = "400 Bad Request"
57
+ elif status == 404:
58
+ text = "404 Not Found"
58
59
  else:
59
- _status = 500
60
- _text = "500 Internal Server Error"
60
+ status = 500
61
+ text = "500 Internal Server Error"
61
62
  _LOGGER.debug("Request exception occured: %s", err)
62
- return web.Response(status=_status, text=_text)
63
+ return web.Response(status=status, text=text)
63
64
 
64
-
65
- for key, value in _body.items():
65
+ for key, value in body.items():
66
66
  if key in CCL_DEVICE_INFO_TYPES:
67
- _info.setdefault(key, value)
67
+ info.setdefault(key, value)
68
68
  elif key in CCL_SENSORS:
69
- _sensors.setdefault(key, value)
69
+ sensors.setdefault(key, value)
70
70
 
71
- _device.update_info(_info)
72
- _device.update_sensors(_sensors)
73
- _status = 200
74
- _text = "200 OK"
75
- _LOGGER.debug("Request processed: %s", _passkey)
76
- return web.Response(status=_status, text=_text)
71
+ device.update_info(info)
72
+ device.update_sensors(sensors)
73
+ status = 200
74
+ text = "200 OK"
75
+ _LOGGER.debug("Request processed: %s", passkey)
76
+ return web.Response(status=status, text=text)
77
77
 
78
78
  app = web.Application()
79
- app.add_routes([web.get('/{passkey}', handler)])
79
+ app.add_routes([web.get("/{passkey}", handler)])
80
80
  runner = web.AppRunner(app)
81
81
 
82
82
  @staticmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aioccl
3
- Version: 2024.12.6
3
+ Version: 2024.12.12
4
4
  Summary: A Python library for CCL API server
5
5
  Home-page: https://github.com/fkiscd/aioccl
6
6
  Download-URL: https://github.com/fkiscd/aioccl
@@ -17,7 +17,6 @@ Classifier: Programming Language :: Python :: 3.12
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
19
  Requires-Dist: aiohttp>3
20
- Requires-Dist: aiohttp_cors>=0.7.0
21
20
 
22
21
  # aioCCL
23
22
  A Python library for CCL API server
@@ -0,0 +1 @@
1
+ aiohttp>3
@@ -3,7 +3,7 @@
3
3
  from pathlib import Path
4
4
  from setuptools import find_packages, setup
5
5
 
6
- VERSION = "2024.12.6"
6
+ VERSION = "2024.12.12"
7
7
 
8
8
  ROOT_DIR = Path(__file__).parent.resolve()
9
9
 
@@ -20,8 +20,7 @@ setup(
20
20
  url="https://github.com/fkiscd/aioccl",
21
21
  download_url="https://github.com/fkiscd/aioccl",
22
22
  install_requires=[
23
- "aiohttp>3",
24
- "aiohttp_cors>=0.7.0"
23
+ "aiohttp>3"
25
24
  ],
26
25
  include_package_data=True,
27
26
  classifiers=[
@@ -1,151 +0,0 @@
1
- """CCL device mapping."""
2
-
3
- from __future__ import annotations
4
-
5
- import logging
6
- import time
7
- from typing import Callable
8
-
9
- from .sensor import CCLSensor, CCL_SENSORS
10
-
11
- _LOGGER = logging.getLogger(__name__)
12
-
13
- CCL_DEVICE_INFO_TYPES = ("serial_no", "mac_address", "model", "fw_ver")
14
-
15
-
16
- class CCLDevice:
17
- """Mapping for a CCL device."""
18
- _binary_sensors: dict[str, CCLSensor] | None = {}
19
- _device_id: str | None = None
20
- _fw_ver: str | None = None
21
- _last_updated_time: float | None = None
22
- _mac_address: str | None = None
23
- _model: str | None = None
24
- _new_binary_sensor_callbacks = set()
25
- _new_sensors: list[CCLSensor] | None = []
26
- _new_sensor_callbacks = set()
27
- _passkey = ''
28
- _sensors: dict[str, CCLSensor] | None = {}
29
- _serial_no: str | None = None
30
- _update_callbacks = set()
31
-
32
-
33
- def __init__(self, passkey: str):
34
- """Initialize a CCL device."""
35
- self._passkey = passkey
36
-
37
- @property
38
- def passkey(self) -> str:
39
- """Return the passkey."""
40
- return self._passkey
41
-
42
- @property
43
- def device_id(self) -> str | None:
44
- """Return the device ID."""
45
- try:
46
- self._device_id = self._mac_address.replace(":", "").lower()[-6:]
47
- except Exception: # pylint: disable=broad-exception-caught
48
- return None
49
- return self._device_id
50
-
51
- @property
52
- def name(self) -> str | None:
53
- """Return the display name."""
54
- if self._device_id is not None:
55
- return self._model + " - " + self._device_id
56
- return self._model
57
-
58
- @property
59
- def mac_address(self) -> str | None:
60
- """Return the MAC address."""
61
- return self._mac_address
62
-
63
- @property
64
- def model(self) -> str | None:
65
- """Return the model."""
66
- return self._model
67
-
68
- @property
69
- def fw_ver(self) -> str | None:
70
- """Return the firmware version."""
71
- return self._fw_ver
72
-
73
- @property
74
- def binary_sensors(self) -> dict[str, CCLSensor] | None:
75
- """Store binary sensor data under this device."""
76
- return self._binary_sensors
77
-
78
- @property
79
- def sensors(self) -> dict[str, CCLSensor] | None:
80
- """Store sensor data under this device."""
81
- return self._sensors
82
-
83
- def update_info(self, info: dict[str, None | str]) -> None:
84
- """Add or update device info."""
85
- self._mac_address = info.get("mac_address")
86
- self._model = info.get("model")
87
- self._fw_ver = info.get("fw_ver")
88
-
89
- def update_sensors(self, sensors: dict[str, None | str | int | float]) -> None:
90
- """Add or update all sensor values."""
91
- for key, value in sensors.items():
92
- if CCL_SENSORS.get(key).binary:
93
- if key not in self._binary_sensors:
94
- self._binary_sensors[key] = CCLSensor(key)
95
- self._new_sensors.append(self._binary_sensors[key])
96
- self._binary_sensors[key].value = value
97
- else:
98
- if key not in self._sensors:
99
- self._sensors[key] = CCLSensor(key)
100
- self._new_sensors.append(self._sensors[key])
101
- self._sensors[key].value = value
102
- self._publish_new_sensors()
103
- self._publish_updates()
104
- self._last_updated_time = time.monotonic()
105
- _LOGGER.debug("Sensors Updated: %s", self._last_updated_time)
106
-
107
- def register_update_cb(self, callback: Callable[[], None]) -> None:
108
- """Register callback, called when Sensor changes state."""
109
- self._update_callbacks.add(callback)
110
-
111
- def remove_update_cb(self, callback: Callable[[], None]) -> None:
112
- """Remove previously registered callback."""
113
- self._update_callbacks.discard(callback)
114
-
115
- def _publish_updates(self) -> None:
116
- """Schedule call all registered callbacks."""
117
- try:
118
- for callback in self._update_callbacks:
119
- callback()
120
- except Exception as err: # pylint: disable=broad-exception-caught
121
- _LOGGER.warning("Error while publishing sensor updates: %s", err)
122
-
123
- def register_new_binary_sensor_cb(self, callback: Callable[[], None]) -> None:
124
- """Register callback, called when Sensor changes state."""
125
- self._new_binary_sensor_callbacks.add(callback)
126
-
127
- def remove_new_binary_sensor_cb(self, callback: Callable[[], None]) -> None:
128
- """Remove previously registered callback."""
129
- self._new_binary_sensor_callbacks.discard(callback)
130
-
131
- def register_new_sensor_cb(self, callback: Callable[[], None]) -> None:
132
- """Register callback, called when Sensor changes state."""
133
- self._new_sensor_callbacks.add(callback)
134
-
135
- def remove_new_sensor_cb(self, callback: Callable[[], None]) -> None:
136
- """Remove previously registered callback."""
137
- self._new_sensor_callbacks.discard(callback)
138
-
139
- def _publish_new_sensors(self) -> None:
140
- """Schedule call all registered callbacks."""
141
- for sensor in self._new_sensors[:]:
142
- try:
143
- if sensor.binary:
144
- for callback in self._new_binary_sensor_callbacks:
145
- callback(sensor)
146
- else:
147
- for callback in self._new_sensor_callbacks:
148
- callback(sensor)
149
- self._new_sensors.remove(sensor)
150
- except Exception as err: # pylint: disable=broad-exception-caught
151
- _LOGGER.warning("Error while publishing new sensors: %s", err)
@@ -1,2 +0,0 @@
1
- aiohttp>3
2
- aiohttp_cors>=0.7.0
File without changes
File without changes
File without changes