dScriptModule 3.4.5__py3-none-any.whl → 3.5.1__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.
- dScriptModule/__init__.py +1 -1
- dScriptModule/dScriptBoard.py +76 -38
- dScriptModule/dScriptObject.py +19 -8
- dScriptModule/dScriptServer.py +49 -30
- dScriptModule/dScriptVirtualBoard.py +1 -1
- {dScriptModule-3.4.5.dist-info → dscriptmodule-3.5.1.dist-info}/METADATA +13 -6
- dscriptmodule-3.5.1.dist-info/RECORD +11 -0
- {dScriptModule-3.4.5.dist-info → dscriptmodule-3.5.1.dist-info}/WHEEL +1 -1
- dScriptModule-3.4.5.dist-info/RECORD +0 -11
- {dScriptModule-3.4.5.dist-info → dscriptmodule-3.5.1.dist-info/licenses}/LICENSE +0 -0
- {dScriptModule-3.4.5.dist-info → dscriptmodule-3.5.1.dist-info}/top_level.txt +0 -0
dScriptModule/__init__.py
CHANGED
dScriptModule/dScriptBoard.py
CHANGED
|
@@ -12,6 +12,7 @@ from .dScriptObject import *
|
|
|
12
12
|
import asyncio
|
|
13
13
|
import struct
|
|
14
14
|
import socket
|
|
15
|
+
import threading
|
|
15
16
|
from Crypto import Random
|
|
16
17
|
from Crypto.Cipher import AES
|
|
17
18
|
|
|
@@ -41,12 +42,16 @@ class dScriptBoard(dScriptObject):
|
|
|
41
42
|
|
|
42
43
|
_EventHandlers = { 'status':[], 'config':[], 'light':[], 'shutter':[], 'socket':[], 'motion':[], 'button':[] }
|
|
43
44
|
__loop = None
|
|
45
|
+
__async_lock = None
|
|
46
|
+
__sync_lock = None
|
|
44
47
|
|
|
45
48
|
'''Initialize the dScriptBoard element with at least its IP and port to be able to connect later'''
|
|
46
49
|
def __init__(self, TCP_IP, TCP_PORT=17123, PROTOCOL='binary') -> None:
|
|
47
50
|
_LOGGER.debug("dScriptBoard - %s:%s: __init__", TCP_IP, TCP_PORT)
|
|
48
51
|
self.IP = TCP_IP
|
|
49
52
|
self.Port = TCP_PORT
|
|
53
|
+
self.__async_lock = None # created on first use within the running event loop
|
|
54
|
+
self.__sync_lock = threading.Lock()
|
|
50
55
|
self.SetProtocol(PROTOCOL)
|
|
51
56
|
self.GetHostName()
|
|
52
57
|
|
|
@@ -80,7 +85,7 @@ class dScriptBoard(dScriptObject):
|
|
|
80
85
|
if self._Protocol == self._Protocols[4] and self._IsInList(msg[0], self._AESNonceCommands): #BinaryAES and command with NONCE
|
|
81
86
|
msg = await self._async_AESEncrypt(msg)
|
|
82
87
|
data = await self.__async_Send(msg,16) #BinaryAES commands always return 16 bytes
|
|
83
|
-
data = await self.
|
|
88
|
+
data = await self._async_AESDecrypt(data,self._GetKeyByValue(command,self._DecimalCommands))
|
|
84
89
|
return data[:self._BinaryReturnByteCounts[command]] # return only the number of bytes usually returend by Binary protocol
|
|
85
90
|
elif self._Protocol == self._Protocols[3] or self._Protocol == self._Protocols[4]: #Binary or BinaryAES but command without NONCE
|
|
86
91
|
return await self.__async_Send(msg,self._BinaryReturnByteCounts[command])
|
|
@@ -193,52 +198,85 @@ class dScriptBoard(dScriptObject):
|
|
|
193
198
|
'''Send a message to the board'''
|
|
194
199
|
def __Send(self,msg,buff):
|
|
195
200
|
_LOGGER.debug("dScriptBoard - %s: Send: %s | %s", self.friendlyname, msg, buff)
|
|
201
|
+
if self.__sync_lock is None:
|
|
202
|
+
self.__sync_lock = threading.Lock()
|
|
203
|
+
with self.__sync_lock: # the board firmware processes one request at a time - never send in parallel
|
|
204
|
+
s = None
|
|
205
|
+
try:
|
|
206
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
207
|
+
s.settimeout(self.ConnectionTimeout)
|
|
208
|
+
s.connect((self.IP, self.Port))
|
|
209
|
+
s.sendall(msg)
|
|
210
|
+
data = b''
|
|
211
|
+
while len(data) < buff:
|
|
212
|
+
try:
|
|
213
|
+
chunk = s.recv(buff - len(data))
|
|
214
|
+
except socket.timeout:
|
|
215
|
+
if data: break # board sent less than expected - return what we got
|
|
216
|
+
raise
|
|
217
|
+
if not chunk: break
|
|
218
|
+
data = data + chunk
|
|
219
|
+
return data
|
|
220
|
+
except Exception as e:
|
|
221
|
+
_LOGGER.error("dScriptBoard - %s: Send failed: %s (%s.%s)", self.friendlyname, str(e), e.__class__.__module__, type(e).__name__)
|
|
222
|
+
return False
|
|
223
|
+
finally:
|
|
224
|
+
if s is not None:
|
|
225
|
+
try: s.close()
|
|
226
|
+
except Exception: pass
|
|
227
|
+
|
|
228
|
+
'''Close an asyncio stream writer without raising'''
|
|
229
|
+
async def __async_CloseWriter(self,writer):
|
|
230
|
+
if writer is None:
|
|
231
|
+
return
|
|
196
232
|
try:
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
_LOGGER.error("dScriptBoard - %s: Send failed: %s (%s.%s)", self.friendlyname, str(e), e.__class__.__module__, type(e).__name__)
|
|
202
|
-
return False
|
|
203
|
-
try:
|
|
204
|
-
s.send(msg)
|
|
205
|
-
data = s.recv(buff)
|
|
206
|
-
except Exception as e:
|
|
207
|
-
_LOGGER.error("dScriptBoard - %s: Send failed: %s (%s.%s)", self.friendlyname, str(e), e.__class__.__module__, type(e).__name__)
|
|
208
|
-
s.close()
|
|
209
|
-
return False
|
|
210
|
-
s.close()
|
|
211
|
-
return data
|
|
233
|
+
writer.close()
|
|
234
|
+
await asyncio.wait_for(writer.wait_closed(), timeout=self.ConnectionTimeout)
|
|
235
|
+
except Exception:
|
|
236
|
+
pass
|
|
212
237
|
|
|
213
238
|
'''Send a message to the board'''
|
|
214
239
|
async def __async_Send(self,msg,buff,retry=False):
|
|
215
240
|
_LOGGER.debug("dScriptBoard - %s: async_Send: %s | %s", self.friendlyname, msg, buff)
|
|
241
|
+
if self.__async_lock is None:
|
|
242
|
+
self.__async_lock = asyncio.Lock()
|
|
243
|
+
async with self.__async_lock: # the board firmware processes one request at a time - never send in parallel
|
|
244
|
+
data = await self.__async_SendUnlocked(msg,buff)
|
|
245
|
+
if data is None and not retry:
|
|
246
|
+
_LOGGER.debug("dScriptBoard - %s: async_Send retry sending: %s | %s", self.friendlyname, msg, buff)
|
|
247
|
+
return await self.__async_Send(msg,buff,True)
|
|
248
|
+
elif data is None:
|
|
249
|
+
_LOGGER.error("dScriptBoard - %s: async_Send failed with retry: %s | %s", self.friendlyname, msg, buff)
|
|
250
|
+
return False
|
|
251
|
+
return data
|
|
252
|
+
|
|
253
|
+
'''Send a message to the board without locking - returns None on timeout to allow a retry'''
|
|
254
|
+
async def __async_SendUnlocked(self,msg,buff):
|
|
255
|
+
writer = None
|
|
216
256
|
try:
|
|
217
|
-
writer = None
|
|
218
257
|
if self.__loop is not None:
|
|
219
258
|
_LOGGER.warning("dScriptBoard - %s: async_Send loop parameter is no longer forwarded as of python 3.10", self.friendlyname )
|
|
220
|
-
|
|
221
|
-
reader, writer = await asyncio.open_connection(self.IP, self.Port)
|
|
259
|
+
reader, writer = await asyncio.wait_for(asyncio.open_connection(self.IP, self.Port), timeout=self.ConnectionTimeout)
|
|
222
260
|
writer.write(msg)
|
|
223
|
-
|
|
224
|
-
|
|
261
|
+
await asyncio.wait_for(writer.drain(), timeout=self.ConnectionTimeout)
|
|
262
|
+
data = b''
|
|
263
|
+
while len(data) < buff:
|
|
264
|
+
try:
|
|
265
|
+
chunk = await asyncio.wait_for(reader.read(buff - len(data)), timeout=self.ConnectionTimeout)
|
|
266
|
+
except asyncio.TimeoutError:
|
|
267
|
+
if data: break # board sent less than expected - return what we got
|
|
268
|
+
raise
|
|
269
|
+
if not chunk: break
|
|
270
|
+
data = data + chunk
|
|
225
271
|
return data
|
|
226
|
-
except TimeoutError as e:
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if writer:
|
|
230
|
-
writer.close()
|
|
231
|
-
await self.__async_Send(msg,buff,True)
|
|
232
|
-
else:
|
|
233
|
-
_LOGGER.error("dScriptBoard - %s: async_Send failed with retry: %s (%s.%s)", self.friendlyname, str(e), e.__class__.__module__, type(e).__name__)
|
|
234
|
-
if not write is None:
|
|
235
|
-
writer.close()
|
|
236
|
-
return False
|
|
272
|
+
except asyncio.TimeoutError as e:
|
|
273
|
+
_LOGGER.debug("dScriptBoard - %s: async_Send timeout: %s (%s.%s)", self.friendlyname, str(e), e.__class__.__module__, type(e).__name__)
|
|
274
|
+
return None
|
|
237
275
|
except Exception as e:
|
|
238
276
|
_LOGGER.error("dScriptBoard - %s: async_Send failed: %s (%s.%s)", self.friendlyname, str(e), e.__class__.__module__, type(e).__name__)
|
|
239
|
-
if not write is None:
|
|
240
|
-
writer.close()
|
|
241
277
|
return False
|
|
278
|
+
finally:
|
|
279
|
+
await self.__async_CloseWriter(writer)
|
|
242
280
|
|
|
243
281
|
'''Check if identifier parameter is valid'''
|
|
244
282
|
def __CheckIdentifier(self,identifier,idtype) -> bool:
|
|
@@ -304,7 +342,7 @@ class dScriptBoard(dScriptObject):
|
|
|
304
342
|
'''Initialize the board and write its results as attributes'''
|
|
305
343
|
async def async_InitBoard(self) -> None:
|
|
306
344
|
_LOGGER.debug("dScriptBoard - %s: async_InitBoard", self.friendlyname)
|
|
307
|
-
self.GetHostName
|
|
345
|
+
await asyncio.get_running_loop().run_in_executor(None, self.GetHostName)
|
|
308
346
|
await self.async_GetStatus()
|
|
309
347
|
await self.async_GetConfig()
|
|
310
348
|
|
|
@@ -320,7 +358,7 @@ class dScriptBoard(dScriptObject):
|
|
|
320
358
|
databits=self._ToDataBits(data)
|
|
321
359
|
|
|
322
360
|
_LOGGER.info("dScriptBoard - %s: GetStatus: update board status information", self.friendlyname)
|
|
323
|
-
self._ModuleID=self._Modules[databytes[0]
|
|
361
|
+
self._ModuleID=self._Modules.get(databytes[0], 'Unknown (%s)' % databytes[0])
|
|
324
362
|
self._SystemFirmwareMajor=databytes[1]
|
|
325
363
|
self._SystemFirmwareMinor=databytes[2]
|
|
326
364
|
self._ApplicationFirmwareMajor=databytes[3]
|
|
@@ -343,7 +381,7 @@ class dScriptBoard(dScriptObject):
|
|
|
343
381
|
databits=self._ToDataBits(data)
|
|
344
382
|
|
|
345
383
|
_LOGGER.info("dScriptBoard - %s: async_GetStatus: update board status information", self.friendlyname)
|
|
346
|
-
self._ModuleID=self._Modules[databytes[0]
|
|
384
|
+
self._ModuleID=self._Modules.get(databytes[0], 'Unknown (%s)' % databytes[0])
|
|
347
385
|
self._SystemFirmwareMajor=databytes[1]
|
|
348
386
|
self._SystemFirmwareMinor=databytes[2]
|
|
349
387
|
self._ApplicationFirmwareMajor=databytes[3]
|
|
@@ -666,7 +704,7 @@ class dScriptBoard(dScriptObject):
|
|
|
666
704
|
databytes=self._ToDataBytes(data)
|
|
667
705
|
return self.__CheckSet(self._ToDataBytes(data)[0])
|
|
668
706
|
except Exception as e:
|
|
669
|
-
_LOGGER.error("dScriptBoard - %s: async_SetLight failed: %s (%s.%s)", str(e), e.__class__.__module__, type(e).__name__)
|
|
707
|
+
_LOGGER.error("dScriptBoard - %s: async_SetLight failed: %s (%s.%s)", self.friendlyname, str(e), e.__class__.__module__, type(e).__name__)
|
|
670
708
|
|
|
671
709
|
'''Execute the SH, SetShutter command to define a shutter status'''
|
|
672
710
|
def SetShutter(self,identifier,state) -> None | bool:
|
dScriptModule/dScriptObject.py
CHANGED
|
@@ -40,8 +40,8 @@ class dScriptObject(object):
|
|
|
40
40
|
'GetConfig':12, 'GetLight':1, 'GetShutter':2, 'GetSocket':1, 'GetMotion':1, 'GetButton':1,
|
|
41
41
|
'HeartBeat':0, 'TestOnline':0, 'StopServer':0, 'BoardRestart':0}
|
|
42
42
|
|
|
43
|
-
_Modules = {30:'dS3484', 31:'dS1242', 34:'dS2824', 35:'dS378'}
|
|
44
|
-
_ModulesConfig = {30:{'_PhysicalRelays':4,}, 31:{'
|
|
43
|
+
_Modules = {30:'dS3484', 31:'dS1242', 34:'dS2824', 35:'dS378', 36:'TCP184', 42:'dS2832'}
|
|
44
|
+
_ModulesConfig = {30:{'_PhysicalRelays':4,}, 31:{'_PhysicalRelays':2}, 34:{'_PhysicalRelays':24}, 35:{'_PhysicalRelays':8}, 36:{'_PhysicalRelays':4}}
|
|
45
45
|
_OnOffStates = {0:'off', 1:'on', 2:'toggle'}
|
|
46
46
|
_ShutterStates = {0:'stopped', 1:'opening', 2:'closing'}
|
|
47
47
|
|
|
@@ -66,6 +66,12 @@ class dScriptObject(object):
|
|
|
66
66
|
return key
|
|
67
67
|
return None
|
|
68
68
|
|
|
69
|
+
'''Return the event handlers of this instance (class level dict is only the template of known topics)'''
|
|
70
|
+
def _GetEventHandlers(self):
|
|
71
|
+
if '_EventHandlers' not in self.__dict__:
|
|
72
|
+
self._EventHandlers = { topic:[] for topic in type(self)._EventHandlers.keys() }
|
|
73
|
+
return self._EventHandlers
|
|
74
|
+
|
|
69
75
|
'''Translate received set of data into byte array'''
|
|
70
76
|
def _ToDataBytes(self,data):
|
|
71
77
|
#_LOGGER.debug("dScriptObject: _ToDataBytes: %s",data)
|
|
@@ -83,30 +89,35 @@ class dScriptObject(object):
|
|
|
83
89
|
def addEventHandler(self, topic, handler):
|
|
84
90
|
#_LOGGER.debug("dScriptObject: addEventHandler")
|
|
85
91
|
topic=topic.lower()
|
|
86
|
-
|
|
92
|
+
handlers=self._GetEventHandlers()
|
|
93
|
+
if not self._IsInList(topic,handlers.keys()):
|
|
87
94
|
raise Exception("Unknown event handler topic: %s", topic)
|
|
88
95
|
return False
|
|
89
|
-
|
|
96
|
+
if not handler in handlers[topic]: # never register the same handler twice
|
|
97
|
+
handlers[topic].append(handler)
|
|
90
98
|
return True
|
|
91
99
|
|
|
92
100
|
def removeEventHandler(self, topic, handler):
|
|
93
101
|
#_LOGGER.debug("dScriptObject: removeEventHandler")
|
|
94
102
|
topic=topic.lower()
|
|
95
|
-
|
|
103
|
+
handlers=self._GetEventHandlers()
|
|
104
|
+
if not self._IsInList(topic,handlers.keys()):
|
|
96
105
|
raise Exception("Unknown event handler topic: %s", topic)
|
|
97
106
|
return False
|
|
98
|
-
|
|
107
|
+
if handler in handlers[topic]:
|
|
108
|
+
handlers[topic].remove(handler)
|
|
99
109
|
return True
|
|
100
110
|
|
|
101
111
|
def _throwEvent(self, sender=IP, topic='topic', identifier=None, value=None):
|
|
102
112
|
_LOGGER.debug("dScriptObject: _throwEvent: from %s about %s for %s with value %s", sender, topic, identifier, value)
|
|
103
113
|
topic=topic.lower()
|
|
104
|
-
|
|
114
|
+
handlers=self._GetEventHandlers()
|
|
115
|
+
if not self._IsInList(topic,handlers.keys()):
|
|
105
116
|
raise Exception("Unknown event handler topic: %s", topic)
|
|
106
117
|
return False
|
|
107
118
|
|
|
108
119
|
eventobj = dScriptEventObj(sender,topic,identifier,value)
|
|
109
|
-
for handler in
|
|
120
|
+
for handler in handlers[topic]:
|
|
110
121
|
eventobj.event += handler
|
|
111
122
|
#_LOGGER.debug("dScriptObject: _throwEvent: object: %s", eventobj)
|
|
112
123
|
eventobj.throw()
|
dScriptModule/dScriptServer.py
CHANGED
|
@@ -42,8 +42,8 @@ class dScriptServer(dScriptObject):
|
|
|
42
42
|
raise Exception("Server already started")
|
|
43
43
|
return False
|
|
44
44
|
try:
|
|
45
|
-
self.__mode = '
|
|
46
|
-
self.__thread = Thread(target=self.__ServerThread)
|
|
45
|
+
self.__mode = 'sync'
|
|
46
|
+
self.__thread = Thread(target=self.__ServerThread, daemon=True)
|
|
47
47
|
self.__thread.start()
|
|
48
48
|
_LOGGER.debug("dScriptServer - %s:%s: StartServer - completed", self.IP, self.Port)
|
|
49
49
|
except Exception as e:
|
|
@@ -67,7 +67,7 @@ class dScriptServer(dScriptObject):
|
|
|
67
67
|
self.__server = server_coro
|
|
68
68
|
_LOGGER.info("dScriptServer - %s:%s: async_StartServer - serving on: %s:%s", self.IP, self.Port, self.IP, self.Port)
|
|
69
69
|
self.State = self.__server.is_serving()
|
|
70
|
-
self.__mode
|
|
70
|
+
self.__mode = 'async'
|
|
71
71
|
_LOGGER.debug("dScriptServer - %s:%s: async_StartServer - completed", self.IP, self.Port)
|
|
72
72
|
except Exception as e:
|
|
73
73
|
_LOGGER.error("dScriptServer - %s:%s: async_StartServer failed: %s (%s.%s)", self.IP, self.Port, str(e), e.__class__.__module__, type(e).__name__)
|
|
@@ -85,8 +85,8 @@ class dScriptServer(dScriptObject):
|
|
|
85
85
|
raise Exception("Server already running")
|
|
86
86
|
return False
|
|
87
87
|
try:
|
|
88
|
-
self.__mode = '
|
|
89
|
-
self.__thread = Thread(target=self.__ServerThread_async)
|
|
88
|
+
self.__mode = 'thread'
|
|
89
|
+
self.__thread = Thread(target=self.__ServerThread_async, daemon=True)
|
|
90
90
|
self.__thread.start()
|
|
91
91
|
_LOGGER.debug("dScriptServer - %s:%s: StartServer_async_thread - completed", self.IP, self.Port)
|
|
92
92
|
except Exception as e:
|
|
@@ -98,16 +98,20 @@ class dScriptServer(dScriptObject):
|
|
|
98
98
|
return self.async_StopServer()
|
|
99
99
|
elif self.__mode == 'thread':
|
|
100
100
|
return self.StopServer_async()
|
|
101
|
-
if self.__server
|
|
102
|
-
self.__server.shutdown(socket.SHUT_RDWR)
|
|
101
|
+
if self.__server is None:
|
|
103
102
|
raise Exception("Server already stopped")
|
|
103
|
+
self.State = False
|
|
104
|
+
try:
|
|
105
|
+
self.__server.shutdown(socket.SHUT_RDWR) # wakes up the thread blocked in accept()
|
|
106
|
+
except Exception:
|
|
107
|
+
pass
|
|
104
108
|
try:
|
|
105
109
|
self.__server.close()
|
|
106
110
|
except Exception as e:
|
|
107
111
|
_LOGGER.error("dScriptServer: StopServer: Exception on server socket close: %s (%s.%s)", str(e), e.__class__.__module__, type(e).__name__)
|
|
108
112
|
pass
|
|
109
113
|
self.State = False
|
|
110
|
-
self.__mode
|
|
114
|
+
self.__mode = None
|
|
111
115
|
self.__server = None
|
|
112
116
|
self.__thread = None
|
|
113
117
|
|
|
@@ -125,7 +129,7 @@ class dScriptServer(dScriptObject):
|
|
|
125
129
|
_LOGGER.error("dScriptServer - %s:%s: async_StopServer failed: %s (%s.%s)", self.IP, self.Port, str(e), e.__class__.__module__, type(e).__name__)
|
|
126
130
|
pass
|
|
127
131
|
self.State = False
|
|
128
|
-
self.__mode
|
|
132
|
+
self.__mode = None
|
|
129
133
|
self.__server = None
|
|
130
134
|
|
|
131
135
|
def StopServer_async(self):
|
|
@@ -145,7 +149,7 @@ class dScriptServer(dScriptObject):
|
|
|
145
149
|
_LOGGER.error("dScriptServer - %s:%s: StopServer_async_thread failed: %s (%s.%s)", self.IP, self.Port, str(e), e.__class__.__module__, type(e).__name__)
|
|
146
150
|
pass
|
|
147
151
|
self.State = False
|
|
148
|
-
self.__mode
|
|
152
|
+
self.__mode = None
|
|
149
153
|
self.__server = None
|
|
150
154
|
self.__thread = None
|
|
151
155
|
|
|
@@ -172,11 +176,12 @@ class dScriptServer(dScriptObject):
|
|
|
172
176
|
while self.State:
|
|
173
177
|
clientsocket, addr = self.__server.accept() # Establish connection with client.
|
|
174
178
|
_LOGGER.debug("dScriptServer - %s:%s: ServerThread: client connected: %s", self.IP, self.Port, addr)
|
|
179
|
+
clientsocket.settimeout(10) # never block a client thread forever
|
|
175
180
|
_thread.start_new_thread(self.__ClientConnected,(clientsocket,addr))
|
|
176
181
|
except Exception as e:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
182
|
+
if self.State:
|
|
183
|
+
_LOGGER.error("dScriptServer - %s:%s: ServerThread failed on client connect: %s (%s.%s)", self.IP, self.Port, str(e), e.__class__.__module__, type(e).__name__)
|
|
184
|
+
self.StopServer()
|
|
180
185
|
_LOGGER.debug("dScriptServer - %s:%s: ServerThread - stopped serving on: (%s:%s)", self.IP, self.Port, self.IP, self.Port)
|
|
181
186
|
|
|
182
187
|
def __ServerThread_async(self):
|
|
@@ -197,11 +202,10 @@ class dScriptServer(dScriptObject):
|
|
|
197
202
|
try:
|
|
198
203
|
await self.__server.start_serving()
|
|
199
204
|
self.State = self.__server.is_serving()
|
|
200
|
-
self.__mode == 'thread'
|
|
201
205
|
_LOGGER.debug("dScriptServer - %s:%s: async_RunServerThread - started serving (%s) on: %s", self.IP, self.Port, self.__server.is_serving(), addr)
|
|
202
206
|
while self.State:
|
|
203
207
|
self.State = self.__server.is_serving()
|
|
204
|
-
await asyncio.sleep(
|
|
208
|
+
await asyncio.sleep(1) # do not spin - this is only a status watcher
|
|
205
209
|
_LOGGER.debug("dScriptServer - %s:%s: async_RunServerThread - stopped serving on: %s", self.IP, self.Port, addr)
|
|
206
210
|
except Exception as e:
|
|
207
211
|
_LOGGER.error("dScriptServer - %s:%s: async_RunServerThread failed: %s (%s.%s)", self.IP, self.Port, str(e), e.__class__.__module__, type(e).__name__)
|
|
@@ -210,31 +214,34 @@ class dScriptServer(dScriptObject):
|
|
|
210
214
|
def __ClientConnected(self,clientsocket,addr):
|
|
211
215
|
_LOGGER.debug("dScriptServer - %s:%s: ClientConnected: %s | %s", self.IP, self.Port, clientsocket, addr)
|
|
212
216
|
try:
|
|
213
|
-
data = clientsocket.recv(
|
|
217
|
+
data = clientsocket.recv(self.__socketsize) # firmware sends 3 bytes (cmd, id, state)
|
|
214
218
|
_LOGGER.debug("dScriptServer - %s:%s: ClientConnected: received data from: %s", self.IP, self.Port, addr)
|
|
215
219
|
|
|
216
|
-
clientsocket.close()
|
|
217
220
|
_LOGGER.debug("dScriptServer - %s:%s: ClientConnected: closed connection: %s", self.IP, self.Port, addr)
|
|
218
221
|
except Exception as e:
|
|
219
222
|
_LOGGER.error("dScriptServer - %s:%s: ClientConnected failed: %s (%s.%s)", self.IP, self.Port, str(e), e.__class__.__module__, type(e).__name__)
|
|
220
|
-
#clientsocket.shutdown(socket.SHUT_RDWR)
|
|
221
223
|
return False
|
|
224
|
+
finally:
|
|
225
|
+
try: clientsocket.close()
|
|
226
|
+
except Exception: pass
|
|
222
227
|
self.__InterpreteData(data,addr[0])
|
|
223
228
|
|
|
224
229
|
async def __async_ClientConnected(self, reader, writer):
|
|
225
230
|
_LOGGER.debug("dScriptServer - %s:%s: async_ClientConnected: %s | %s", self.IP, self.Port, reader, writer)
|
|
226
231
|
try:
|
|
227
|
-
data = await reader.read(self.__socketsize)
|
|
228
|
-
#message = data.decode()
|
|
229
232
|
addr = writer.get_extra_info('peername')
|
|
233
|
+
data = await asyncio.wait_for(reader.read(self.__socketsize), timeout=10) # never wait forever for a (broken) client
|
|
230
234
|
_LOGGER.debug("dScriptServer - %s:%s: async_ClientConnected: received data from: %s", self.IP, self.Port, addr)
|
|
231
|
-
|
|
232
|
-
writer.close()
|
|
233
|
-
await asyncio.sleep(0)
|
|
234
|
-
_LOGGER.debug("dScriptServer - %s:%s: async_ClientConnected: closed connection: %s", self.IP, self.Port, addr)
|
|
235
235
|
except Exception as e:
|
|
236
236
|
_LOGGER.error("dScriptServer - %s:%s: async_ClientConnected failed: %s (%s.%s)", self.IP, self.Port, str(e), e.__class__.__module__, type(e).__name__)
|
|
237
237
|
return False
|
|
238
|
+
finally:
|
|
239
|
+
try:
|
|
240
|
+
writer.close()
|
|
241
|
+
await asyncio.wait_for(writer.wait_closed(), timeout=5)
|
|
242
|
+
except Exception:
|
|
243
|
+
pass
|
|
244
|
+
_LOGGER.debug("dScriptServer - %s:%s: async_ClientConnected: closed connection: %s", self.IP, self.Port, writer.get_extra_info('peername'))
|
|
238
245
|
self.__InterpreteData(data,addr[0])
|
|
239
246
|
|
|
240
247
|
def __InterpreteData(self,data,SenderIP):
|
|
@@ -242,8 +249,15 @@ class dScriptServer(dScriptObject):
|
|
|
242
249
|
#TO-DO: identify protocol & select according action
|
|
243
250
|
#if self._Protocol == self._Protocols[4]: #BinaryAES
|
|
244
251
|
# data=self._AESDecrypt(data)
|
|
245
|
-
|
|
246
|
-
|
|
252
|
+
try:
|
|
253
|
+
databytes=self._ToDataBytes(data)
|
|
254
|
+
if len(databytes) == 0:
|
|
255
|
+
_LOGGER.debug("dScriptServer - %s:%s: InterpreteData: empty message from %s", self.IP, self.Port, SenderIP)
|
|
256
|
+
return False
|
|
257
|
+
self.__InterpreteBinary(databytes,SenderIP)
|
|
258
|
+
except Exception as e:
|
|
259
|
+
_LOGGER.error("dScriptServer - %s:%s: InterpreteData failed for %s from %s: %s (%s.%s)", self.IP, self.Port, data, SenderIP, str(e), e.__class__.__module__, type(e).__name__)
|
|
260
|
+
return False
|
|
247
261
|
|
|
248
262
|
def __InterpreteBinary(self,databytes,SenderIP):
|
|
249
263
|
_LOGGER.debug("dScriptServer - %s:%s: InterpreteBinary: %s | %s", self.IP, self.Port, databytes, SenderIP)
|
|
@@ -258,13 +272,18 @@ class dScriptServer(dScriptObject):
|
|
|
258
272
|
elif cmd == 'heartbeat' or cmd == 'getstatus' or cmd == 'getconfig' or cmd == 'testonline': #all of these do not need an identifier
|
|
259
273
|
self._throwEvent(SenderIP, cmd)
|
|
260
274
|
else:
|
|
261
|
-
if len(databytes)
|
|
262
|
-
if int(databytes[2]) < 0:
|
|
275
|
+
if len(databytes) >= 3:
|
|
276
|
+
if int(databytes[2]) < 0 or int(databytes[2]) == 255: # firmware sends -1 (=255 as unsigned byte) for 'no state'
|
|
263
277
|
self._throwEvent(SenderIP, cmd, int(databytes[1]))
|
|
264
278
|
elif cmd == 'getlight' or cmd == 'getsocket' or cmd == 'getmotion' or cmd == 'getrelay' or cmd == 'getinput':
|
|
265
|
-
|
|
279
|
+
if databytes[2] in self._OnOffStates:
|
|
280
|
+
self._throwEvent(SenderIP, cmd, int(databytes[1]), self._OnOffStates[databytes[2]])
|
|
281
|
+
else:
|
|
282
|
+
self._throwEvent(SenderIP, cmd, int(databytes[1]))
|
|
266
283
|
else:
|
|
267
284
|
self._throwEvent(SenderIP, cmd, int(databytes[1]), int(databytes[2]))
|
|
268
|
-
|
|
285
|
+
elif len(databytes) == 2:
|
|
269
286
|
self._throwEvent(SenderIP, cmd, int(databytes[1]))
|
|
287
|
+
else:
|
|
288
|
+
_LOGGER.debug("dScriptServer - %s:%s: InterpreteBinary: missing identifier for %s from %s", self.IP, self.Port, cmd, SenderIP)
|
|
270
289
|
#return True
|
|
@@ -20,7 +20,7 @@ class dScriptVirtualBoard(dScriptBoard):
|
|
|
20
20
|
def __init__(self, TCP_IP='127.0.0.1', TCP_PORT=17123, PROTOCOL='binary'):
|
|
21
21
|
_LOGGER.debug("dScriptVirtualBoard: __init__")
|
|
22
22
|
self._MACAddress="02:00:00:%02x:%02x:%02x" % (random.randint(0, 255),random.randint(0, 255),random.randint(0, 255))
|
|
23
|
-
self._ModuleID=random.choice(list(dScriptObject.
|
|
23
|
+
self._ModuleID=random.choice(list(dScriptObject._ModulesConfig.keys())) # only modules with known config can be emulated
|
|
24
24
|
self._CustomFirmeware=True
|
|
25
25
|
self.InitBoard()
|
|
26
26
|
|
|
@@ -1,21 +1,28 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: dScriptModule
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.5.1
|
|
4
4
|
Summary: Home Assistant ready python module for dScript boards (by Robot-Electronics / Devantech Ltd.) with CUSTOM app firmware
|
|
5
5
|
Home-page: https://github.com/mk-maddin/dScriptModule
|
|
6
6
|
Author: Martin Kraemer
|
|
7
7
|
Author-email: mk.maddin@gmail.com
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Platform: UNKNOWN
|
|
10
8
|
Classifier: Programming Language :: Python :: 3
|
|
11
9
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
12
10
|
Classifier: Operating System :: OS Independent
|
|
13
11
|
Requires-Python: >=3.7
|
|
14
12
|
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
15
14
|
Requires-Dist: bitstring
|
|
16
15
|
Requires-Dist: pycryptodome
|
|
16
|
+
Dynamic: author
|
|
17
|
+
Dynamic: author-email
|
|
18
|
+
Dynamic: classifier
|
|
19
|
+
Dynamic: description
|
|
20
|
+
Dynamic: description-content-type
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
Dynamic: requires-dist
|
|
24
|
+
Dynamic: requires-python
|
|
25
|
+
Dynamic: summary
|
|
17
26
|
|
|
18
27
|
# dScriptModule
|
|
19
28
|
Home Assistant ready python module for dScript boards (by Robot-Electronics / Devantech Ltd.) with CUSTOM app firmware available at: https://github.com/mk-maddin/dScriptRoomControl
|
|
20
|
-
|
|
21
|
-
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
dScriptModule/__init__.py,sha256=P6tL2pqP0XCgTUeCb8O0ug3G8lK-VmSj1LXyDK1hF3s,144
|
|
2
|
+
dScriptModule/dScriptBoard.py,sha256=7RvcbjhZCIwyHbioPCDr3rFCdQTSWVzFoFxNHK6ZgrY,44880
|
|
3
|
+
dScriptModule/dScriptEvent.py,sha256=gl_h1DfLUkU5YNXadPgZRKQzSH0wv-GkdBo7EuAHqqU,1957
|
|
4
|
+
dScriptModule/dScriptObject.py,sha256=tFJvDXq6ZVivE5KjK7Ktqu2WFavv4lqy5ve94JO5TnM,7138
|
|
5
|
+
dScriptModule/dScriptServer.py,sha256=MwByosqc7u2nuNTqw1YwFuf4gPtvCQf034AXzIWD4YA,15008
|
|
6
|
+
dScriptModule/dScriptVirtualBoard.py,sha256=F3KePf2dO9PXwsTxs936zcm9x0mAiyW7SqU4Lg9lXs4,8245
|
|
7
|
+
dscriptmodule-3.5.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
8
|
+
dscriptmodule-3.5.1.dist-info/METADATA,sha256=jUFucpASGxIURyjl4lixxOBFAEVbg06oZZevadmKqDQ,1023
|
|
9
|
+
dscriptmodule-3.5.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
10
|
+
dscriptmodule-3.5.1.dist-info/top_level.txt,sha256=nXwXjGsXgTqoxseWdKCq33mFMC1UwyM7DMPKjFTkQg0,14
|
|
11
|
+
dscriptmodule-3.5.1.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
dScriptModule/__init__.py,sha256=EcM2ruXC4gfB1MeiNcOhetZDkV4EhZmuIBPC7XO5JvI,144
|
|
2
|
-
dScriptModule/dScriptBoard.py,sha256=bAE0QxpU9c2SWfTQCzZCJHvDXeeWieUTu8md2ORHaiQ,42881
|
|
3
|
-
dScriptModule/dScriptEvent.py,sha256=gl_h1DfLUkU5YNXadPgZRKQzSH0wv-GkdBo7EuAHqqU,1957
|
|
4
|
-
dScriptModule/dScriptObject.py,sha256=6DLvuOQfe9mLkx5rBA5pJNGjO0_HQP5A0DA0DKKb8m8,6575
|
|
5
|
-
dScriptModule/dScriptServer.py,sha256=JX7y25EBRq8jO9de7Kw4C9DJv2Nc2NBbQNGPhxXGGPQ,13697
|
|
6
|
-
dScriptModule/dScriptVirtualBoard.py,sha256=kREjRuFWlgMeqb8Ed3BRGmvQj7RvzJyU01_eBLYl4m4,8190
|
|
7
|
-
dScriptModule-3.4.5.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
8
|
-
dScriptModule-3.4.5.dist-info/METADATA,sha256=-z5Xay9X6s0caVyzlrWtZzvj_TI-pHTCETtX88pQpRw,819
|
|
9
|
-
dScriptModule-3.4.5.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
|
|
10
|
-
dScriptModule-3.4.5.dist-info/top_level.txt,sha256=nXwXjGsXgTqoxseWdKCq33mFMC1UwyM7DMPKjFTkQg0,14
|
|
11
|
-
dScriptModule-3.4.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|