TISControlProtocol 1.0.66__tar.gz → 1.0.68__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.66 → tiscontrolprotocol-1.0.68}/PKG-INFO +1 -1
  2. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/pyproject.toml +1 -1
  3. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/api.py +4 -64
  4. tiscontrolprotocol-1.0.68/src/TISControlProtocol/display.py +178 -0
  5. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/.gitignore +0 -0
  6. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/.vscode/c_cpp_properties.json +0 -0
  7. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/.vscode/launch.json +0 -0
  8. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/.vscode/settings.json +0 -0
  9. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/.vscode/tasks.json +0 -0
  10. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/LICENSE +0 -0
  11. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/MANIFEST.in +0 -0
  12. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/README.md +0 -0
  13. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/dev/pump_version.py +0 -0
  14. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/BytesHelper.py +0 -0
  15. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/__init__.py +0 -0
  16. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/AckCoordinator.py +0 -0
  17. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketDispatcher.py +0 -0
  18. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketExtractor.py +0 -0
  19. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/AnalogFeedbackHandler.py +0 -0
  20. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/AutoBinaryFeedbackHandler.py +0 -0
  21. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/BinaryFeedbackHandler.py +0 -0
  22. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/ClimateBinaryFeedbackHandler.py +0 -0
  23. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/ClimateControlFeedbackHandler.py +0 -0
  24. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/ControlResponseHandler.py +0 -0
  25. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/DiscoveryFeedbackHandler.py +0 -0
  26. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/EnergyFeedbackHandler.py +0 -0
  27. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/FloorBinaryFeedbackHandler.py +0 -0
  28. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/HealthFeedbackHandler.py +0 -0
  29. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/LunaTempFeedbackHandler.py +0 -0
  30. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/RealTimeFeedbackHandler.py +0 -0
  31. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/SearchResponseHandler.py +0 -0
  32. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/SecurityFeedbackHandler.py +0 -0
  33. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/UpdateResponseHandler.py +0 -0
  34. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/UpdateSecurityHandler.py +0 -0
  35. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/WeatherFeedbackHandler.py +0 -0
  36. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketHandlers/__init__.py +0 -0
  37. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketProtocol.py +0 -0
  38. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketReceiver.py +0 -0
  39. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/PacketSender.py +0 -0
  40. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/ProtocolHandler.py +0 -0
  41. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/UpdateCoordinator.py +0 -0
  42. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/Protocols/udp/__init__.py +0 -0
  43. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/__init__.py +0 -0
  44. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/apis/__init__.py +0 -0
  45. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/apis/bill_config_endpoint.py +0 -0
  46. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/apis/change_password_endpoint.py +0 -0
  47. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/apis/get_bill_config_endpoint.py +0 -0
  48. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/apis/get_key_endpoint.py +0 -0
  49. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/apis/password_dashboard.py +0 -0
  50. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/apis/password_form_endpoint.py +0 -0
  51. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/apis/passwords_endpoint.py +0 -0
  52. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/apis/restart_endpoint.py +0 -0
  53. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/apis/scan_devices_endpoint.py +0 -0
  54. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/apis/shared.py +0 -0
  55. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/apis/submit_password.py +0 -0
  56. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/apis/tis_endpoint.py +0 -0
  57. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/apis/update_endpoint.py +0 -0
  58. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/crc.py +0 -0
  59. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/logo.png +0 -0
  60. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/package-lock.json +0 -0
  61. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/package.json +0 -0
  62. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/postcss.config.mjs +0 -0
  63. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/public/__init__.py +0 -0
  64. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/public/dashboard.css +0 -0
  65. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/public/password_dashboard_loader.js +0 -0
  66. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/shared.py +0 -0
  67. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/views/__init__.py +0 -0
  68. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/views/password_dashboard/index.html +0 -0
  69. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/views/password_dashboard/styles.css +0 -0
  70. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/views/password_form/index.html +0 -0
  71. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/src/TISControlProtocol/views/password_form/styles.css +0 -0
  72. {tiscontrolprotocol-1.0.66 → tiscontrolprotocol-1.0.68}/tests/test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TISControlProtocol
3
- Version: 1.0.66
3
+ Version: 1.0.68
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.66"
7
+ version = "1.0.68"
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 board
36
- import digitalio
37
- import busio
38
- from adafruit_rgb_display import st7789
39
-
40
- HAS_ST7789 = True
41
- except (ImportError, RuntimeError, NotImplementedError) 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."""
@@ -219,58 +205,12 @@ class TISApi:
219
205
  }
220
206
 
221
207
  def run_display(self, style="dots"):
222
- try:
223
- if HAS_ST7789:
224
- # 1. Initialize the SPI bus
225
- spi = board.SPI()
226
-
227
- # 2. Define the GPIO pins
228
- cs_pin = digitalio.DigitalInOut(board.CE0)
229
- dc_pin = digitalio.DigitalInOut(board.D23)
230
- rst_pin = digitalio.DigitalInOut(board.D25)
231
- bl_pin = digitalio.DigitalInOut(board.D12)
232
-
233
- # 3. Turn on the backlight physically
234
- bl_pin.direction = digitalio.Direction.OUTPUT
235
- bl_pin.value = True
236
- self._bl_pin = bl_pin # Store to prevent garbage collection
237
-
238
- # 4. Initialize the ST7789 Display
239
- self.display = st7789.ST7789(
240
- spi,
241
- cs=cs_pin,
242
- dc=dc_pin,
243
- rst=rst_pin,
244
- baudrate=60000000,
245
- width=320,
246
- height=240,
247
- rotation=0
248
- )
249
-
250
- self.set_display_image()
251
- else:
252
- logging.error("Can't start display, some packages are missing")
208
+ from .display import TISDisplay
253
209
 
254
- except Exception as e:
255
- logging.error(f"error initializing display, {e}")
256
- return
257
-
258
- def set_display_image(self):
259
- if self.display_logo:
260
- img = Image.open(self.display_logo).convert("RGB")
261
- version_text = f"V {self.version}"
262
-
263
- draw = ImageDraw.Draw(img)
264
- try:
265
- font = ImageFont.load_default(size=28)
266
- except TypeError:
267
- font = ImageFont.load_default()
210
+ if self.display is None:
211
+ self.display = TISDisplay(self.display_logo, self.version)
268
212
 
269
- x, y = 78, 235
270
- draw.text((x, y), version_text, font=font, fill=(255, 255, 255))
271
- img = img.rotate(-90, expand=True)
272
-
273
- self.display.image(img)
213
+ self.display.run_display()
274
214
 
275
215
  async def parse_device_manager_request(self, data: dict) -> None:
276
216
  """Parse the device manager request."""
@@ -0,0 +1,178 @@
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
+ class TISDisplay:
21
+ def __init__(self, display_logo: str, version: str):
22
+ self.display_logo = display_logo
23
+ self.version = version
24
+ self.spi_fd = None
25
+ self.chip_path = None
26
+ self.req_dc = None
27
+ self.req_rst = None
28
+ self.global_req_blk = None
29
+ self.chip0 = None
30
+
31
+ def _set_pin(self, pin, value):
32
+ try:
33
+ val = Value.ACTIVE if value else Value.INACTIVE
34
+ if pin == 23:
35
+ self.req_dc.set_value(23, val)
36
+ elif pin == 25:
37
+ self.req_rst.set_value(25, val)
38
+ except Exception as e:
39
+ logging.error(f"Error setting pin {pin} to {value}: {e}")
40
+
41
+ def send_command(self, cmd):
42
+ self._set_pin(23, 0) # Command mode (0)
43
+ os.write(self.spi_fd, bytes([cmd]))
44
+
45
+ def send_data(self, data):
46
+ self._set_pin(23, 1) # Data mode (1)
47
+ if isinstance(data, int):
48
+ os.write(self.spi_fd, bytes([data]))
49
+ else:
50
+ os.write(self.spi_fd, bytes(data))
51
+
52
+ def init_display(self):
53
+ # Hardware Reset
54
+ self._set_pin(25, 1)
55
+ time.sleep(0.1)
56
+ self._set_pin(25, 0)
57
+ time.sleep(0.1)
58
+ self._set_pin(25, 1)
59
+ time.sleep(0.1)
60
+
61
+ # ST7789 Initialization Sequence
62
+ self.send_command(0x01) # SWRESET
63
+ time.sleep(0.15)
64
+ self.send_command(0x11) # Sleep out
65
+ time.sleep(0.15)
66
+
67
+ self.send_command(0x3A) # Color mode 16-bit
68
+ self.send_data(0x55) # 0x55 for RGB565
69
+
70
+ self.send_command(0x36) # Memory Access Control (MADCTL)
71
+ self.send_data(0x00) # Normal rotation
72
+
73
+ self.send_command(0x21) # INVON
74
+ self.send_command(0x13) # NORON
75
+ time.sleep(0.01)
76
+ self.send_command(0x29) # Display on
77
+ time.sleep(0.1)
78
+
79
+ def set_window(self, x1, y1, x2, y2):
80
+ self.send_command(0x2A) # Column Address Set
81
+ self.send_data(bytearray([x1 >> 8, x1 & 0xFF, x2 >> 8, x2 & 0xFF]))
82
+
83
+ self.send_command(0x2B) # Row Address Set
84
+ self.send_data(bytearray([y1 >> 8, y1 & 0xFF, y2 >> 8, y2 & 0xFF]))
85
+ self.send_command(0x2C) # RAMWR
86
+
87
+ def set_display_image(self):
88
+ if self.display_logo:
89
+ img = Image.open(self.display_logo).convert("RGB")
90
+ version_text = f"V {self.version}"
91
+
92
+ draw = ImageDraw.Draw(img)
93
+ try:
94
+ font = ImageFont.load_default(size=28)
95
+ except TypeError:
96
+ font = ImageFont.load_default()
97
+
98
+ x, y = 78, 235
99
+ draw.text((x, y), version_text, font=font, fill=(255, 255, 255))
100
+ img = img.rotate(-90, expand=True)
101
+
102
+ if img.size != (240, 320):
103
+ img = img.resize((240, 320))
104
+
105
+ r, g, b = img.split()
106
+ r_data = list(r.getdata()) if not hasattr(r, "get_flattened_data") else r.get_flattened_data()
107
+ g_data = list(g.getdata()) if not hasattr(g, "get_flattened_data") else g.get_flattened_data()
108
+ b_data = list(b.getdata()) if not hasattr(b, "get_flattened_data") else b.get_flattened_data()
109
+
110
+ rgb565 = bytearray(img.width * img.height * 2)
111
+ for i in range(len(r_data)):
112
+ pixel = (((r_data[i] & 0xF8) << 8) | ((g_data[i] & 0xFC) << 3) | (b_data[i] >> 3))
113
+ rgb565[i * 2] = pixel >> 8
114
+ rgb565[i * 2 + 1] = pixel & 0xFF
115
+
116
+ self.set_window(0, 0, img.width - 1, img.height - 1)
117
+ self._set_pin(23, 1) # Data mode
118
+
119
+ mv = memoryview(rgb565)
120
+ chunk_size = 4096
121
+ for i in range(0, len(mv), chunk_size):
122
+ os.write(self.spi_fd, mv[i : i + chunk_size])
123
+
124
+ def run_display(self):
125
+ try:
126
+ if HAS_ST7789:
127
+ # Initialize SPI Bus natively
128
+ self.spi_fd = os.open("/dev/spidev0.0", os.O_RDWR)
129
+ SPI_IOC_WR_MODE = 0x40016B01
130
+ SPI_IOC_WR_MAX_SPEED_HZ = 0x40046B04
131
+ fcntl.ioctl(self.spi_fd, SPI_IOC_WR_MODE, struct.pack("B", 0))
132
+ fcntl.ioctl(self.spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, struct.pack("I", 10000000))
133
+
134
+ # Initialize GPIOs via gpiod dynamically
135
+ self.chip_path = None
136
+ chip_paths = glob.glob("/dev/gpiochip*")
137
+ for chip_path in chip_paths:
138
+ try:
139
+ with gpiod.Chip(chip_path) as chip:
140
+ info = chip.get_info()
141
+ if info.num_lines >= 50 or "bcm" in info.label.lower():
142
+ self.chip_path = chip_path
143
+ break
144
+ except Exception:
145
+ pass
146
+
147
+ if not self.chip_path:
148
+ logging.error("Could not find a suitable BCM gpiochip!")
149
+ return
150
+
151
+ self.req_dc = gpiod.request_lines(
152
+ self.chip_path,
153
+ consumer="display_dc",
154
+ config={23: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE)}
155
+ )
156
+ self.req_rst = gpiod.request_lines(
157
+ self.chip_path,
158
+ consumer="display_rst",
159
+ config={25: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE)}
160
+ )
161
+
162
+ try:
163
+ self.chip0 = gpiod.Chip("/dev/gpiochip0")
164
+ self.global_req_blk = self.chip0.request_lines(
165
+ config={12: gpiod.LineSettings(direction=Direction.OUTPUT)}
166
+ )
167
+ self.global_req_blk.set_value(12, Value.INACTIVE)
168
+ except Exception as e:
169
+ logging.error(f"Failed to initialize global backlight: {e}")
170
+
171
+ self.init_display()
172
+ self.set_display_image()
173
+ else:
174
+ logging.error("Can't start display, some packages are missing")
175
+
176
+ except Exception as e:
177
+ logging.error(f"error initializing display, {e}")
178
+ return