dScriptModule 3.4.5__py3-none-any.whl → 3.5.0__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 CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "3.4.5"
1
+ __version__ = "3.5.0"
2
2
 
3
3
  from .dScriptObject import *
4
4
  from .dScriptServer import *
@@ -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._async_AESEncrypt(data,self._GetKeyByValue(command,self._DecimalCommands))
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
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
198
- s.settimeout(self.ConnectionTimeout)
199
- s.connect((self.IP, self.Port))
200
- except Exception as e:
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
- #reader, writer = await asyncio.open_connection(self.IP, self.Port, loop=self.__loop)
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
- data = await reader.read(buff)
224
- writer.close()
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
- if not retry:
228
- _LOGGER.debug("dScriptBoard - %s: async_Send retry sending: %s | %s", self.friendlyname, msg, buff)
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]
@@ -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:{'_PhyiscalRelays':2}, 34:{'_PhysicalRelays':24}, 35:{'_PhysicalRelays':8}}
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
- if not self._IsInList(topic,self._EventHandlers.keys()):
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
- self._EventHandlers[topic].append(handler)
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
- if not self._IsInList(topic,self._EventHandlers.keys()):
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
- self._EventHandlers[topic].remove(handler)
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
- if not self._IsInList(topic,self._EventHandlers.keys()):
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 self._EventHandlers[topic]:
120
+ for handler in handlers[topic]:
110
121
  eventobj.event += handler
111
122
  #_LOGGER.debug("dScriptObject: _throwEvent: object: %s", eventobj)
112
123
  eventobj.throw()
@@ -42,8 +42,8 @@ class dScriptServer(dScriptObject):
42
42
  raise Exception("Server already started")
43
43
  return False
44
44
  try:
45
- self.__mode = 'init'
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 == 'thread'
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 = 'init'
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 == None:
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 == None
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 == None
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 == None
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
- _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__)
178
- self.StopServer()
179
- pass
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(0)
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(2) # received byte size is always 2 bytes from dScriptServerUpdate
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
- databytes=self._ToDataBytes(data)
246
- self.__InterpreteBinary(databytes,SenderIP)
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) == 3:
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
- self._throwEvent(SenderIP, cmd, int(databytes[1]), self._OnOffStates[databytes[2]])
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
- else:
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._Modules.keys()))
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
1
+ Metadata-Version: 2.4
2
2
  Name: dScriptModule
3
- Version: 3.4.5
3
+ Version: 3.5.0
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=XIxmnLrBdw-D9QAnwKCIjKIGW2TEA5Fzfu7c9oggeWM,144
2
+ dScriptModule/dScriptBoard.py,sha256=nAMMNGwL2iZYxlkPZNWA0-wWgl4Qb0A32AThOASXGh4,44861
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.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
8
+ dscriptmodule-3.5.0.dist-info/METADATA,sha256=EM3LuhWxea4ajgrdwkjYwaWWrM2z0f1nSIxRzI9BJlk,1023
9
+ dscriptmodule-3.5.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
10
+ dscriptmodule-3.5.0.dist-info/top_level.txt,sha256=nXwXjGsXgTqoxseWdKCq33mFMC1UwyM7DMPKjFTkQg0,14
11
+ dscriptmodule-3.5.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.34.2)
2
+ Generator: setuptools (84.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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,,