TISControlProtocol 0.0.4__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 (26) hide show
  1. tiscontrolprotocol-0.0.4/.gitignore +1 -0
  2. tiscontrolprotocol-0.0.4/.pypirc +3 -0
  3. tiscontrolprotocol-0.0.4/LICENSE +21 -0
  4. tiscontrolprotocol-0.0.4/PKG-INFO +19 -0
  5. tiscontrolprotocol-0.0.4/README.md +5 -0
  6. tiscontrolprotocol-0.0.4/pyproject.toml +22 -0
  7. tiscontrolprotocol-0.0.4/src/TISControlProtocol/BytesHelper.py +65 -0
  8. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/__init__.py +18 -0
  9. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/udp/AckCoordinator.py +50 -0
  10. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/udp/PacketDispatcher.py +16 -0
  11. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/udp/PacketExtractor.py +19 -0
  12. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/udp/PacketHandlers/AutoBinaryFeedbackHandler.py +18 -0
  13. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/udp/PacketHandlers/BinaryFeedbackHandler.py +17 -0
  14. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/udp/PacketHandlers/ClimateFeedbackHandler.py +9 -0
  15. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/udp/PacketHandlers/ControlResponseHandler.py +34 -0
  16. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/udp/PacketHandlers/DiscoveryFeedbackHandler.py +10 -0
  17. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/udp/PacketHandlers/SearchResponseHandler.py +9 -0
  18. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/udp/PacketHandlers/__init__.py +1 -0
  19. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/udp/PacketProtocol.py +56 -0
  20. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/udp/PacketReceiver.py +37 -0
  21. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/udp/PacketSender.py +92 -0
  22. tiscontrolprotocol-0.0.4/src/TISControlProtocol/Protocols/udp/__init__.py +6 -0
  23. tiscontrolprotocol-0.0.4/src/TISControlProtocol/__init__.py +0 -0
  24. tiscontrolprotocol-0.0.4/src/TISControlProtocol/crc.py +306 -0
  25. tiscontrolprotocol-0.0.4/src/TISControlProtocol/shared.py +21 -0
  26. tiscontrolprotocol-0.0.4/tests/test.py +13 -0
@@ -0,0 +1 @@
1
+ .env
@@ -0,0 +1,3 @@
1
+ [pypi]
2
+ username = __token__
3
+ password = pypi-AgEIcHlwaS5vcmcCJGNhNmI1MjUyLTk3ZjUtNGMyOS05MTU5LTBlZTNmNTU3YzBkMAACKlszLCI3NTZmYWQ1MC0xZDAwLTQwN2QtOTI2MC03MDZmYzVjMTlmMjAiXQAABiCtQBdnR6dtdmpuwD-X_wUk-TkZfzd3A17jrKSNFis_aQ
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [year] [fullname]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.3
2
+ Name: TISControlProtocol
3
+ Version: 0.0.4
4
+ Summary: A small example package
5
+ Project-URL: Homepage, https://github.com/pypa/sampleproject
6
+ Project-URL: Issues, https://github.com/pypa/sampleproject/issues
7
+ Author-email: Mostafa Ashraf <mustafaashraf744@gmail.com>
8
+ License-File: LICENSE
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: POSIX :: Linux
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+
15
+ # Example Package
16
+
17
+ This is a simple example package. You can use
18
+ [GitHub-flavored Markdown](https://guides.github.com/features/mastering-markdown/)
19
+ to write your content.
@@ -0,0 +1,5 @@
1
+ # Example Package
2
+
3
+ This is a simple example package. You can use
4
+ [GitHub-flavored Markdown](https://guides.github.com/features/mastering-markdown/)
5
+ to write your content.
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+ [project]
5
+ name = "TISControlProtocol"
6
+ version = "0.0.4"
7
+ authors = [{ name = "Mostafa Ashraf", email = "mustafaashraf744@gmail.com" }]
8
+ description = "A small example package"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ classifiers = [
12
+ "Programming Language :: Python :: 3",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Operating System :: POSIX :: Linux",
15
+ ]
16
+
17
+ [project.urls]
18
+ Homepage = "https://github.com/pypa/sampleproject"
19
+ Issues = "https://github.com/pypa/sampleproject/issues"
20
+
21
+
22
+ # [testpypi]
@@ -0,0 +1,65 @@
1
+ import binascii
2
+ from TISControlProtocol.crc import checkCRC, packCRC
3
+
4
+
5
+ def bytes2hex(data, rtype=[]):
6
+ """A helper function to parse bytes to hex
7
+
8
+ :param data: the raw bytes array
9
+ :type data: bytes array
10
+ :param rtype: determine return type whether list of ints or single hex str, defaults to []
11
+ :type rtype: list, optional
12
+ :return: list of ints or hex string
13
+ :rtype: list | str
14
+ """
15
+ hex_string = binascii.hexlify(data).decode()
16
+ hex_list = [int(hex_string[i : i + 2], 16) for i in range(0, len(hex_string), 2)]
17
+ if isinstance(rtype, list):
18
+ return hex_list
19
+ else:
20
+ return hex_string
21
+
22
+
23
+ def build_packet(
24
+ operation_code: list,
25
+ ip_address: str,
26
+ destination_mac: str,
27
+ source_mac: str = "CB:CB:CB:CB:CB:CB:CB:CB",
28
+ device_id: list = [],
29
+ source_device_id: list = [0x01, 0xFE],
30
+ additional_packets: list = [],
31
+ header="SMARTCLOUD",
32
+ ):
33
+
34
+ # test if all params are loaded
35
+ ip_bytes = [int(part) for part in ip_address.split(".")]
36
+ header_bytes = [ord(char) for char in header]
37
+ source_mac = [int(part, 16) for part in source_mac.split(":")]
38
+ destination_mac = [int(part, 16) for part in destination_mac.split(":")]
39
+ # TODO: modify length
40
+ # length = 21 + len(additional_packets)
41
+ length = 11 + len(additional_packets)
42
+ packet = (
43
+ ip_bytes
44
+ + header_bytes
45
+ + [0xAA, 0xAA]
46
+ + [length]
47
+ # + source_mac
48
+ + source_device_id
49
+ + [0xFF, 0xFE]
50
+ + operation_code
51
+ # + destination_mac
52
+ + device_id
53
+ + additional_packets
54
+ )
55
+ packet = packCRC(packet)
56
+ return packet
57
+
58
+
59
+ def decode_mac(mac: list):
60
+ return ":".join([f"{byte:02X}" for byte in mac])
61
+
62
+
63
+ def int_to_8_bit_binary(number):
64
+ binary_string = bin(number)[2:]
65
+ return binary_string.zfill(8)[::-1]
@@ -0,0 +1,18 @@
1
+ from asyncio import get_event_loop, AbstractEventLoop
2
+ import socket
3
+ from TISControlProtocol.Protocols.udp.PacketProtocol import PacketProtocol
4
+
5
+ loop = get_event_loop()
6
+
7
+
8
+ async def setup_udp_protocol(
9
+ sock: socket, loop: AbstractEventLoop, udp_ip, udp_port, local_ip, hass
10
+ ) -> tuple[socket.socket, PacketProtocol]:
11
+ transport, protocol = await loop.create_datagram_endpoint(
12
+ lambda: PacketProtocol(sock, udp_ip, udp_port, hass),
13
+ remote_addr=(udp_ip, udp_port),
14
+ local_addr=("0.0.0.0", udp_port),
15
+ allow_broadcast=True,
16
+ reuse_port=True,
17
+ )
18
+ return transport, protocol
@@ -0,0 +1,50 @@
1
+ import asyncio
2
+ from TISControlProtocol.shared import ack_events
3
+ from typing import Union
4
+
5
+
6
+ class AckCoordinator:
7
+ def __init__(self):
8
+ self.ack_events = ack_events
9
+
10
+ def create_ack_event(self, unique_id: Union[str, tuple]) -> asyncio.Event:
11
+ print(f"creating ack event for {unique_id}")
12
+ event = asyncio.Event()
13
+ self.ack_events[unique_id] = event
14
+ return event
15
+
16
+ def get_ack_event(self, unique_id: Union[str, tuple]) -> Union[asyncio.Event, None]:
17
+ return self.ack_events.get(unique_id)
18
+
19
+ def remove_ack_event(self, unique_id: Union[str, tuple]) -> None:
20
+ if unique_id in self.ack_events:
21
+ del self.ack_events[unique_id]
22
+
23
+ # async def create_ack_task(
24
+ # self,
25
+ # sender,
26
+ # packet,
27
+ # packet_dict=None,
28
+ # channel_number=None,
29
+ # attempts=3,
30
+ # timeout=5.0,
31
+ # ) -> bool:
32
+ # unique_id = (
33
+ # (tuple(packet_dict["device_id"])),
34
+ # packet_dict["operation_code"],
35
+ # channel_number,
36
+ # )
37
+ # event = self.create_ack_event(unique_id)
38
+
39
+ # for attempt in range(attempts):
40
+ # sender.send_packet(packet)
41
+ # try:
42
+ # await asyncio.wait_for(event.wait(), timeout)
43
+ # self.remove_ack_event(unique_id)
44
+ # return True
45
+ # except asyncio.TimeoutError:
46
+ # print(
47
+ # f"ack not received within {timeout} seconds, attempt {attempt + 1}"
48
+ # )
49
+
50
+ # return False
@@ -0,0 +1,16 @@
1
+ from homeassistant.core import HomeAssistant # type: ignore
2
+
3
+
4
+ class PacketDispatcher:
5
+ def __init__(self, hass: HomeAssistant, OPERATIONS_DICT: dict):
6
+ self.hass = hass
7
+ self.operations_dict = OPERATIONS_DICT
8
+
9
+ async def dispatch_packet(self, info):
10
+ packet_handler = self.operations_dict.get(
11
+ tuple(info["operation_code"]), "unknown operation"
12
+ )
13
+ if packet_handler != "unknown operation":
14
+ await packet_handler(self.hass, info)
15
+ else:
16
+ print(f"unknown operation code: {info['operation_code']}")
@@ -0,0 +1,19 @@
1
+ from TISControlProtocol.BytesHelper import checkCRC
2
+
3
+
4
+ # PacketExtractor.py
5
+ class PacketExtractor:
6
+ @staticmethod
7
+ def extract_info(packet: list):
8
+ packet_check = checkCRC(packet)
9
+ info = {}
10
+ if packet_check:
11
+ print("correct packet")
12
+ info["device_id"] = packet[17:19]
13
+ info["device_type"] = packet[19:21]
14
+ info["operation_code"] = packet[21:23]
15
+ info["additional_bytes"] = packet[25:-2]
16
+
17
+ else:
18
+ print("wrong packet")
19
+ return info
@@ -0,0 +1,18 @@
1
+ from homeassistant.core import HomeAssistant # type: ignore
2
+ import logging
3
+
4
+
5
+ async def handle_auto_binary_feedback(hass: HomeAssistant, info: dict):
6
+ logging.error(f"Auto Binary Feedback: {info}")
7
+ channels_number: int = info["additional_bytes"][0]
8
+ channels_values: list = info["additional_bytes"][-1 * channels_number :]
9
+
10
+ event_data = {
11
+ "device_id": info["device_id"],
12
+ "feedback_type": "auto_binary_feedback",
13
+ "channels_values": channels_values,
14
+ }
15
+ try:
16
+ hass.bus.async_fire(str(info["device_id"]), event_data)
17
+ except Exception as e:
18
+ logging.error(f"error in firing event: {e}")
@@ -0,0 +1,17 @@
1
+ from homeassistant.core import HomeAssistant # type: ignore
2
+ import logging
3
+
4
+
5
+ async def handle_binary_feedback(hass: HomeAssistant, info: dict):
6
+ # remove auxilary bytes which represents number of scenarios
7
+ len_aux = info["additional_bytes"][0]
8
+ info["additional_bytes"] = info["additional_bytes"][len_aux + 1 :]
9
+ event_data = {
10
+ "device_id": info["device_id"],
11
+ "feedback_type": "binary_feedback",
12
+ "additional_bytes": info["additional_bytes"],
13
+ }
14
+ try:
15
+ hass.bus.async_fire(str(info["device_id"]), event_data)
16
+ except Exception as e:
17
+ logging.error(f"error in firing event: {e}")
@@ -0,0 +1,9 @@
1
+ from TISControlProtocol.shared import ack_events
2
+ import asyncio
3
+ from homeassistant.core import HomeAssistant
4
+
5
+ import logging
6
+
7
+
8
+ async def handle_climate_feedback(hass: HomeAssistant, info: dict):
9
+ logging.error(f"got ac feedback {info}")
@@ -0,0 +1,34 @@
1
+ from homeassistant.core import HomeAssistant
2
+
3
+ import logging
4
+ import asyncio
5
+ from TISControlProtocol.shared import ack_events
6
+
7
+
8
+ async def handle_control_response(hass: HomeAssistant, info: dict):
9
+ channel_number = info["additional_bytes"][0]
10
+ # await target_appliance.handle_packet(info["additional_bytes"], "control")
11
+ event_data = {
12
+ "device_id": info["device_id"],
13
+ "channel_number": channel_number,
14
+ "feedback_type": "control_response",
15
+ "additional_bytes": info["additional_bytes"],
16
+ }
17
+ try:
18
+ hass.bus.async_fire(str(info["device_id"]), event_data)
19
+ except Exception as e:
20
+ logging.error(f"error in firing even for feedbackt: {e}")
21
+
22
+ try:
23
+ event: asyncio.Event = ack_events.get(
24
+ (
25
+ tuple(info["device_id"]),
26
+ (0x00, 0x31),
27
+ channel_number,
28
+ )
29
+ )
30
+ if event is not None:
31
+ print("setting event")
32
+ event.set()
33
+ except Exception as e:
34
+ print(e)
@@ -0,0 +1,10 @@
1
+ from homeassistant.core import HomeAssistant # type: ignore
2
+
3
+
4
+ async def handle_discovery_feedback(hass: HomeAssistant, info: dict):
5
+ # check if the record already exists if not add it
6
+ if not any(
7
+ device["device_id"] == info["device_id"]
8
+ for device in hass.data["tis_control"]["discovered_devices"]
9
+ ):
10
+ hass.data["tis_control"]["discovered_devices"].append(info)
@@ -0,0 +1,9 @@
1
+
2
+ async def handle_search_response(self, info: dict):
3
+ print(f"got search response packet from {info['device_id']}")
4
+ self.discovered_devices.append(
5
+ {
6
+ "mac": info["device_id"],
7
+ "device_type": DEVICES_DICT[tuple(info["additional_bytes"])],
8
+ }
9
+ )
@@ -0,0 +1 @@
1
+ # TODO: implement all handlers here
@@ -0,0 +1,56 @@
1
+ from TISControlProtocol.BytesHelper import * # noqa: F403
2
+ from TISControlProtocol.Protocols.udp.PacketSender import PacketSender
3
+ from TISControlProtocol.Protocols.udp.PacketReceiver import PacketReceiver
4
+ from TISControlProtocol.Protocols.udp.AckCoordinator import AckCoordinator
5
+
6
+ from TISControlProtocol.shared import ack_events
7
+
8
+ from homeassistant.core import HomeAssistant # type: ignore
9
+ from .PacketHandlers.BinaryFeedbackHandler import handle_binary_feedback
10
+ from .PacketHandlers.ControlResponseHandler import handle_control_response
11
+
12
+ from .PacketHandlers.AutoBinaryFeedbackHandler import handle_auto_binary_feedback
13
+
14
+ from .PacketHandlers.ClimateFeedbackHandler import handle_climate_feedback
15
+ from .PacketHandlers.DiscoveryFeedbackHandler import handle_discovery_feedback
16
+
17
+ import socket as Socket
18
+
19
+ OPERATIONS_DICT = {
20
+ (0x00, 0x32): handle_control_response,
21
+ (0xEF, 0xFF): handle_binary_feedback,
22
+ (0xDC, 0x22): handle_auto_binary_feedback,
23
+ (0xE0, 0xED): handle_climate_feedback,
24
+ (0x00, 0x0F): handle_discovery_feedback,
25
+ }
26
+ # 1C 01 30 1B BA DC 22 FF FF 08 02 02 02 02 02 02 02 02 00 01 01 01 01 01 01 01 57 62
27
+
28
+
29
+ class PacketProtocol:
30
+ def __init__(
31
+ self,
32
+ socket: Socket.socket,
33
+ UDP_IP,
34
+ UDP_PORT,
35
+ hass: HomeAssistant,
36
+ ):
37
+ self.UDP_IP = UDP_IP
38
+ self.UDP_PORT = UDP_PORT
39
+ self.socket = socket
40
+ self.searching = False
41
+ self.search_results = []
42
+ self.discovered_devices = []
43
+ self.hass = hass
44
+
45
+ self.ack_events = ack_events
46
+ self.coordinator = AckCoordinator()
47
+ self.sender = PacketSender(
48
+ socket=self.socket,
49
+ coordinator=self.coordinator,
50
+ UDP_IP=self.UDP_IP,
51
+ UDP_PORT=self.UDP_PORT,
52
+ )
53
+ self.receiver = PacketReceiver(self.socket, OPERATIONS_DICT, self.hass)
54
+
55
+ self.connection_made = self.receiver.connection_made
56
+ self.datagram_received = self.receiver.datagram_received
@@ -0,0 +1,37 @@
1
+ from TISControlProtocol.BytesHelper import * # noqa: F403
2
+ from socket import socket
3
+ from TISControlProtocol.Protocols.udp.PacketExtractor import PacketExtractor
4
+ from TISControlProtocol.Protocols.udp.PacketDispatcher import PacketDispatcher
5
+ import logging
6
+ from homeassistant.core import HomeAssistant # type: ignore
7
+
8
+
9
+ # PacketReceiver.py
10
+ class PacketReceiver:
11
+ def __init__(
12
+ self,
13
+ socket: socket,
14
+ OPERATIONS_DICT: dict,
15
+ hass: HomeAssistant,
16
+ ):
17
+ self.socket = socket
18
+ self._hass = hass
19
+ self.dispatcher = PacketDispatcher(self._hass, OPERATIONS_DICT)
20
+ self.transport = None
21
+ self
22
+
23
+ def connection_made(self, transport):
24
+ self.transport = transport
25
+ logging.error("connection made")
26
+
27
+ def datagram_received(self, data, addr):
28
+ # logging.error(f"datagram received {data} from {addr}")
29
+ try:
30
+ hex = bytes2hex(data, []) # noqa: F405
31
+ logging.warning(f"recieved packet {hex}")
32
+ info = PacketExtractor.extract_info(hex)
33
+ # dispatch the packet to the appropriate method according to the info
34
+ self._hass.async_create_task(self.dispatcher.dispatch_packet(info))
35
+
36
+ except Exception as e:
37
+ logging.error(f"Error in datagram_received: {e}, info: {info}")
@@ -0,0 +1,92 @@
1
+ from socket import socket
2
+ from TISControlProtocol.Protocols.udp.AckCoordinator import AckCoordinator
3
+ import asyncio
4
+ from abc import ABC, abstractmethod
5
+ from TISControlProtocol.shared import ack_events # noqa: F401
6
+ import logging
7
+
8
+
9
+ class IPacketSender(ABC):
10
+ @abstractmethod
11
+ async def send_packet(self, packet):
12
+ pass
13
+
14
+ @abstractmethod
15
+ async def send_packet_with_ack(
16
+ self,
17
+ packet: list,
18
+ packet_dict: dict = None,
19
+ channel_number: int = None,
20
+ attempts: int = 3,
21
+ timeout: float = 5.0,
22
+ ):
23
+ pass
24
+
25
+
26
+ # PacketSender.py
27
+ class PacketSender:
28
+ def __init__(self, socket: socket, coordinator: AckCoordinator, UDP_IP, UDP_PORT):
29
+ self.UDP_IP = UDP_IP
30
+ self.UDP_PORT = UDP_PORT
31
+ self.socket = socket
32
+ self.coordinator = coordinator
33
+ self.command_stacks = {} # Holds the command stacks
34
+ self.last_command_times = {} # Holds the last command times for debouncing
35
+
36
+ async def send_packet(self, packet: list):
37
+ print(f"sending {packet}")
38
+ self.socket.sendto(bytes(packet), (self.UDP_IP, self.UDP_PORT))
39
+
40
+ async def send_packet_with_ack(
41
+ self,
42
+ packet: list,
43
+ packet_dict: dict = None,
44
+ channel_number: int = None,
45
+ attempts: int = 10,
46
+ timeout: float = 0.5,
47
+ debounce_time: float = 0.5, # The debounce time in seconds
48
+ ):
49
+ unique_id = (
50
+ tuple(packet_dict["device_id"]),
51
+ tuple(packet_dict["operation_code"]),
52
+ channel_number,
53
+ )
54
+
55
+ # Add the command to the stack for this unique ID
56
+ if unique_id not in self.command_stacks:
57
+ self.command_stacks[unique_id] = []
58
+ self.command_stacks[unique_id].append(packet)
59
+
60
+ # Only process the last command in the stack
61
+ if packet != self.command_stacks[unique_id][-1]:
62
+ return
63
+
64
+ # If the command is being called too quickly after the last one, ignore it
65
+ if (
66
+ unique_id in self.last_command_times
67
+ and asyncio.get_event_loop().time() - self.last_command_times[unique_id]
68
+ < debounce_time
69
+ ):
70
+ return
71
+
72
+ self.last_command_times[unique_id] = asyncio.get_event_loop().time()
73
+
74
+ event = self.coordinator.create_ack_event(unique_id)
75
+
76
+ for attempt in range(attempts):
77
+ await self.send_packet(packet)
78
+ try:
79
+ await asyncio.wait_for(event.wait(), timeout)
80
+ logging.error(f"ack received for {unique_id}")
81
+ # Remove the command from the stack after it's processed
82
+ self.command_stacks[unique_id].remove(packet)
83
+ return True
84
+ except asyncio.TimeoutError:
85
+ print(
86
+ f"ack not received within {timeout} seconds, attempt {attempt + 1}"
87
+ )
88
+ logging.error(f"ack not received within {timeout} seconds")
89
+
90
+ self.coordinator.remove_ack_event(unique_id)
91
+ logging.error(f"ack not received after {attempts} attempts")
92
+ return False
@@ -0,0 +1,6 @@
1
+ from TISControlProtocol.Protocols.udp.PacketHandlers.BinaryFeedbackHandler import (
2
+ handle_binary_feedback, # noqa: F401
3
+ )
4
+ from TISControlProtocol.Protocols.udp.PacketHandlers.ControlResponseHandler import (
5
+ handle_control_response, # noqa: F401
6
+ )
@@ -0,0 +1,306 @@
1
+ from ctypes import *
2
+
3
+ # -------------------------------------- #
4
+ CRC_TAB = [
5
+ 0x0000,
6
+ 0x1021,
7
+ 0x2042,
8
+ 0x3063,
9
+ 0x4084,
10
+ 0x50A5,
11
+ 0x60C6,
12
+ 0x70E7,
13
+ 0x8108,
14
+ 0x9129,
15
+ 0xA14A,
16
+ 0xB16B,
17
+ 0xC18C,
18
+ 0xD1AD,
19
+ 0xE1CE,
20
+ 0xF1EF,
21
+ 0x1231,
22
+ 0x0210,
23
+ 0x3273,
24
+ 0x2252,
25
+ 0x52B5,
26
+ 0x4294,
27
+ 0x72F7,
28
+ 0x62D6,
29
+ 0x9339,
30
+ 0x8318,
31
+ 0xB37B,
32
+ 0xA35A,
33
+ 0xD3BD,
34
+ 0xC39C,
35
+ 0xF3FF,
36
+ 0xE3DE,
37
+ 0x2462,
38
+ 0x3443,
39
+ 0x0420,
40
+ 0x1401,
41
+ 0x64E6,
42
+ 0x74C7,
43
+ 0x44A4,
44
+ 0x5485,
45
+ 0xA56A,
46
+ 0xB54B,
47
+ 0x8528,
48
+ 0x9509,
49
+ 0xE5EE,
50
+ 0xF5CF,
51
+ 0xC5AC,
52
+ 0xD58D,
53
+ 0x3653,
54
+ 0x2672,
55
+ 0x1611,
56
+ 0x0630,
57
+ 0x76D7,
58
+ 0x66F6,
59
+ 0x5695,
60
+ 0x46B4,
61
+ 0xB75B,
62
+ 0xA77A,
63
+ 0x9719,
64
+ 0x8738,
65
+ 0xF7DF,
66
+ 0xE7FE,
67
+ 0xD79D,
68
+ 0xC7BC,
69
+ 0x48C4,
70
+ 0x58E5,
71
+ 0x6886,
72
+ 0x78A7,
73
+ 0x0840,
74
+ 0x1861,
75
+ 0x2802,
76
+ 0x3823,
77
+ 0xC9CC,
78
+ 0xD9ED,
79
+ 0xE98E,
80
+ 0xF9AF,
81
+ 0x8948,
82
+ 0x9969,
83
+ 0xA90A,
84
+ 0xB92B,
85
+ 0x5AF5,
86
+ 0x4AD4,
87
+ 0x7AB7,
88
+ 0x6A96,
89
+ 0x1A71,
90
+ 0x0A50,
91
+ 0x3A33,
92
+ 0x2A12,
93
+ 0xDBFD,
94
+ 0xCBDC,
95
+ 0xFBBF,
96
+ 0xEB9E,
97
+ 0x9B79,
98
+ 0x8B58,
99
+ 0xBB3B,
100
+ 0xAB1A,
101
+ 0x6CA6,
102
+ 0x7C87,
103
+ 0x4CE4,
104
+ 0x5CC5,
105
+ 0x2C22,
106
+ 0x3C03,
107
+ 0x0C60,
108
+ 0x1C41,
109
+ 0xEDAE,
110
+ 0xFD8F,
111
+ 0xCDEC,
112
+ 0xDDCD,
113
+ 0xAD2A,
114
+ 0xBD0B,
115
+ 0x8D68,
116
+ 0x9D49,
117
+ 0x7E97,
118
+ 0x6EB6,
119
+ 0x5ED5,
120
+ 0x4EF4,
121
+ 0x3E13,
122
+ 0x2E32,
123
+ 0x1E51,
124
+ 0x0E70,
125
+ 0xFF9F,
126
+ 0xEFBE,
127
+ 0xDFDD,
128
+ 0xCFFC,
129
+ 0xBF1B,
130
+ 0xAF3A,
131
+ 0x9F59,
132
+ 0x8F78,
133
+ 0x9188,
134
+ 0x81A9,
135
+ 0xB1CA,
136
+ 0xA1EB,
137
+ 0xD10C,
138
+ 0xC12D,
139
+ 0xF14E,
140
+ 0xE16F,
141
+ 0x1080,
142
+ 0x00A1,
143
+ 0x30C2,
144
+ 0x20E3,
145
+ 0x5004,
146
+ 0x4025,
147
+ 0x7046,
148
+ 0x6067,
149
+ 0x83B9,
150
+ 0x9398,
151
+ 0xA3FB,
152
+ 0xB3DA,
153
+ 0xC33D,
154
+ 0xD31C,
155
+ 0xE37F,
156
+ 0xF35E,
157
+ 0x02B1,
158
+ 0x1290,
159
+ 0x22F3,
160
+ 0x32D2,
161
+ 0x4235,
162
+ 0x5214,
163
+ 0x6277,
164
+ 0x7256,
165
+ 0xB5EA,
166
+ 0xA5CB,
167
+ 0x95A8,
168
+ 0x8589,
169
+ 0xF56E,
170
+ 0xE54F,
171
+ 0xD52C,
172
+ 0xC50D,
173
+ 0x34E2,
174
+ 0x24C3,
175
+ 0x14A0,
176
+ 0x0481,
177
+ 0x7466,
178
+ 0x6447,
179
+ 0x5424,
180
+ 0x4405,
181
+ 0xA7DB,
182
+ 0xB7FA,
183
+ 0x8799,
184
+ 0x97B8,
185
+ 0xE75F,
186
+ 0xF77E,
187
+ 0xC71D,
188
+ 0xD73C,
189
+ 0x26D3,
190
+ 0x36F2,
191
+ 0x0691,
192
+ 0x16B0,
193
+ 0x6657,
194
+ 0x7676,
195
+ 0x4615,
196
+ 0x5634,
197
+ 0xD94C,
198
+ 0xC96D,
199
+ 0xF90E,
200
+ 0xE92F,
201
+ 0x99C8,
202
+ 0x89E9,
203
+ 0xB98A,
204
+ 0xA9AB,
205
+ 0x5844,
206
+ 0x4865,
207
+ 0x7806,
208
+ 0x6827,
209
+ 0x18C0,
210
+ 0x08E1,
211
+ 0x3882,
212
+ 0x28A3,
213
+ 0xCB7D,
214
+ 0xDB5C,
215
+ 0xEB3F,
216
+ 0xFB1E,
217
+ 0x8BF9,
218
+ 0x9BD8,
219
+ 0xABBB,
220
+ 0xBB9A,
221
+ 0x4A75,
222
+ 0x5A54,
223
+ 0x6A37,
224
+ 0x7A16,
225
+ 0x0AF1,
226
+ 0x1AD0,
227
+ 0x2AB3,
228
+ 0x3A92,
229
+ 0xFD2E,
230
+ 0xED0F,
231
+ 0xDD6C,
232
+ 0xCD4D,
233
+ 0xBDAA,
234
+ 0xAD8B,
235
+ 0x9DE8,
236
+ 0x8DC9,
237
+ 0x7C26,
238
+ 0x6C07,
239
+ 0x5C64,
240
+ 0x4C45,
241
+ 0x3CA2,
242
+ 0x2C83,
243
+ 0x1CE0,
244
+ 0x0CC1,
245
+ 0xEF1F,
246
+ 0xFF3E,
247
+ 0xCF5D,
248
+ 0xDF7C,
249
+ 0xAF9B,
250
+ 0xBFBA,
251
+ 0x8FD9,
252
+ 0x9FF8,
253
+ 0x6E17,
254
+ 0x7E36,
255
+ 0x4E55,
256
+ 0x5E74,
257
+ 0x2E93,
258
+ 0x3EB2,
259
+ 0x0ED1,
260
+ 0x1EF0,
261
+ ]
262
+
263
+
264
+ # -------------------------------------- #
265
+ def bytes(intParam):
266
+ return divmod(intParam, 0x100)
267
+
268
+
269
+ # -------------------------------------- #
270
+ def packCRC(ptr):
271
+ crc = c_ushort(0)
272
+ for i in range(len(ptr) - 16):
273
+ data = c_ubyte(crc.value >> 8)
274
+ crc.value <<= 8
275
+ reg = c_ubyte(data.value ^ ptr[i + 16])
276
+ crc = c_ushort(crc.value ^ CRC_TAB[reg.value])
277
+ crcValueH, crcValueL = bytes(crc.value)
278
+ ptr.append(crcValueH)
279
+ ptr.append(crcValueL)
280
+ return ptr
281
+
282
+
283
+ # -------------------------------------- #
284
+ def checkCRC(ptr):
285
+ crcValueL = ptr.pop()
286
+ crcValueH = ptr.pop()
287
+ targPtr = packCRC(ptr)
288
+ if (targPtr[len(targPtr) - 1] == crcValueL) and (
289
+ targPtr[len(targPtr) - 2] == crcValueH
290
+ ):
291
+ return True
292
+ else:
293
+ return False
294
+
295
+
296
+ # -------------------------------------- #
297
+ # toPack = [0x0f,0x01,0xfe,0xff,0xfe,0x00,0x31,0x01,0x1f,0x01,0x64,0x00,0x00] # ,0x5d ,0x8f]
298
+ # toCheck = [0x0f,0x01,0xfe,0xff,0xfe,0x00,0x31,0x01,0x1f,0x01,0x64,0x00,0x00, 0x5d,0x8f]
299
+ # toPack = [0x0F, 0xBB, 0xBB, 0xCC, 0xB1, 0x00, 0x31, 0x01, 0x2b, 0x02, 0x64, 0x00, 0x00]
300
+ # toCheck = [0x0F, 0xBB, 0xBB, 0xCC, 0xB1, 0x00, 0x31, 0x01, 0x2b, 0x02, 0x64, 0x00, 0x00, 0xBB, 0xAA]
301
+ # checkCRC(toCheck)
302
+ # toPack = [0x0f,0xbb,0xbb,0xcc,0xb1,0x00,0x31,0x01,0x2b,0x03,0x64,0x00,0x00]
303
+ # toCheck = [0x0f,0xbb,0xbb,0xcc,0xb1,0x00,0x31,0x01,0x2b,0x03,0x64,0x00,0x00,0xcd,0x1e]
304
+ # toCheck = [0xc0,0xa8,0x01,0x05,0x53,0x4d,0x41,0x52,0x54,0x43,0x4c,0x4f,0x55,0x44,0xaa,0xaa,0x0f,0xbb,0xbb,0xcc,0xb1,0x00,0x31,0x01,0x2b,0x03,0x64,0x00,0x00,0xcd,0x1e]
305
+ # checkCRC(toCheck)
306
+ # -------------------------------------- #
@@ -0,0 +1,21 @@
1
+ appliances_dict = {}
2
+ mqtt_appliances_dict = {}
3
+ ack_events = {}
4
+ loggers = {}
5
+
6
+
7
+ def get_appliance(device_id: tuple, channel: tuple, appliances_dict: dict):
8
+ # appliance key form is ((device_id),(channels))
9
+ try:
10
+ device_appliances = [
11
+ a
12
+ for k, a in appliances_dict.items()
13
+ if k[0] == device_id and channel in k[1]
14
+ ]
15
+ return tuple(device_appliances)[0]
16
+ except IndexError:
17
+ print(f"No appliances found for key {(device_id,channel)}")
18
+ return None
19
+ except Exception as e:
20
+ print(f"An error occurred: {e}")
21
+ return None
@@ -0,0 +1,13 @@
1
+ from TISControlProtocol.BytesHelper import build_packet
2
+
3
+ x = build_packet(
4
+ [0x01, 0x01],
5
+ "1111.111.1.1",
6
+ "11:11:11:11:11:11:11:11",
7
+ "11:11:11:11:11:11:11:11",
8
+ [0x01, 0x01],
9
+ [0x01, 0x01],
10
+ [0x01, 0x01],
11
+ )
12
+
13
+ print(x)