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.
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/PKG-INFO +1 -1
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/pyproject.toml +1 -1
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/api.py +4 -110
- tiscontrolprotocol-1.0.69/src/TISControlProtocol/display.py +205 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/.gitignore +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/.vscode/c_cpp_properties.json +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/.vscode/launch.json +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/.vscode/settings.json +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/.vscode/tasks.json +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/LICENSE +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/MANIFEST.in +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/README.md +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/dev/pump_version.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/BytesHelper.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/__init__.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/AckCoordinator.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketDispatcher.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketExtractor.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/AnalogFeedbackHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/AutoBinaryFeedbackHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/BinaryFeedbackHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/ClimateBinaryFeedbackHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/ClimateControlFeedbackHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/ControlResponseHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/DiscoveryFeedbackHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/EnergyFeedbackHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/FloorBinaryFeedbackHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/HealthFeedbackHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/LunaTempFeedbackHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/RealTimeFeedbackHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/SearchResponseHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/SecurityFeedbackHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/UpdateResponseHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/UpdateSecurityHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/WeatherFeedbackHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketHandlers/__init__.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketProtocol.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketReceiver.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/PacketSender.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/ProtocolHandler.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/UpdateCoordinator.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/udp/__init__.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/__init__.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/__init__.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/bill_config_endpoint.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/change_password_endpoint.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/get_bill_config_endpoint.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/get_key_endpoint.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/password_dashboard.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/password_form_endpoint.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/passwords_endpoint.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/restart_endpoint.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/scan_devices_endpoint.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/shared.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/submit_password.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/tis_endpoint.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/update_endpoint.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/crc.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/logo.png +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/package-lock.json +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/package.json +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/postcss.config.mjs +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/public/__init__.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/public/dashboard.css +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/public/password_dashboard_loader.js +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/shared.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/views/__init__.py +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/views/password_dashboard/index.html +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/views/password_dashboard/styles.css +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/views/password_form/index.html +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/views/password_form/styles.css +0 -0
- {tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/tests/test.py +0 -0
|
@@ -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
|
-
|
|
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
|
-
|
|
282
|
-
|
|
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
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/BytesHelper.py
RENAMED
|
File without changes
|
{tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/Protocols/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/shared.py
RENAMED
|
File without changes
|
|
File without changes
|
{tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/apis/tis_endpoint.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/package-lock.json
RENAMED
|
File without changes
|
|
File without changes
|
{tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/postcss.config.mjs
RENAMED
|
File without changes
|
{tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/public/__init__.py
RENAMED
|
File without changes
|
{tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/public/dashboard.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tiscontrolprotocol-1.0.67 → tiscontrolprotocol-1.0.69}/src/TISControlProtocol/views/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|