TISControlProtocol 1.0.67__tar.gz → 1.0.69__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 (72) hide show
  1. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/PKG-INFO +1 -1
  2. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/pyproject.toml +1 -1
  3. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/api.py +4 -110
  4. tiscontrolprotocol-1.0.69/src/TISControlProtocol/display.py +205 -0
  5. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/.gitignore +0 -0
  6. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/.vscode/c_cpp_properties.json +0 -0
  7. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/.vscode/launch.json +0 -0
  8. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/.vscode/settings.json +0 -0
  9. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/.vscode/tasks.json +0 -0
  10. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/LICENSE +0 -0
  11. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/MANIFEST.in +0 -0
  12. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/README.md +0 -0
  13. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/dev/pump_version.py +0 -0
  14. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/BytesHelper.py +0 -0
  15. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/__init__.py +0 -0
  16. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/AckCoordinator.py +0 -0
  17. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketDispatcher.py +0 -0
  18. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketExtractor.py +0 -0
  19. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/AnalogFeedbackHandler.py +0 -0
  20. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/AutoBinaryFeedbackHandler.py +0 -0
  21. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/BinaryFeedbackHandler.py +0 -0
  22. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/ClimateBinaryFeedbackHandler.py +0 -0
  23. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/ClimateControlFeedbackHandler.py +0 -0
  24. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/ControlResponseHandler.py +0 -0
  25. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/DiscoveryFeedbackHandler.py +0 -0
  26. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/EnergyFeedbackHandler.py +0 -0
  27. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/FloorBinaryFeedbackHandler.py +0 -0
  28. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/HealthFeedbackHandler.py +0 -0
  29. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/LunaTempFeedbackHandler.py +0 -0
  30. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/RealTimeFeedbackHandler.py +0 -0
  31. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/SearchResponseHandler.py +0 -0
  32. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/SecurityFeedbackHandler.py +0 -0
  33. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/UpdateResponseHandler.py +0 -0
  34. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/UpdateSecurityHandler.py +0 -0
  35. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/WeatherFeedbackHandler.py +0 -0
  36. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/__init__.py +0 -0
  37. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketProtocol.py +0 -0
  38. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketReceiver.py +0 -0
  39. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketSender.py +0 -0
  40. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/ProtocolHandler.py +0 -0
  41. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/UpdateCoordinator.py +0 -0
  42. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/__init__.py +0 -0
  43. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/__init__.py +0 -0
  44. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/__init__.py +0 -0
  45. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/bill_config_endpoint.py +0 -0
  46. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/change_password_endpoint.py +0 -0
  47. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/get_bill_config_endpoint.py +0 -0
  48. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/get_key_endpoint.py +0 -0
  49. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/password_dashboard.py +0 -0
  50. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/password_form_endpoint.py +0 -0
  51. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/passwords_endpoint.py +0 -0
  52. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/restart_endpoint.py +0 -0
  53. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/scan_devices_endpoint.py +0 -0
  54. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/shared.py +0 -0
  55. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/submit_password.py +0 -0
  56. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/tis_endpoint.py +0 -0
  57. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/update_endpoint.py +0 -0
  58. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/crc.py +0 -0
  59. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/logo.png +0 -0
  60. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/package-lock.json +0 -0
  61. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/package.json +0 -0
  62. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/postcss.config.mjs +0 -0
  63. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/public/__init__.py +0 -0
  64. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/public/dashboard.css +0 -0
  65. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/public/password_dashboard_loader.js +0 -0
  66. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/shared.py +0 -0
  67. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/views/__init__.py +0 -0
  68. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/views/password_dashboard/index.html +0 -0
  69. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/views/password_dashboard/styles.css +0 -0
  70. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/views/password_form/index.html +0 -0
  71. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/views/password_form/styles.css +0 -0
  72. {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/tests/test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TISControlProtocol
3
- Version: 1.0.67
3
+ Version: 1.0.69
4
4
  Summary: A library for TIS Control Protocol
5
5
  Project-URL: Homepage, https://github.com/TISControlHass
6
6
  Project-URL: Issues, https://github.com/TISControlHass/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "TISControlProtocol"
7
- version = "1.0.67"
7
+ version = "1.0.69"
8
8
  description = "A library for TIS Control Protocol"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -12,7 +12,6 @@ import psutil
12
12
  from homeassistant.core import HomeAssistant
13
13
  from homeassistant.helpers.aiohttp_client import async_get_clientsession
14
14
  from homeassistant.helpers.event import async_track_time_interval
15
- from PIL import Image, ImageDraw, ImageFont
16
15
 
17
16
  from TISControlProtocol.Protocols import setup_udp_protocol
18
17
  from TISControlProtocol.shared import get_real_mac
@@ -31,19 +30,6 @@ from .apis import (
31
30
  setup_views,
32
31
  )
33
32
 
34
- try:
35
- import time
36
- import spidev
37
- import gpiod
38
- from gpiod.line import Direction, Value
39
-
40
- HAS_ST7789 = True
41
- except (ImportError, RuntimeError) as e:
42
- HAS_ST7789 = False
43
- logging.error(
44
- "Failed to load display dependencies. Display will be disabled. Error: %s", e
45
- )
46
-
47
33
 
48
34
  class TISApi:
49
35
  """TIS API class."""
@@ -218,105 +204,13 @@ class TISApi:
218
204
  "ram_percent": mem.percent,
219
205
  }
220
206
 
221
- def send_command(self, cmd):
222
- self.gpio_request.set_value(23, Value.INACTIVE) # Command mode (0)
223
- self.spi.xfer2([cmd])
224
-
225
- def send_data(self, data):
226
- self.gpio_request.set_value(23, Value.ACTIVE) # Data mode (1)
227
- self.spi.xfer2(list(data))
228
-
229
- def init_display(self):
230
- # Hardware Reset
231
- self.gpio_request.set_value(25, Value.INACTIVE)
232
- time.sleep(0.1)
233
- self.gpio_request.set_value(25, Value.ACTIVE)
234
- time.sleep(0.1)
235
-
236
- # ST7789 Initialization Sequence
237
- self.send_command(0x11) # Sleep out
238
- time.sleep(0.12)
239
-
240
- self.send_command(0x3A) # Color mode 16-bit
241
- self.send_data([0x55]) # 0x55 for RGB565
242
-
243
- self.send_command(0x36) # Memory Access Control (MADCTL)
244
- self.send_data([0x00]) # Normal rotation
245
-
246
- self.send_command(0x29) # Display on
247
- time.sleep(0.1)
248
-
249
- def set_window(self, x1, y1, x2, y2):
250
- self.send_command(0x2A) # Column Address Set
251
- self.send_data([x1 >> 8, x1 & 0xFF, x2 >> 8, x2 & 0xFF])
252
-
253
- self.send_command(0x2B) # Row Address Set
254
- self.send_data([y1 >> 8, y1 & 0xFF, y2 >> 8, y2 & 0xFF])
255
-
256
207
  def run_display(self, style="dots"):
257
- try:
258
- if HAS_ST7789:
259
- # Initialize SPI Bus
260
- self.spi = spidev.SpiDev()
261
- self.spi.open(0, 0)
262
- self.spi.max_speed_hz = 30 * 1000 * 1000 # 30 MHz (conservative and stable)
263
- self.spi.mode = 0b00
264
-
265
- # Open and request GPIO lines using gpiod v2 API
266
- self.gpio_request = gpiod.request_lines(
267
- "/dev/gpiochip0",
268
- consumer="display",
269
- config={
270
- 23: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE),
271
- 25: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.ACTIVE),
272
- 12: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.ACTIVE),
273
- },
274
- )
275
-
276
- self.init_display()
277
- self.set_display_image()
278
- else:
279
- logging.error("Can't start display, some packages are missing")
208
+ from .display import TISDisplay
280
209
 
281
- except Exception as e:
282
- logging.error(f"error initializing display, {e}")
283
- return
284
-
285
- def set_display_image(self):
286
- if self.display_logo:
287
- img = Image.open(self.display_logo).convert("RGB")
288
- version_text = f"V {self.version}"
289
-
290
- draw = ImageDraw.Draw(img)
291
- try:
292
- font = ImageFont.load_default(size=28)
293
- except TypeError:
294
- font = ImageFont.load_default()
210
+ if self.display is None:
211
+ self.display = TISDisplay(self.display_logo, self.version)
295
212
 
296
- x, y = 78, 235
297
- draw.text((x, y), version_text, font=font, fill=(255, 255, 255))
298
- img = img.rotate(-90, expand=True)
299
-
300
- # Convert image to RGB565 (16-bit)
301
- img_bytes = []
302
- for y in range(img.height):
303
- for x in range(img.width):
304
- r, g, b = img.getpixel((x, y))
305
- # Pack into RGB565 format
306
- rgb565 = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)
307
- img_bytes.append((rgb565 >> 8) & 0xFF)
308
- img_bytes.append(rgb565 & 0xFF)
309
-
310
- # Send pixel data
311
- self.set_window(0, 0, img.width - 1, img.height - 1)
312
- self.send_command(0x2C) # Memory Write
313
-
314
- # Send in chunks to avoid overwhelming the SPI buffer
315
- chunk_size = 4096
316
- for i in range(0, len(img_bytes), chunk_size):
317
- chunk = img_bytes[i:i + chunk_size]
318
- self.gpio_request.set_value(23, Value.ACTIVE) # Data mode
319
- self.spi.xfer2(chunk)
213
+ self.display.run_display()
320
214
 
321
215
  async def parse_device_manager_request(self, data: dict) -> None:
322
216
  """Parse the device manager request."""
@@ -0,0 +1,205 @@
1
+ import logging
2
+ import os
3
+ import time
4
+ from PIL import Image, ImageDraw, ImageFont
5
+
6
+ try:
7
+ import fcntl
8
+ import struct
9
+ import glob
10
+ import gpiod
11
+ from gpiod.line import Direction, Value
12
+
13
+ HAS_ST7789 = True
14
+ except (ImportError, RuntimeError) as e:
15
+ HAS_ST7789 = False
16
+ logging.error(
17
+ "Failed to load display dependencies. Display will be disabled. Error: %s", e
18
+ )
19
+
20
+
21
+ class TISDisplay:
22
+ def __init__(self, display_logo: str, version: str):
23
+ self.display_logo = display_logo
24
+ self.version = version
25
+ self.spi_fd = None
26
+ self.chip_path = None
27
+ self.req_dc = None
28
+ self.req_rst = None
29
+ self.global_req_blk = None
30
+ self.chip0 = None
31
+
32
+ def _set_pin(self, pin, value):
33
+ try:
34
+ val = Value.ACTIVE if value else Value.INACTIVE
35
+ if pin == 23:
36
+ self.req_dc.set_value(23, val)
37
+ elif pin == 25:
38
+ self.req_rst.set_value(25, val)
39
+ except Exception as e:
40
+ logging.error(f"Error setting pin {pin} to {value}: {e}")
41
+
42
+ def send_command(self, cmd):
43
+ self._set_pin(23, 0) # Command mode (0)
44
+ os.write(self.spi_fd, bytes([cmd]))
45
+
46
+ def send_data(self, data):
47
+ self._set_pin(23, 1) # Data mode (1)
48
+ if isinstance(data, int):
49
+ os.write(self.spi_fd, bytes([data]))
50
+ else:
51
+ os.write(self.spi_fd, bytes(data))
52
+
53
+ def init_display(self):
54
+ # Hardware Reset
55
+ self._set_pin(25, 1)
56
+ time.sleep(0.1)
57
+ self._set_pin(25, 0)
58
+ time.sleep(0.1)
59
+ self._set_pin(25, 1)
60
+ time.sleep(0.1)
61
+
62
+ # ST7789 Initialization Sequence
63
+ self.send_command(0x01) # SWRESET
64
+ time.sleep(0.15)
65
+ self.send_command(0x11) # Sleep out
66
+ time.sleep(0.15)
67
+
68
+ self.send_command(0x3A) # Color mode 16-bit
69
+ self.send_data(0x55) # 0x55 for RGB565
70
+
71
+ self.send_command(0x36) # Memory Access Control (MADCTL)
72
+ self.send_data(0x00) # Normal rotation
73
+
74
+ self.send_command(0x21) # INVON
75
+ self.send_command(0x13) # NORON
76
+ time.sleep(0.01)
77
+ self.send_command(0x29) # Display on
78
+ time.sleep(0.1)
79
+
80
+ def set_window(self, x1, y1, x2, y2):
81
+ self.send_command(0x2A) # Column Address Set
82
+ self.send_data(bytearray([x1 >> 8, x1 & 0xFF, x2 >> 8, x2 & 0xFF]))
83
+
84
+ self.send_command(0x2B) # Row Address Set
85
+ self.send_data(bytearray([y1 >> 8, y1 & 0xFF, y2 >> 8, y2 & 0xFF]))
86
+ self.send_command(0x2C) # RAMWR
87
+
88
+ def set_display_image(self):
89
+ if self.display_logo:
90
+ img = Image.open(self.display_logo).convert("RGB")
91
+ version_text = f"V {self.version}"
92
+
93
+ draw = ImageDraw.Draw(img)
94
+ try:
95
+ font = ImageFont.load_default(size=28)
96
+ except TypeError:
97
+ font = ImageFont.load_default()
98
+
99
+ x, y = 78, 235
100
+ draw.text((x, y), version_text, font=font, fill=(255, 255, 255))
101
+ img = img.rotate(180, expand=True)
102
+
103
+ if img.size != (240, 320):
104
+ img = img.resize((240, 320))
105
+
106
+ r, g, b = img.split()
107
+ r_data = (
108
+ list(r.getdata())
109
+ if not hasattr(r, "get_flattened_data")
110
+ else r.get_flattened_data()
111
+ )
112
+ g_data = (
113
+ list(g.getdata())
114
+ if not hasattr(g, "get_flattened_data")
115
+ else g.get_flattened_data()
116
+ )
117
+ b_data = (
118
+ list(b.getdata())
119
+ if not hasattr(b, "get_flattened_data")
120
+ else b.get_flattened_data()
121
+ )
122
+
123
+ rgb565 = bytearray(img.width * img.height * 2)
124
+ for i in range(len(r_data)):
125
+ pixel = (
126
+ ((r_data[i] & 0xF8) << 8)
127
+ | ((g_data[i] & 0xFC) << 3)
128
+ | (b_data[i] >> 3)
129
+ )
130
+ rgb565[i * 2] = pixel >> 8
131
+ rgb565[i * 2 + 1] = pixel & 0xFF
132
+
133
+ self.set_window(0, 0, img.width - 1, img.height - 1)
134
+ self._set_pin(23, 1) # Data mode
135
+
136
+ mv = memoryview(rgb565)
137
+ chunk_size = 4096
138
+ for i in range(0, len(mv), chunk_size):
139
+ os.write(self.spi_fd, mv[i : i + chunk_size])
140
+
141
+ def run_display(self):
142
+ try:
143
+ if HAS_ST7789:
144
+ # Initialize SPI Bus natively
145
+ self.spi_fd = os.open("/dev/spidev0.0", os.O_RDWR)
146
+ SPI_IOC_WR_MODE = 0x40016B01
147
+ SPI_IOC_WR_MAX_SPEED_HZ = 0x40046B04
148
+ fcntl.ioctl(self.spi_fd, SPI_IOC_WR_MODE, struct.pack("B", 0))
149
+ fcntl.ioctl(
150
+ self.spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, struct.pack("I", 10000000)
151
+ )
152
+
153
+ # Initialize GPIOs via gpiod dynamically
154
+ self.chip_path = None
155
+ chip_paths = glob.glob("/dev/gpiochip*")
156
+ for chip_path in chip_paths:
157
+ try:
158
+ with gpiod.Chip(chip_path) as chip:
159
+ info = chip.get_info()
160
+ if info.num_lines >= 50 or "bcm" in info.label.lower():
161
+ self.chip_path = chip_path
162
+ break
163
+ except Exception:
164
+ pass
165
+
166
+ if not self.chip_path:
167
+ logging.error("Could not find a suitable BCM gpiochip!")
168
+ return
169
+
170
+ self.req_dc = gpiod.request_lines(
171
+ self.chip_path,
172
+ consumer="display_dc",
173
+ config={
174
+ 23: gpiod.LineSettings(
175
+ direction=Direction.OUTPUT, output_value=Value.INACTIVE
176
+ )
177
+ },
178
+ )
179
+ self.req_rst = gpiod.request_lines(
180
+ self.chip_path,
181
+ consumer="display_rst",
182
+ config={
183
+ 25: gpiod.LineSettings(
184
+ direction=Direction.OUTPUT, output_value=Value.INACTIVE
185
+ )
186
+ },
187
+ )
188
+
189
+ try:
190
+ self.chip0 = gpiod.Chip("/dev/gpiochip0")
191
+ self.global_req_blk = self.chip0.request_lines(
192
+ config={12: gpiod.LineSettings(direction=Direction.OUTPUT)}
193
+ )
194
+ self.global_req_blk.set_value(12, Value.INACTIVE)
195
+ except Exception as e:
196
+ logging.error(f"Failed to initialize global backlight: {e}")
197
+
198
+ self.init_display()
199
+ self.set_display_image()
200
+ else:
201
+ logging.error("Can't start display, some packages are missing")
202
+
203
+ except Exception as e:
204
+ logging.error(f"error initializing display, {e}")
205
+ return