ihcsdk 2.8.8__py3-none-any.whl → 2.8.11__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.
ihcsdk/__init__.py CHANGED
@@ -1 +1 @@
1
- """Init file for ihcsdk """
1
+ """Init file for ihcsdk."""
ihcsdk/ihcclient.py CHANGED
@@ -1,12 +1,13 @@
1
- """
2
- Implements the connection to the ihc controller
3
- """
1
+ """Implements the connection to the ihc controller."""
4
2
 
5
3
  # pylint: disable=bare-except
6
4
  import base64
7
5
  import datetime
8
6
  import io
7
+ import xml.etree.ElementTree as ET
9
8
  import zlib
9
+ from typing import Any, ClassVar, Literal
10
+
10
11
  from ihcsdk.ihcconnection import IHCConnection
11
12
  from ihcsdk.ihcsslconnection import IHCSSLConnection
12
13
 
@@ -14,17 +15,17 @@ IHCSTATE_READY = "text.ctrl.state.ready"
14
15
 
15
16
 
16
17
  class IHCSoapClient:
17
- """Implements a limited set of the soap request for the IHC controller"""
18
+ """Implements a limited set of the soap request for the IHC controller."""
18
19
 
19
- ihcns = {
20
+ ihcns: ClassVar[dict[str, str]] = {
20
21
  "SOAP-ENV": "http://schemas.xmlsoap.org/soap/envelope/",
21
22
  "ns1": "utcs",
22
23
  "ns2": "utcs.values",
23
24
  "ns3": "utcs.values",
24
25
  }
25
26
 
26
- def __init__(self, url: str):
27
- """Initialize the IIHCSoapClient with a url for the controller"""
27
+ def __init__(self, url: str) -> None:
28
+ """Initialize the IIHCSoapClient with a url for the controller."""
28
29
  self.url = url
29
30
  self.username = ""
30
31
  self.password = ""
@@ -33,10 +34,17 @@ class IHCSoapClient:
33
34
  else:
34
35
  self.connection = IHCConnection(url)
35
36
 
37
+ def close(self) -> None:
38
+ """Close the connection."""
39
+ self.connection.close()
40
+ self.connection = None
41
+
36
42
  def authenticate(self, username: str, password: str) -> bool:
37
- """Do an Authentricate request and save the cookie returned to be used
38
- on the following requests.
39
- Return True if the request was successfull
43
+ """
44
+ Do an Authentricate request.
45
+
46
+ And save the cookie returned to be used on the following requests.
47
+ Return True if the request was successfull.
40
48
  """
41
49
  self.username = username
42
50
  self.password = password
@@ -61,7 +69,7 @@ class IHCSoapClient:
61
69
  return False
62
70
 
63
71
  def get_state(self) -> str:
64
- """Get the controller state"""
72
+ """Get the controller state."""
65
73
  xdoc = self.connection.soap_action("/ws/ControllerService", "getState", "")
66
74
  if xdoc is not False:
67
75
  return xdoc.find(
@@ -69,18 +77,16 @@ class IHCSoapClient:
69
77
  ).text
70
78
  return False
71
79
 
72
- def wait_for_state_change(self, state: str, waitsec) -> str:
73
- """Wait for controller state change and return state"""
74
- payload = """<ns1:waitForControllerStateChange1
80
+ def wait_for_state_change(self, state: str, waitsec: int) -> str:
81
+ """Wait for controller state change and return state."""
82
+ payload = f"""<ns1:waitForControllerStateChange1
75
83
  xmlns:ns1=\"utcs\" xsi:type=\"ns1:WSControllerState\">
76
84
  <ns1:state xsi:type=\"xsd:string\">{state}</ns1:state>
77
85
  </ns1:waitForControllerStateChange1>
78
86
  <ns2:waitForControllerStateChange2
79
87
  xmlns:ns2=\"utcs\" xsi:type=\"xsd:int\">
80
- {wait}</ns2:waitForControllerStateChange2>
81
- """.format(
82
- state=state, wait=waitsec
83
- )
88
+ {waitsec}</ns2:waitForControllerStateChange2>
89
+ """
84
90
  xdoc = self.connection.soap_action(
85
91
  "/ws/ControllerService", "waitForControllerStateChange", payload
86
92
  )
@@ -92,9 +98,11 @@ class IHCSoapClient:
92
98
  return False
93
99
 
94
100
  def get_project(self) -> str:
95
- """Get the ihc project in single SOAP action.
96
- You should use the get_project_in_segments to get the project in multiple segments.
97
- This will stress the IHC controller less
101
+ """
102
+ Get the ihc project in single SOAP action.
103
+
104
+ You should use the get_project_in_segments to get the project in multiple
105
+ segments. This will stress the IHC controller less.
98
106
  """
99
107
  xdoc = self.connection.soap_action("/ws/ControllerService", "getIHCProject", "")
100
108
  if xdoc is not False:
@@ -109,18 +117,21 @@ class IHCSoapClient:
109
117
  )
110
118
  return False
111
119
 
112
- def get_project_in_segments(self, info=None) -> str:
113
- """Get the ihc project per segments.
114
- Param: info .. reuse existing project info. If not provided, the get_project_info() is called internally.
120
+ def get_project_in_segments(self, info: dict[str, Any] | None = None) -> str:
121
+ """
122
+ Get the ihc project per segments.
123
+
124
+ Param: info .. reuse existing project info.
125
+ If not provided, the get_project_info() is called internally.
115
126
  """
116
127
  if info is None:
117
128
  info = self.get_project_info()
118
129
  if info:
119
- projectMajor = info.get("projectMajorRevision", 0)
120
- projectMinor = info.get("projectMinorRevision", 0)
130
+ project_major = info.get("projectMajorRevision", 0)
131
+ project_minor = info.get("projectMinorRevision", 0)
121
132
  buffer = io.BytesIO()
122
133
  for s in range(self.get_project_number_of_segments()):
123
- segment = self.get_project_segment(s, projectMajor, projectMinor)
134
+ segment = self.get_project_segment(s, project_major, project_minor)
124
135
  if segment is False:
125
136
  return False
126
137
  buffer.write(segment)
@@ -129,8 +140,8 @@ class IHCSoapClient:
129
140
  )
130
141
  return False
131
142
 
132
- def get_project_info(self) -> dict:
133
- """Returns dictionary of project info items."""
143
+ def get_project_info(self) -> dict[str, Any]:
144
+ """Return dictionary of project info items."""
134
145
  xdoc = self.connection.soap_action(
135
146
  "/ws/ControllerService", "getProjectInfo", ""
136
147
  )
@@ -145,7 +156,7 @@ class IHCSoapClient:
145
156
  return False
146
157
 
147
158
  def get_project_number_of_segments(self) -> int:
148
- """Returns the number of segments needed to fetch the current ihc-project."""
159
+ """Return the number of segments needed to fetch the current ihc-project."""
149
160
  xdoc = self.connection.soap_action(
150
161
  "/ws/ControllerService", "getIHCProjectNumberOfSegments", ""
151
162
  )
@@ -158,20 +169,23 @@ class IHCSoapClient:
158
169
  )
159
170
  return False
160
171
 
161
- def get_project_segment(self, segment: int, projectMajor: int, projectMinor: int):
162
- """Returns a segment of the ihc-project with the given number.
163
- Returns null if the segment number increases above the number of segments available.
164
- The segments are offset from 0.
165
- The project-versions given as parameters are used to indentify the project that should be fetched.
166
- That is, to make sure that you suddenly don't get segments belonging to another project.
172
+ def get_project_segment(
173
+ self, segment: int, project_major: int, project_minor: int
174
+ ) -> bytes:
175
+ """
176
+ Return a segment of the ihc-project with the given number.
177
+
178
+ Returns null if the segment number increases above the number of segments
179
+ available. The segments are offset from 0.
180
+ The project-versions given as parameters are used to indentify the project that
181
+ should be fetched. That is, to make sure that you suddenly don't get segments
182
+ belonging to another project.
167
183
  """
168
- payload = """
184
+ payload = f"""
169
185
  <getIHCProjectSegment1 xmlns="utcs">{segment}</getIHCProjectSegment1>
170
- <getIHCProjectSegment2 xmlns="utcs">{major}</getIHCProjectSegment2>
171
- <getIHCProjectSegment3 xmlns="utcs">{minor}</getIHCProjectSegment3>
172
- """.format(
173
- segment=segment, major=projectMajor, minor=projectMinor
174
- )
186
+ <getIHCProjectSegment2 xmlns="utcs">{project_major}</getIHCProjectSegment2>
187
+ <getIHCProjectSegment3 xmlns="utcs">{project_minor}</getIHCProjectSegment3>
188
+ """
175
189
  xdoc = self.connection.soap_action(
176
190
  "/ws/ControllerService", "getIHCProjectSegment", payload
177
191
  )
@@ -182,29 +196,22 @@ class IHCSoapClient:
182
196
  ).text
183
197
  if not base64:
184
198
  return False
185
- compresseddata = base64.b64decode(base64data)
186
- return compresseddata
199
+ return base64.b64decode(base64data)
187
200
  return False
188
201
 
189
202
  def set_runtime_value_bool(self, resourceid: int, value: bool) -> bool:
190
- """Set a boolean runtime value"""
191
- if value:
192
- boolvalue = "true"
193
- else:
194
- boolvalue = "false"
195
-
196
- payload = """
203
+ """Set a boolean runtime value."""
204
+ boolvalue = "true" if value else "false"
205
+ payload = f"""
197
206
  <setResourceValue1 xmlns=\"utcs\"
198
207
  xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
199
208
  <value i:type=\"a:WSBooleanValue\" xmlns:a=\"utcs.values\">
200
- <a:value>{value}</a:value></value>
209
+ <a:value>{boolvalue}</a:value></value>
201
210
  <typeString/>
202
- <resourceID>{id}</resourceID>
211
+ <resourceID>{resourceid}</resourceID>
203
212
  <isValueRuntime>true</isValueRuntime>
204
213
  </setResourceValue1>
205
- """.format(
206
- id=resourceid, value=boolvalue
207
- )
214
+ """
208
215
  xdoc = self.connection.soap_action(
209
216
  "/ws/ResourceInteractionService", "setResourceValue", payload
210
217
  )
@@ -215,20 +222,18 @@ class IHCSoapClient:
215
222
  return result == "true"
216
223
  return False
217
224
 
218
- def set_runtime_value_int(self, resourceid: int, intvalue: int):
219
- """Set a integer runtime value"""
220
- payload = """
225
+ def set_runtime_value_int(self, resourceid: int, intvalue: int) -> bool:
226
+ """Set a integer runtime value."""
227
+ payload = f"""
221
228
  <setResourceValue1 xmlns=\"utcs\"
222
229
  xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
223
230
  <value i:type=\"a:WSIntegerValue\" xmlns:a=\"utcs.values\">
224
- <a:integer>{value}</a:integer></value>
231
+ <a:integer>{intvalue}</a:integer></value>
225
232
  <typeString/>
226
- <resourceID>{id}</resourceID>
233
+ <resourceID>{resourceid}</resourceID>
227
234
  <isValueRuntime>true</isValueRuntime>
228
235
  </setResourceValue1>
229
- """.format(
230
- id=resourceid, value=intvalue
231
- )
236
+ """
232
237
  xdoc = self.connection.soap_action(
233
238
  "/ws/ResourceInteractionService", "setResourceValue", payload
234
239
  )
@@ -239,21 +244,19 @@ class IHCSoapClient:
239
244
  return result == "true"
240
245
  return False
241
246
 
242
- def set_runtime_value_float(self, resourceid: int, floatvalue: float):
243
- """Set a flot runtime value"""
244
- payload = """
247
+ def set_runtime_value_float(self, resourceid: int, floatvalue: float) -> bool:
248
+ """Set a flot runtime value."""
249
+ payload = f"""
245
250
  <setResourceValue1 xmlns=\"utcs\"
246
251
  xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
247
252
  <value i:type=\"a:WSFloatingPointValue\" xmlns:a=\"utcs.values\">
248
- <a:floatingPointValue>{value}</a:floatingPointValue></value>
253
+ <a:floatingPointValue>{floatvalue}</a:floatingPointValue></value>
249
254
  <typeString/>
250
- <resourceID>{id}</resourceID>
255
+ <resourceID>{resourceid}</resourceID>
251
256
  <isValueRuntime>true</isValueRuntime>
252
257
  </setResourceValue1>
253
258
  </s:Body>
254
- """.format(
255
- id=resourceid, value=floatvalue
256
- )
259
+ """
257
260
  xdoc = self.connection.soap_action(
258
261
  "/ws/ResourceInteractionService", "setResourceValue", payload
259
262
  )
@@ -264,21 +267,19 @@ class IHCSoapClient:
264
267
  return result == "true"
265
268
  return False
266
269
 
267
- def set_runtime_value_timer(self, resourceid: int, timer: int):
268
- """Set a timer runtime value in milliseconds"""
269
- payload = """
270
+ def set_runtime_value_timer(self, resourceid: int, timer: int) -> bool:
271
+ """Set a timer runtime value in milliseconds."""
272
+ payload = f"""
270
273
  <setResourceValue1 xmlns=\"utcs\"
271
274
  xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
272
275
  <value i:type=\"a:WSTimerValue\" xmlns:a=\"utcs.values\">
273
- <a:milliseconds>{value}</a:milliseconds></value>
276
+ <a:milliseconds>{timer}</a:milliseconds></value>
274
277
  <typeString/>
275
- <resourceID>{id}</resourceID>
278
+ <resourceID>{resourceid}</resourceID>
276
279
  <isValueRuntime>true</isValueRuntime>
277
280
  </setResourceValue1>
278
281
  </s:Body>
279
- """.format(
280
- id=resourceid, value=timer
281
- )
282
+ """
282
283
  xdoc = self.connection.soap_action(
283
284
  "/ws/ResourceInteractionService", "setResourceValue", payload
284
285
  )
@@ -291,8 +292,8 @@ class IHCSoapClient:
291
292
 
292
293
  def set_runtime_value_time(
293
294
  self, resourceid: int, hours: int, minutes: int, seconds: int
294
- ):
295
- """Set a time runtime value in hours:minutes:seconds"""
295
+ ) -> bool:
296
+ """Set a time runtime value in hours:minutes:seconds."""
296
297
  payload = f"""
297
298
  <setResourceValue1 xmlns=\"utcs\"
298
299
  xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
@@ -317,15 +318,16 @@ class IHCSoapClient:
317
318
  return result == "true"
318
319
  return False
319
320
 
320
- def get_time(resource_value):
321
-
321
+ @staticmethod
322
+ def _get_time(resource_value: ET.Element) -> datetime.time:
322
323
  hours = int(resource_value.find("./ns2:hours", IHCSoapClient.ihcns).text)
323
324
  minutes = int(resource_value.find("./ns2:minutes", IHCSoapClient.ihcns).text)
324
325
  seconds = int(resource_value.find("./ns2:seconds", IHCSoapClient.ihcns).text)
325
326
 
326
327
  return datetime.time(hours, minutes, seconds)
327
328
 
328
- def get_datetime(resource_value):
329
+ @staticmethod
330
+ def _get_datetime(resource_value: ET.Element) -> datetime.datetime:
329
331
  year = int(resource_value.find("./ns1:year", IHCSoapClient.ihcns).text)
330
332
  month = int(
331
333
  resource_value.find("./ns1:monthWithJanuaryAsOne", IHCSoapClient.ihcns).text
@@ -334,18 +336,22 @@ class IHCSoapClient:
334
336
  hours = int(resource_value.find("./ns1:hours", IHCSoapClient.ihcns).text)
335
337
  minutes = int(resource_value.find("./ns1:minutes", IHCSoapClient.ihcns).text)
336
338
  seconds = int(resource_value.find("./ns1:seconds", IHCSoapClient.ihcns).text)
337
- return datetime.datetime(year, month, day, hours, minutes, seconds)
339
+ return datetime.datetime(year, month, day, hours, minutes, seconds) # noqa: DTZ001
338
340
 
339
- def get_date(resource_value):
341
+ @staticmethod
342
+ def _get_date(resource_value: ET.Element) -> datetime.datetime:
340
343
  year = int(resource_value.find("./ns2:year", IHCSoapClient.ihcns).text)
341
344
  if year == 0:
342
- year = datetime.datetime.today().year
345
+ year = datetime.datetime.today().year # noqa: DTZ002
343
346
  month = int(resource_value.find("./ns2:month", IHCSoapClient.ihcns).text)
344
347
  day = int(resource_value.find("./ns2:day", IHCSoapClient.ihcns).text)
345
- return datetime.datetime(year, month, day)
348
+ return datetime.datetime(year, month, day) # noqa: DTZ001
346
349
 
347
- def __get_value(resource_value):
348
- """Get a runtime value from the xml base on the type in the xml"""
350
+ @staticmethod
351
+ def __get_value(
352
+ resource_value: ET.Element,
353
+ ) -> bool | int | float | str | datetime.datetime | None:
354
+ """Get a runtime value from the xml base on the type in the xml."""
349
355
  if resource_value is None:
350
356
  return None
351
357
  valuetype = resource_value.attrib[
@@ -354,34 +360,50 @@ class IHCSoapClient:
354
360
  result = resource_value.text
355
361
  match valuetype:
356
362
  case "WSBooleanValue":
357
- result = resource_value.find("./ns2:value", IHCSoapClient.ihcns).text == "true"
363
+ result = (
364
+ resource_value.find("./ns2:value", IHCSoapClient.ihcns).text
365
+ == "true"
366
+ )
358
367
  case "WSIntegerValue":
359
- result = resource_value.find("./ns2:integer", IHCSoapClient.ihcns).text
368
+ result = int(
369
+ resource_value.find("./ns2:integer", IHCSoapClient.ihcns).text
370
+ )
360
371
  case "WSFloatingPointValue":
361
- result = round(float(resource_value.find("./ns2:floatingPointValue", IHCSoapClient.ihcns).text), 2)
372
+ result = round(
373
+ float(
374
+ resource_value.find(
375
+ "./ns2:floatingPointValue", IHCSoapClient.ihcns
376
+ ).text
377
+ ),
378
+ 2,
379
+ )
362
380
  case "WSEnumValue":
363
381
  result = resource_value.find("./ns2:enumName", IHCSoapClient.ihcns).text
364
382
  case "WSTimerValue":
365
- return int(resource_value.find("./ns2:milliseconds", IHCSoapClient.ihcns).text)
383
+ return int(
384
+ resource_value.find("./ns2:milliseconds", IHCSoapClient.ihcns).text
385
+ )
366
386
  case "WSTimeValue":
367
- result = IHCSoapClient.get_time(resource_value)
387
+ result = IHCSoapClient._get_time(resource_value)
368
388
  case "WSDate":
369
- result = IHCSoapClient.get_datetime(resource_value)
389
+ result = IHCSoapClient._get_datetime(resource_value)
370
390
  case "WSDateValue":
371
- result = IHCSoapClient.get_date(resource_value)
391
+ result = IHCSoapClient._get_date(resource_value)
372
392
  case "int":
373
393
  result = int(resource_value.text)
374
394
  return result
375
395
 
376
- def get_runtime_value(self, resourceid: int):
377
- """Get runtime value of specified resource it
396
+ def get_runtime_value(
397
+ self, resourceid: int
398
+ ) -> bool | int | float | str | datetime.datetime | None:
399
+ """
400
+ Get runtime value of specified resource id.
401
+
378
402
  The returned value will be boolean, integer or float
379
403
  Return None if resource cannot be found or on error
380
404
  """
381
- payload = """<getRuntimeValue1 xmlns="utcs">{id}</getRuntimeValue1>
382
- """.format(
383
- id=resourceid
384
- )
405
+ payload = f"""<getRuntimeValue1 xmlns="utcs">{resourceid}</getRuntimeValue1>
406
+ """
385
407
  xdoc = self.connection.soap_action(
386
408
  "/ws/ResourceInteractionService", "getResourceValue", payload
387
409
  )
@@ -392,14 +414,17 @@ class IHCSoapClient:
392
414
  )
393
415
  return IHCSoapClient.__get_value(value)
394
416
 
395
- def get_runtime_values(self, resourceids):
396
- """Get runtime values of specified resource ids
397
- Return None if resource cannot be found or on error
417
+ def get_runtime_values(
418
+ self, resourceids: list[int]
419
+ ) -> dict[int, Any] | Literal[False]:
398
420
  """
421
+ Get runtime values of specified resource ids.
399
422
 
423
+ Return None if resource cannot be found or on error
424
+ """
400
425
  idsarr = ""
401
426
  for ihcid in resourceids:
402
- idsarr += "<arrayItem>{id}</arrayItem>".format(id=ihcid)
427
+ idsarr += f"<arrayItem>{ihcid}</arrayItem>"
403
428
  payload = '<getRuntimeValues1 xmlns="utcs">' + idsarr + "</getRuntimeValues1>"
404
429
  xdoc = self.connection.soap_action(
405
430
  "/ws/ResourceInteractionService", "getResourceValues", payload
@@ -414,17 +439,19 @@ class IHCSoapClient:
414
439
  ihcid = item.find("ns1:resourceID", IHCSoapClient.ihcns)
415
440
  if ihcid is None:
416
441
  continue
417
- resourceValue = item.find("./ns1:value", IHCSoapClient.ihcns)
418
- itemValue = IHCSoapClient.__get_value(resourceValue)
419
- if itemValue is not None:
420
- changes[int(ihcid.text)] = itemValue
442
+ resource_value = item.find("./ns1:value", IHCSoapClient.ihcns)
443
+ item_value = IHCSoapClient.__get_value(resource_value)
444
+ if item_value is not None:
445
+ changes[int(ihcid.text)] = item_value
421
446
  return changes
422
447
 
423
- def cycle_bool_value(self, resourceid: int):
424
- """Turn a booelan resource On and back Off
448
+ def cycle_bool_value(self, resourceid: int) -> bool | None:
449
+ """
450
+ Turn a booelan resource On and back Off.
451
+
425
452
  Return None if resource cannot be found or on error
426
453
  """
427
- setBool = (
454
+ set_bool = (
428
455
  "<arrayItem>"
429
456
  '<value xsi:type="ns1:WSBooleanValue">'
430
457
  "<ns1:value>{value}</ns1:value>"
@@ -436,8 +463,8 @@ class IHCSoapClient:
436
463
  )
437
464
  payload = (
438
465
  '<setResourceValues1 xmlns="utcs" xmlns:ns1="utcs.values">'
439
- + setBool.format(value="true", id=resourceid)
440
- + setBool.format(value="false", id=resourceid)
466
+ + set_bool.format(value="true", id=resourceid)
467
+ + set_bool.format(value="false", id=resourceid)
441
468
  + "</setResourceValues1>"
442
469
  )
443
470
  xdoc = self.connection.soap_action(
@@ -447,53 +474,53 @@ class IHCSoapClient:
447
474
  return None
448
475
  return True
449
476
 
450
- def enable_runtime_notification(self, resourceid: int):
451
- """Enable notification for specified resource id"""
477
+ def enable_runtime_notification(self, resourceid: int) -> bool:
478
+ """Enable notification for specified resource id."""
452
479
  return self.enable_runtime_notifications([resourceid])
453
480
 
454
- def enable_runtime_notifications(self, resourceids):
455
- """Enable notification for specified resource ids"""
481
+ def enable_runtime_notifications(self, resourceids: list[int]) -> bool:
482
+ """Enable notification for specified resource ids."""
456
483
  idsarr = ""
457
484
  for ihcid in resourceids:
458
- idsarr += "<a:arrayItem>{id}</a:arrayItem>".format(id=ihcid)
485
+ idsarr += f"<a:arrayItem>{ihcid}</a:arrayItem>"
459
486
 
460
- payload = """<enableRuntimeValueNotifications1 xmlns=\"utcs\"
487
+ payload = f"""<enableRuntimeValueNotifications1 xmlns=\"utcs\"
461
488
  xmlns:a=\"http://www.w3.org/2001/XMLSchema\"
462
489
  xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
463
- {arr}
490
+ {idsarr}
464
491
  </enableRuntimeValueNotifications1>
465
- """.format(
466
- arr=idsarr
467
- )
492
+ """
468
493
  xdoc = self.connection.soap_action(
469
494
  "/ws/ResourceInteractionService", "enableRuntimeValueNotifications", payload
470
495
  )
471
496
  return xdoc is not False
472
497
 
473
- def wait_for_resource_value_changes(self, wait: int = 10):
498
+ def wait_for_resource_value_changes(
499
+ self, wait: int = 10
500
+ ) -> dict[int, str] | Literal[False]:
474
501
  """
475
- Long polling for changes and return a dictionary with resource:value
476
- for changes (Only last change)
502
+ Long polling for changes.
503
+
504
+ And return a dictionary with resource:value for changes (Only last change)
477
505
  """
478
506
  change_list = self.wait_for_resource_value_change_list(wait)
479
507
  if change_list is False:
480
508
  return False
481
- last_changes = {}
482
- for id, value in change_list:
483
- last_changes[id] = value
484
- return last_changes
509
+ return dict(change_list)
485
510
 
486
- def wait_for_resource_value_change_list(self, wait: int = 10):
511
+ def wait_for_resource_value_change_list(
512
+ self, wait: int = 10
513
+ ) -> list[(int, Any)] | Literal[False]:
487
514
  """
488
- Long polling for changes and return a resource id dictionary with a list of all changes since last poll.
515
+ Long polling for changes.
516
+
517
+ And return a resource id dictionary with a list of all changes since last poll.
489
518
  Return a list of tuples with the id,value
490
519
  """
491
520
  changes = []
492
- payload = """<waitForResourceValueChanges1
493
- xmlns=\"utcs\">{timeout}</waitForResourceValueChanges1>
494
- """.format(
495
- timeout=wait
496
- )
521
+ payload = f"""<waitForResourceValueChanges1
522
+ xmlns=\"utcs\">{wait}</waitForResourceValueChanges1>
523
+ """
497
524
  xdoc = self.connection.soap_action(
498
525
  "/ws/ResourceInteractionService", "getResourceValue", payload
499
526
  )
@@ -514,14 +541,12 @@ class IHCSoapClient:
514
541
  changes.append((int(ihcid.text), value))
515
542
  return changes
516
543
 
517
- def get_user_log(self, language="da"):
518
- """Get the controller state"""
519
- payload = """<getUserLog1 xmlns="utcs" />
544
+ def get_user_log(self, language: str = "da") -> str | Literal[False]:
545
+ """Get the controller state."""
546
+ payload = f"""<getUserLog1 xmlns="utcs" />
520
547
  <getUserLog2 xmlns="utcs">0</getUserLog2>
521
548
  <getUserLog3 xmlns="utcs">{language}</getUserLog3>
522
- """.format(
523
- language=language
524
- )
549
+ """
525
550
  xdoc = self.connection.soap_action(
526
551
  "/ws/ConfigurationService", "getUserLog", payload
527
552
  )
@@ -534,50 +559,47 @@ class IHCSoapClient:
534
559
  return base64.b64decode(base64data).decode("UTF-8")
535
560
  return False
536
561
 
537
- def clear_user_log(self):
538
- """Clear the user log in the controller"""
562
+ def clear_user_log(self) -> None:
563
+ """Clear the user log in the controller."""
539
564
  self.connection.soap_action("/ws/ConfigurationService", "clearUserLog", "")
540
- return
541
565
 
542
- def get_system_info(self):
543
- """Get controller system info"""
566
+ def get_system_info(self) -> dict[str, str] | bool:
567
+ """Get controller system info."""
544
568
  xdoc = self.connection.soap_action(
545
569
  "/ws/ConfigurationService", "getSystemInfo", ""
546
570
  )
547
571
  if xdoc is False:
548
572
  return False
549
- info = {
550
- "uptime": IHCSoapClient.__extract_sysinfo(xdoc, "uptime"),
551
- "realtimeclock": IHCSoapClient.__extract_sysinfo(xdoc, "realtimeclock"),
552
- "serial_number": IHCSoapClient.__extract_sysinfo(xdoc, "serialNumber"),
553
- "production_date": IHCSoapClient.__extract_sysinfo(xdoc, "productionDate"),
554
- "brand": IHCSoapClient.__extract_sysinfo(xdoc, "brand"),
555
- "version": IHCSoapClient.__extract_sysinfo(xdoc, "version"),
556
- "hw_revision": IHCSoapClient.__extract_sysinfo(xdoc, "hwRevision"),
557
- "sw_date": IHCSoapClient.__extract_sysinfo(xdoc, "swDate"),
558
- "dataline_version": IHCSoapClient.__extract_sysinfo(
559
- xdoc, "datalineVersion"
560
- ),
561
- "rf_module_software_version": IHCSoapClient.__extract_sysinfo(
573
+ return {
574
+ "uptime": IHCSoapClient._extract_sysinfo(xdoc, "uptime"),
575
+ "realtimeclock": IHCSoapClient._extract_sysinfo(xdoc, "realtimeclock"),
576
+ "serial_number": IHCSoapClient._extract_sysinfo(xdoc, "serialNumber"),
577
+ "production_date": IHCSoapClient._extract_sysinfo(xdoc, "productionDate"),
578
+ "brand": IHCSoapClient._extract_sysinfo(xdoc, "brand"),
579
+ "version": IHCSoapClient._extract_sysinfo(xdoc, "version"),
580
+ "hw_revision": IHCSoapClient._extract_sysinfo(xdoc, "hwRevision"),
581
+ "sw_date": IHCSoapClient._extract_sysinfo(xdoc, "swDate"),
582
+ "dataline_version": IHCSoapClient._extract_sysinfo(xdoc, "datalineVersion"),
583
+ "rf_module_software_version": IHCSoapClient._extract_sysinfo(
562
584
  xdoc, "rfModuleSoftwareVersion"
563
585
  ),
564
- "rf_module_serial_number": IHCSoapClient.__extract_sysinfo(
586
+ "rf_module_serial_number": IHCSoapClient._extract_sysinfo(
565
587
  xdoc, "rfModuleSerialNumber"
566
588
  ),
567
- "application_is_without_viewer": IHCSoapClient.__extract_sysinfo(
589
+ "application_is_without_viewer": IHCSoapClient._extract_sysinfo(
568
590
  xdoc, "applicationIsWithoutViewer"
569
591
  ),
570
- "sms_modem_software_version": IHCSoapClient.__extract_sysinfo(
592
+ "sms_modem_software_version": IHCSoapClient._extract_sysinfo(
571
593
  xdoc, "smsModemSoftwareVersion"
572
594
  ),
573
- "led_dimmer_software_version": IHCSoapClient.__extract_sysinfo(
595
+ "led_dimmer_software_version": IHCSoapClient._extract_sysinfo(
574
596
  xdoc, "ledDimmerSoftwareVersion"
575
597
  ),
576
598
  }
577
- return info
578
599
 
579
- def __extract_sysinfo(xdoc, param) -> str:
580
- """Internal function to extrach a parameter from system info"""
600
+ @staticmethod
601
+ def _extract_sysinfo(xdoc: ET.Element, param: str) -> str:
602
+ """Extract a parameter from system info."""
581
603
  element = xdoc.find(
582
604
  f"./SOAP-ENV:Body/ns1:getSystemInfo1/ns1:{param}", IHCSoapClient.ihcns
583
605
  )
ihcsdk/ihcconnection.py CHANGED
@@ -1,19 +1,21 @@
1
- """Implements soap reqeust using the "requests" module"""
1
+ """Implements soap reqeust using the "requests" module."""
2
2
 
3
- # pylint: disable=too-few-public-methods
4
3
  import logging
5
- import requests
6
- import xml.etree.ElementTree
7
-
4
+ import time
5
+ import xml.etree.ElementTree as ET
6
+ from http import HTTPStatus
7
+ from typing import Literal
8
8
  from urllib.parse import urlparse
9
- from urllib3.util import Retry
9
+
10
+ import requests
10
11
  from requests.adapters import HTTPAdapter
12
+ from urllib3.util import Retry
11
13
 
12
14
  _LOGGER = logging.getLogger(__name__)
13
15
 
14
16
 
15
- class IHCConnection(object):
16
- """Implements a http connection to the controller"""
17
+ class IHCConnection:
18
+ """Implements a http connection to the controller."""
17
19
 
18
20
  soapenvelope = """<?xml version=\"1.0\" encoding=\"UTF-8\"?>
19
21
  <s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"
@@ -21,8 +23,8 @@ class IHCConnection(object):
21
23
  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">
22
24
  <s:Body>{body}</s:Body></s:Envelope>"""
23
25
 
24
- def __init__(self, url: str):
25
- """Initialize the IHCConnection with a url for the controller"""
26
+ def __init__(self, url: str) -> None:
27
+ """Initialize the IHCConnection with a url for the controller."""
26
28
  self.url = url
27
29
  self.verify = False
28
30
  self.last_exception = None
@@ -35,11 +37,23 @@ class IHCConnection(object):
35
37
  allowed_methods={"POST"},
36
38
  )
37
39
  self.session.mount("http://", HTTPAdapter(max_retries=self.retries))
40
+ # default minimum time between calls in seconds (0 will not rate limit)
41
+ self.min_interval: float = 0.0
42
+ self.last_call_time: float = 0
43
+ self.logtiming = False
44
+
45
+ def close(self) -> None:
46
+ """Close the connection."""
47
+ self.session.close()
48
+ self.session = None
38
49
 
39
- def cert_verify(self):
50
+ def cert_verify(self) -> str | None:
51
+ """Validate the certificate and return the cert file."""
40
52
  return None
41
53
 
42
- def soap_action(self, service, action, payloadbody):
54
+ def soap_action(
55
+ self, service: str, action: str, payloadbody: str
56
+ ) -> ET.Element | Literal[False]:
43
57
  """Do a soap request."""
44
58
  payload = self.soapenvelope.format(body=payloadbody).encode("utf-8")
45
59
  headers = {
@@ -50,6 +64,7 @@ class IHCConnection(object):
50
64
  "SOAPAction": action,
51
65
  }
52
66
  try:
67
+ self.rate_limit()
53
68
  _LOGGER.debug("soap payload %s", payload)
54
69
  self.last_exception = None
55
70
  response = self.session.post(
@@ -59,19 +74,34 @@ class IHCConnection(object):
59
74
  verify=self.cert_verify(),
60
75
  )
61
76
  _LOGGER.debug("soap request response status %d", response.status_code)
62
- if response.status_code != 200:
77
+ if response.status_code != HTTPStatus.OK:
63
78
  self.last_response = response
64
79
  return False
65
80
  _LOGGER.debug("soap request response %s", response.text)
66
- xdoc = xml.etree.ElementTree.fromstring(response.text)
81
+ xdoc = ET.fromstring(response.text) # noqa: S314
67
82
  if xdoc is None:
68
83
  return False
69
- return xdoc
70
84
  except requests.exceptions.RequestException as exp:
71
- _LOGGER.error("soap request exception %s", exp)
85
+ _LOGGER.exception("soap request exception")
72
86
  self.last_exception = exp
73
- except xml.etree.ElementTree.ParseError as exp:
74
- _LOGGER.error("soap request xml parse error %s", exp)
87
+ except ET.ParseError as exp:
88
+ _LOGGER.exception("soap request xml parse erro")
75
89
  self.last_exception = exp
76
90
  self.last_response = response
91
+ else:
92
+ return xdoc
77
93
  return False
94
+
95
+ def rate_limit(self) -> None:
96
+ """Rate limit the calls to this function."""
97
+ current_time: float = time.time()
98
+ time_since_last_call: float = current_time - self.last_call_time
99
+ if self.logtiming:
100
+ _LOGGER.warning("time since last call %f sec", time_since_last_call)
101
+ # If not enough time has passed, sleep for the remaining time
102
+ if time_since_last_call < self.min_interval:
103
+ sleep_time: float = self.min_interval - time_since_last_call
104
+ _LOGGER.debug("Ratelimiting for %f sec", sleep_time)
105
+ time.sleep(sleep_time)
106
+ # Update the last call time and call the function
107
+ self.last_call_time = time.time()
ihcsdk/ihccontroller.py CHANGED
@@ -1,28 +1,36 @@
1
1
  """
2
- Wraps the ihcclient in a more user friendly interface to handle lost connection
2
+ Wraps the ihcclient in a more user friendly interface to handle lost connection.
3
+
3
4
  Notify thread to handle change notifications
4
5
  """
5
6
 
6
7
  # pylint: disable=invalid-name, bare-except, too-many-instance-attributes
7
- from datetime import datetime, timedelta
8
8
  import logging
9
- import requests
10
9
  import threading
11
10
  import time
12
- from ihcsdk.ihcclient import IHCSoapClient, IHCSTATE_READY
11
+ from collections.abc import Callable
12
+ from datetime import datetime, timedelta
13
+ from http import HTTPStatus
14
+ from typing import Any, Literal
15
+
16
+ import requests
17
+
18
+ from ihcsdk.ihcclient import IHCSTATE_READY, IHCSoapClient
13
19
 
14
20
  _LOGGER = logging.getLogger(__name__)
15
21
 
16
22
 
17
23
  class IHCController:
18
24
  """
19
- Implements the notification thread and
25
+ Implements the notification thread.
26
+
20
27
  will re-authenticate if needed.
21
28
  """
22
29
 
23
30
  _mutex = threading.Lock()
24
31
 
25
- def __init__(self, url: str, username: str, password: str):
32
+ def __init__(self, url: str, username: str, password: str) -> None:
33
+ """Initialize the IHC controller with connection data."""
26
34
  self.client = IHCSoapClient(url)
27
35
  self.reauthenticatetimeout = 30
28
36
  self.retryinterval = 10
@@ -35,26 +43,26 @@ class IHCController:
35
43
  self._newnotifyids = []
36
44
  self._project = None
37
45
 
46
+ @staticmethod
38
47
  def is_ihc_controller(url: str) -> bool:
39
48
  """Will return True if the url respods like an IHC controller."""
40
49
  try:
41
50
  client = IHCSoapClient(url)
42
51
  response = client.connection.session.get(
43
- f"{url}/wsdl/controller.wsdl", verify=client.connection.cert_verify()
52
+ f"{url}/wsdl/controller.wsdl", verify=False
44
53
  )
45
- if response.status_code != 200:
54
+ client.close()
55
+ if response.status_code != HTTPStatus.OK:
46
56
  return False
47
57
  if not response.headers["content-type"].startswith("text/xml"):
48
58
  return False
49
- if response.text.find("getIHCProject") < 0:
50
- return False
51
- return True
59
+ return not response.text.find("getIHCProject") < 0
52
60
  except requests.exceptions.RequestException as exp:
53
61
  _LOGGER.warning("is_ihc_controller %s", exp)
54
62
  return False
55
63
 
56
64
  def authenticate(self) -> bool:
57
- """Authenticate and enable the registered notifications"""
65
+ """Authenticate and enable the registered notifications."""
58
66
  with IHCController._mutex:
59
67
  _LOGGER.debug("Authenticating login on ihc controller")
60
68
  if not self.client.authenticate(self._username, self._password):
@@ -65,30 +73,34 @@ class IHCController:
65
73
  self.client.enable_runtime_notifications(self._ihcevents.keys())
66
74
  return True
67
75
 
68
- def disconnect(self):
69
- """Disconnect by stopping the notification thread
70
- TODO call disconnect on ihcclient
71
- """
76
+ def disconnect(self) -> None:
77
+ """Disconnect by stopping the notification thread. And closing the client."""
72
78
  self._notifyrunning = False
73
-
74
- def get_runtime_value(self, ihcid: int):
75
- """Get runtime value with re-authenticate if needed"""
79
+ # wait for notify thread to finish
80
+ while self._notifythread.is_alive():
81
+ time.sleep(0.1) # Optional sleep to prevent busy waiting
82
+ self.client.close()
83
+
84
+ def get_runtime_value(
85
+ self, ihcid: int
86
+ ) -> bool | int | float | str | datetime | None:
87
+ """Get runtime value with re-authenticate if needed."""
76
88
  value = self.client.get_runtime_value(ihcid)
77
89
  if value is not None:
78
90
  return value
79
91
  self.re_authenticate()
80
92
  return self.client.get_runtime_value(ihcid)
81
93
 
82
- def get_runtime_values(self, ihcids):
83
- """Get runtime value with re-authenticate if needed"""
94
+ def get_runtime_values(self, ihcids: list[int]) -> dict[int, Any] | Literal[False]:
95
+ """Get runtime value with re-authenticate if needed."""
84
96
  value = self.client.get_runtime_values(ihcids)
85
97
  if value is not None:
86
98
  return value
87
99
  self.re_authenticate()
88
100
  return self.client.get_runtime_values(ihcids)
89
101
 
90
- def cycle_bool_value(self, resourceid: int):
91
- """Turn a booelan resource On and back Off"""
102
+ def cycle_bool_value(self, resourceid: int) -> bool | None:
103
+ """Turn a booelan resource On and back Off."""
92
104
  value = self.client.cycle_bool_value(resourceid)
93
105
  if value is not None:
94
106
  return value
@@ -96,28 +108,28 @@ class IHCController:
96
108
  return self.client.cycle_bool_value(resourceid)
97
109
 
98
110
  def set_runtime_value_bool(self, ihcid: int, value: bool) -> bool:
99
- """Set bool runtime value with re-authenticate if needed"""
111
+ """Set bool runtime value with re-authenticate if needed."""
100
112
  if self.client.set_runtime_value_bool(ihcid, value):
101
113
  return True
102
114
  self.re_authenticate()
103
115
  return self.client.set_runtime_value_bool(ihcid, value)
104
116
 
105
117
  def set_runtime_value_int(self, ihcid: int, value: int) -> bool:
106
- """Set integer runtime value with re-authenticate if needed"""
118
+ """Set integer runtime value with re-authenticate if needed."""
107
119
  if self.client.set_runtime_value_int(ihcid, value):
108
120
  return True
109
121
  self.re_authenticate()
110
122
  return self.client.set_runtime_value_int(ihcid, value)
111
123
 
112
124
  def set_runtime_value_float(self, ihcid: int, value: float) -> bool:
113
- """Set float runtime value with re-authenticate if needed"""
125
+ """Set float runtime value with re-authenticate if needed."""
114
126
  if self.client.set_runtime_value_float(ihcid, value):
115
127
  return True
116
128
  self.re_authenticate()
117
129
  return self.client.set_runtime_value_float(ihcid, value)
118
130
 
119
131
  def set_runtime_value_timer(self, ihcid: int, value: int) -> bool:
120
- """Set timer runtime value with re-authenticate if needed"""
132
+ """Set timer runtime value with re-authenticate if needed."""
121
133
  if self.client.set_runtime_value_timer(ihcid, value):
122
134
  return True
123
135
  self.re_authenticate()
@@ -126,14 +138,14 @@ class IHCController:
126
138
  def set_runtime_value_time(
127
139
  self, ihcid: int, hours: int, minutes: int, seconds: int
128
140
  ) -> bool:
129
- """Set time runtime value with re-authenticate if needed"""
141
+ """Set time runtime value with re-authenticate if needed."""
130
142
  if self.client.set_runtime_value_time(ihcid, hours, minutes):
131
143
  return True
132
144
  self.re_authenticate()
133
145
  return self.client.set_runtime_value_time(ihcid, hours, minutes, seconds)
134
146
 
135
147
  def get_project(self, insegments: bool = True) -> str:
136
- """Get the ihc project and make sure controller is ready before"""
148
+ """Get the ihc project and make sure controller is ready before."""
137
149
  with IHCController._mutex:
138
150
  if self._project is None:
139
151
  if self.client.get_state() != IHCSTATE_READY:
@@ -146,8 +158,15 @@ class IHCController:
146
158
  self._project = self.client.get_project()
147
159
  return self._project
148
160
 
149
- def add_notify_event(self, resourceid: int, callback, delayed=False):
150
- """Add a notify callback for a specified resource id
161
+ def add_notify_event(
162
+ self,
163
+ resourceid: int,
164
+ callback: Callable[[int, bool | float | str | datetime], None],
165
+ delayed: bool = False,
166
+ ) -> bool:
167
+ """
168
+ Add a notify callback for a specified resource id.
169
+
151
170
  If delayed is set to true the enable request will be send from the
152
171
  notofication thread
153
172
  """
@@ -158,17 +177,16 @@ class IHCController:
158
177
  self._ihcevents[resourceid] = [callback]
159
178
  if delayed:
160
179
  self._newnotifyids.append(resourceid)
161
- else:
162
- if not self.client.enable_runtime_notification(resourceid):
163
- return False
180
+ elif not self.client.enable_runtime_notification(resourceid):
181
+ return False
164
182
  if not self._notifyrunning:
165
183
  self._notifyrunning = True
166
184
  self._notifythread.start()
167
185
 
168
186
  return True
169
187
 
170
- def _notify_fn(self):
171
- """The notify thread function."""
188
+ def _notify_fn(self) -> None:
189
+ """Notify thread function."""
172
190
  _LOGGER.debug("Starting notify thread")
173
191
  while self._notifyrunning:
174
192
  try:
@@ -180,7 +198,7 @@ class IHCController:
180
198
 
181
199
  changes = self.client.wait_for_resource_value_change_list()
182
200
  if changes is False:
183
- self.re_authenticate(True)
201
+ self.re_authenticate(notify=True)
184
202
  continue
185
203
  for ihcid, value in changes:
186
204
  if ihcid in self._ihcevents:
@@ -191,32 +209,33 @@ class IHCController:
191
209
  ):
192
210
  callback(ihcid, value)
193
211
  self._ihcvalues[ihcid] = value
194
- except Exception as exp:
195
- _LOGGER.error("Exception in notify thread %s", exp)
196
- self.re_authenticate(True)
212
+ except Exception:
213
+ _LOGGER.exception("Exception in notify thread")
214
+ self.re_authenticate(notify=True)
197
215
 
198
216
  def re_authenticate(self, notify: bool = False) -> bool:
199
- """Authenticate again after failure.
217
+ """
218
+ Authenticate again after failure.
219
+
200
220
  Keep trying with 10 sec interval. If called from the notify thread
201
221
  we will not have a timeout, but will end if the notify thread has
202
222
  been cancled.
203
223
  Will return True if authentication was successful.
204
224
  """
205
- timeout = datetime.now() + timedelta(seconds=self.reauthenticatetimeout)
225
+ timeout = datetime.now() + timedelta(seconds=self.reauthenticatetimeout) # noqa: DTZ005
206
226
  while True:
207
227
  _LOGGER.debug("Reauthenticating login on ihc controller")
208
228
  if self.authenticate():
209
229
  return True
210
230
  _LOGGER.debug(
211
- "Authenticate failed - Reauthenticating login on ihc controller in 10 sec"
231
+ "Authenticate failed, reauthenticating login on ihc controller in 10s"
212
232
  )
213
233
 
214
234
  # if called from the notify and notify a cancled we do not want to retry
215
235
  if notify:
216
236
  if not self._notifyrunning:
217
237
  return False
218
- else:
219
- if timeout and datetime.now() > timeout:
220
- return False
238
+ elif timeout and datetime.now() > timeout: # noqa: DTZ005
239
+ return False
221
240
  # wait before we try to authenticate again
222
241
  time.sleep(self.retryinterval)
@@ -1,50 +1,50 @@
1
- """Implements soap reqeust using the "requests" module"""
1
+ """Implements soap reqeust using the "requests" module."""
2
2
 
3
3
  # pylint: disable=too-few-public-methods
4
4
  import os
5
- import requests
6
5
 
7
- from cryptography.x509 import load_pem_x509_certificate
6
+ import requests
8
7
  from cryptography.hazmat.backends import default_backend
9
8
  from cryptography.hazmat.primitives import hashes
9
+ from cryptography.x509 import load_pem_x509_certificate
10
10
  from requests.packages.urllib3.util.ssl_ import create_urllib3_context
11
11
 
12
12
  from ihcsdk.ihcconnection import IHCConnection
13
13
 
14
14
 
15
15
  class IHCSSLConnection(IHCConnection):
16
- """Implements a https connection to the controller"""
16
+ """Implements a https connection to the controller."""
17
17
 
18
- def __init__(self, url: str):
19
- """Initialize the IHCSSLConnection with a url for the controller"""
20
- super(IHCSSLConnection, self).__init__(url)
18
+ def __init__(self, url: str) -> None:
19
+ """Initialize the IHCSSLConnection with a url for the controller."""
20
+ super().__init__(url)
21
21
  self.cert_file = os.path.dirname(__file__) + "/certs/ihc3.crt"
22
22
  self.session.mount(
23
23
  "https://",
24
24
  CertAdapter(self.get_fingerprint_from_cert(), max_retries=self.retries),
25
25
  )
26
26
 
27
- def get_fingerprint_from_cert(self):
28
- """Get the fingerprint from the certificate"""
27
+ def get_fingerprint_from_cert(self) -> str:
28
+ """Get the fingerprint from the certificate."""
29
29
  pem = open(self.cert_file, "rb").read()
30
30
  cert = load_pem_x509_certificate(pem, default_backend())
31
31
  f = cert.fingerprint(hashes.SHA1())
32
32
  return "".join("{:02x}".format(x) for x in f)
33
33
 
34
- def cert_verify(self):
34
+ def cert_verify(self) -> str:
35
35
  return self.cert_file
36
36
 
37
37
 
38
38
  class CertAdapter(requests.adapters.HTTPAdapter):
39
- """A adapter for a specific certificate"""
39
+ """A adapter for a specific certificate."""
40
40
 
41
- def __init__(self, fingerprint, **kwargs):
42
- """Constructor. Store the fingerprint for use when creating the poolmanager."""
41
+ def __init__(self, fingerprint, **kwargs) -> None:
42
+ """Store the fingerprint for use when creating the poolmanager."""
43
43
  self.fingerprint = fingerprint
44
- super(CertAdapter, self).__init__(**kwargs)
44
+ super().__init__(**kwargs)
45
45
 
46
46
  def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs):
47
- """Create a custom poolmanager"""
47
+ """Create a custom poolmanager."""
48
48
  CIPHERS = "DEFAULT:!DH"
49
49
  context = create_urllib3_context(ciphers=CIPHERS)
50
50
  pool_kwargs["ssl_context"] = context
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: ihcsdk
3
- Version: 2.8.8
3
+ Version: 2.8.11
4
4
  Summary: IHC Python SDK
5
5
  Home-page: https://github.com/dingusdk/PythonIhcSdk
6
6
  Author: Jens Nielsen
@@ -8,5 +8,12 @@ License: GPL-3.0
8
8
  License-File: license.txt
9
9
  Requires-Dist: requests
10
10
  Requires-Dist: cryptography
11
+ Dynamic: author
12
+ Dynamic: description
13
+ Dynamic: home-page
14
+ Dynamic: license
15
+ Dynamic: license-file
16
+ Dynamic: requires-dist
17
+ Dynamic: summary
11
18
 
12
19
  SDK for connection to the LK IHC Controller. Made for interfacing to home assistant
@@ -0,0 +1,12 @@
1
+ ihcsdk/__init__.py,sha256=IFv4tTt5vulfQlTqcok2IJA8ms3rE7XR7WIsRM2az9g,28
2
+ ihcsdk/ihcclient.py,sha256=NyTKFaF_jz5x1_fz1bHna22ke7KRm4DukMqn3aE70nM,24970
3
+ ihcsdk/ihcconnection.py,sha256=ISruHcCqusn5KCVSBwE8LNJS-v0aIw89WPCxAQuIk4w,4133
4
+ ihcsdk/ihccontroller.py,sha256=wb-7y5J0arNrlCMi35I6CLYwtKgcJvLNmzaABHqW3Aw,9767
5
+ ihcsdk/ihcsslconnection.py,sha256=OjGo8eJGHFnhVc9nz0Krf6mCCVrOOWj9BpTDOTtxKqk,2063
6
+ ihcsdk/certs/ihc.crt,sha256=VYY_DiHrctlXBTNXGdJ2FN4TYuVnwnpVZ1i8t2_0cec,1002
7
+ ihcsdk/certs/ihc3.crt,sha256=Ka2L8zQ06A76W6fZc0ckScdrj1Cn-mhgqdhc61-cCrk,1398
8
+ ihcsdk-2.8.11.dist-info/licenses/license.txt,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
9
+ ihcsdk-2.8.11.dist-info/METADATA,sha256=qUKqMaLYOBUsmAhn7jIvlGydjIqFz6Ohu_63qUiaG9E,482
10
+ ihcsdk-2.8.11.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
11
+ ihcsdk-2.8.11.dist-info/top_level.txt,sha256=QgKE7TWblC-uXe5-7MVO5JtzvrPiMoVmGgKKsVR43hU,7
12
+ ihcsdk-2.8.11.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,12 +0,0 @@
1
- ihcsdk/__init__.py,sha256=ocAoa28ZKxGgMlDEHWEsd7n00OtGB0R9N7XQfRlih9Q,28
2
- ihcsdk/ihcclient.py,sha256=LzcO0ZfIqfdQxF3TzoIfo2jVBU2u3QoZeZA4Fj3psqc,24302
3
- ihcsdk/ihcconnection.py,sha256=qCFhU4cAWYnqgiXWF-xjUZj1TMBZ83caDj8jEE1qaoc,2889
4
- ihcsdk/ihccontroller.py,sha256=9D97RX8DrFMLMxDBcIrhoYY-QEeKNjbVv_PiRW0wNyU,9180
5
- ihcsdk/ihcsslconnection.py,sha256=q7gDchCN2_SXItp2T3dMvlXmHC7zVO5M0bqGnepiKiU,2079
6
- ihcsdk/certs/ihc.crt,sha256=VYY_DiHrctlXBTNXGdJ2FN4TYuVnwnpVZ1i8t2_0cec,1002
7
- ihcsdk/certs/ihc3.crt,sha256=Ka2L8zQ06A76W6fZc0ckScdrj1Cn-mhgqdhc61-cCrk,1398
8
- ihcsdk-2.8.8.dist-info/METADATA,sha256=I2qpZJjWGY0Yad46vDMjNBljsRURFsopM0Ep8vgrn24,339
9
- ihcsdk-2.8.8.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
10
- ihcsdk-2.8.8.dist-info/license.txt,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
11
- ihcsdk-2.8.8.dist-info/top_level.txt,sha256=QgKE7TWblC-uXe5-7MVO5JtzvrPiMoVmGgKKsVR43hU,7
12
- ihcsdk-2.8.8.dist-info/RECORD,,