not1mm 24.11.6__py3-none-any.whl → 24.11.12.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.
not1mm/__main__.py CHANGED
@@ -1107,11 +1107,16 @@ class MainWindow(QtWidgets.QMainWindow):
1107
1107
  -------
1108
1108
  Nothing
1109
1109
  """
1110
+
1110
1111
  if mode in ["CW", "SSB", "RTTY"]:
1111
1112
  freq = fakefreq(str(band), mode)
1112
1113
  self.change_freq(freq)
1113
1114
  vfo = float(freq)
1114
1115
  vfo = int(vfo * 1000)
1116
+ if mode == "CW":
1117
+ mode = self.rig_control.last_cw_mode
1118
+ if mode == "RTTY":
1119
+ mode = self.rig_control.last_data_mode
1115
1120
  self.change_mode(mode, intended_freq=vfo)
1116
1121
 
1117
1122
  def quit_app(self) -> None:
@@ -2019,8 +2024,19 @@ class MainWindow(QtWidgets.QMainWindow):
2019
2024
  Returns:
2020
2025
  -------
2021
2026
  None
2027
+
2028
+ Control
2029
+ QWRTYIOPSFGHJLBNM,./;'[]//-
2030
+
2031
+
2032
+ shift control
2033
+ ABCDEFGHIJKLMNOPQRSTUVWXY
2022
2034
  """
2023
2035
  modifier = event.modifiers()
2036
+ # the_key = event.key()
2037
+
2038
+ # print(f"Modifier is {modifier=} Key is {the_key=}")
2039
+
2024
2040
  if (
2025
2041
  event.key() == Qt.Key.Key_Equal
2026
2042
  and modifier == Qt.KeyboardModifier.ControlModifier
@@ -3315,10 +3331,9 @@ class MainWindow(QtWidgets.QMainWindow):
3315
3331
  -------
3316
3332
  None
3317
3333
  """
3318
-
3319
3334
  if mode in ("CW", "CW-U", "CW-L", "CWR"):
3320
3335
  if self.rig_control and self.rig_control.online:
3321
- self.rig_control.set_mode("CW")
3336
+ self.rig_control.set_mode(self.rig_control.last_cw_mode)
3322
3337
  if self.pref.get("cwtype") == 3 and self.rig_control is not None:
3323
3338
  if self.rig_control.interface == "flrig":
3324
3339
  self.cwspeed_spinbox_changed()
@@ -3332,9 +3347,18 @@ class MainWindow(QtWidgets.QMainWindow):
3332
3347
  self.clearinputs()
3333
3348
  self.read_cw_macros()
3334
3349
  return
3335
- if mode == "RTTY":
3350
+ if mode in (
3351
+ "DIGI-U",
3352
+ "DIGI-L",
3353
+ "RTTY",
3354
+ "RTTY-R",
3355
+ "LSB-D",
3356
+ "USB-D",
3357
+ "AM-D",
3358
+ "FM-D",
3359
+ ):
3336
3360
  if self.rig_control and self.rig_control.online:
3337
- self.rig_control.set_mode("RTTY")
3361
+ self.rig_control.set_mode(self.rig_control.last_data_mode)
3338
3362
  else:
3339
3363
  self.radio_state["mode"] = "RTTY"
3340
3364
  self.setmode("RTTY")
@@ -3548,6 +3572,7 @@ class MainWindow(QtWidgets.QMainWindow):
3548
3572
  Passing in a dictionary object with the
3549
3573
  vfo freq, mode, bandwidth, and online state of the radio.
3550
3574
  """
3575
+ logger.debug(f"{the_dict=}")
3551
3576
  self.set_radio_icon(0)
3552
3577
  info_dirty = False
3553
3578
  vfo = the_dict.get("vfoa", "")
@@ -3645,7 +3670,7 @@ class MainWindow(QtWidgets.QMainWindow):
3645
3670
  -------
3646
3671
  None
3647
3672
  """
3648
- if self.radio_state.get("mode") == "CW":
3673
+ if self.radio_state.get("mode") in ("CW", "CW-L", "CW-R", "CWR"):
3649
3674
  macro_file = "cwmacros.txt"
3650
3675
  elif self.radio_state.get("mode") in (
3651
3676
  "RTTY",
@@ -3659,6 +3684,7 @@ class MainWindow(QtWidgets.QMainWindow):
3659
3684
  "RTTYR",
3660
3685
  "PKTLSB",
3661
3686
  "PKTUSB",
3687
+ "FSK",
3662
3688
  ):
3663
3689
  macro_file = "rttymacros.txt"
3664
3690
  else:
@@ -3687,7 +3713,11 @@ class MainWindow(QtWidgets.QMainWindow):
3687
3713
  temp directory this is running from... In theory.
3688
3714
  """
3689
3715
 
3690
- if self.radio_state.get("mode") == "CW":
3716
+ if self.radio_state.get("mode") in (
3717
+ "CW",
3718
+ "CW-L",
3719
+ "CW-R",
3720
+ ):
3691
3721
  macro_file = "cwmacros.txt"
3692
3722
  elif self.radio_state.get("mode") in (
3693
3723
  "RTTY",
@@ -81,7 +81,11 @@ class CAT:
81
81
  }
82
82
 
83
83
  if self.interface == "flrig":
84
- target = f"http://{host}:{port}"
84
+ if not self.__check_sane_ip(self.host):
85
+ self.online = False
86
+ return
87
+
88
+ target = f"http://{self.host}:{self.port}"
85
89
  logger.debug("%s", target)
86
90
  self.server = xmlrpc.client.ServerProxy(target)
87
91
  self.online = True
@@ -91,14 +95,30 @@ class CAT:
91
95
  ConnectionRefusedError,
92
96
  xmlrpc.client.Fault,
93
97
  http.client.BadStatusLine,
98
+ socket.error,
99
+ socket.gaierror,
94
100
  ):
95
101
  self.online = False
96
102
  elif self.interface == "rigctld":
103
+ if not self.__check_sane_ip(self.host):
104
+ self.online = False
105
+ return
97
106
  self.__initialize_rigctrld()
98
107
  elif self.interface == "fake":
99
108
  self.online = True
109
+ logger.debug("Using Fake Rig")
100
110
  return
101
111
 
112
+ def __check_sane_ip(self, ip: str) -> bool:
113
+ """check if IP address look normal"""
114
+ x = ip.split(".")
115
+ if len(x) != 4:
116
+ return False
117
+ for y in x:
118
+ if not y.isnumeric():
119
+ return False
120
+ return True
121
+
102
122
  def __initialize_rigctrld(self):
103
123
  try:
104
124
  self.rigctrlsocket = socket.socket()
@@ -106,7 +126,13 @@ class CAT:
106
126
  self.rigctrlsocket.connect((self.host, self.port))
107
127
  logger.debug("Connected to rigctrld")
108
128
  self.online = True
109
- except (ConnectionRefusedError, TimeoutError, OSError) as exception:
129
+ except (
130
+ ConnectionRefusedError,
131
+ TimeoutError,
132
+ OSError,
133
+ socket.error,
134
+ socket.gaierror,
135
+ ) as exception:
110
136
  self.rigctrlsocket = None
111
137
  self.online = False
112
138
  logger.debug("%s", f"{exception}")
@@ -223,16 +249,19 @@ class CAT:
223
249
  """Poll the radio using flrig"""
224
250
  try:
225
251
  self.online = True
226
- return self.server.rig.get_vfo()
252
+ vfo_value = self.server.rig.get_vfo()
253
+ logger.debug(f"{vfo_value=}")
254
+ return vfo_value
227
255
  except (
228
256
  ConnectionRefusedError,
229
257
  xmlrpc.client.Fault,
230
258
  http.client.BadStatusLine,
231
259
  http.client.CannotSendRequest,
232
260
  http.client.ResponseNotReady,
261
+ AttributeError,
233
262
  ) as exception:
234
263
  self.online = False
235
- logger.debug("getvfo_flrig: %s", f"{exception}")
264
+ logger.debug(f"{exception=}")
236
265
  return ""
237
266
 
238
267
  def __getvfo_rigctld(self) -> str:
@@ -244,7 +273,7 @@ class CAT:
244
273
  return self.__get_serial_string().strip()
245
274
  except socket.error as exception:
246
275
  self.online = False
247
- logger.debug("getvfo_rigctld: %s", f"{exception}")
276
+ logger.debug(f"{exception=}")
248
277
  self.rigctrlsocket = None
249
278
  return ""
250
279
 
@@ -268,13 +297,16 @@ class CAT:
268
297
  # 7300 ['LSB', 'USB', 'AM', 'FM', 'CW', 'CW-R', 'RTTY', 'RTTY-R', 'LSB-D', 'USB-D', 'AM-D', 'FM-D']
269
298
  try:
270
299
  self.online = True
271
- return self.server.rig.get_mode()
300
+ mode_value = self.server.rig.get_mode()
301
+ logger.debug(f"{mode_value=}")
302
+ return mode_value
272
303
  except (
273
304
  ConnectionRefusedError,
274
305
  xmlrpc.client.Fault,
275
306
  http.client.BadStatusLine,
276
307
  http.client.CannotSendRequest,
277
308
  http.client.ResponseNotReady,
309
+ AttributeError,
278
310
  ) as exception:
279
311
  self.online = False
280
312
  logger.debug("%s", f"{exception}")
@@ -282,7 +314,7 @@ class CAT:
282
314
 
283
315
  def __getmode_rigctld(self) -> str:
284
316
  """Returns mode vai rigctld"""
285
- # QMX 'AM CW USB LSB RTTY FM CWR RTTYR'
317
+ # QMX 'DIGI-U DIGI-L CW-U CW-L' or 'LSB', 'USB', 'CW', 'FM', 'AM', 'FSK'
286
318
  # 7300 'AM CW USB LSB RTTY FM CWR RTTYR PKTLSB PKTUSB FM-D AM-D'
287
319
  if self.rigctrlsocket:
288
320
  try:
@@ -316,6 +348,7 @@ class CAT:
316
348
  try:
317
349
  self.online = True
318
350
  bandwidth = self.server.rig.get_bw()
351
+ logger.debug(f"{bandwidth=}")
319
352
  return bandwidth[0]
320
353
  except (
321
354
  ConnectionRefusedError,
@@ -323,6 +356,7 @@ class CAT:
323
356
  http.client.BadStatusLine,
324
357
  http.client.CannotSendRequest,
325
358
  http.client.ResponseNotReady,
359
+ AttributeError,
326
360
  ) as exception:
327
361
  self.online = False
328
362
  logger.debug("getbw_flrig: %s", f"{exception}")
@@ -438,13 +472,16 @@ class CAT:
438
472
  """Returns list of modes supported by the radio"""
439
473
  try:
440
474
  self.online = True
441
- return self.server.rig.get_modes()
475
+ mode_list = self.server.rig.get_modes()
476
+ logger.debug(f"{mode_list=}")
477
+ return mode_list
442
478
  except (
443
479
  ConnectionRefusedError,
444
480
  xmlrpc.client.Fault,
445
481
  http.client.BadStatusLine,
446
482
  http.client.CannotSendRequest,
447
483
  http.client.ResponseNotReady,
484
+ AttributeError,
448
485
  ) as exception:
449
486
  self.online = False
450
487
  logger.debug("%s", f"{exception}")
@@ -494,6 +531,7 @@ class CAT:
494
531
  http.client.BadStatusLine,
495
532
  http.client.CannotSendRequest,
496
533
  http.client.ResponseNotReady,
534
+ AttributeError,
497
535
  ) as exception:
498
536
  self.online = False
499
537
  logger.debug("setvfo_flrig: %s", f"{exception}")
@@ -529,16 +567,20 @@ class CAT:
529
567
  """Sets the radios mode"""
530
568
  try:
531
569
  self.online = True
532
- return self.server.rig.set_mode(mode)
570
+ logger.debug(f"{mode=}")
571
+ set_mode_result = self.server.rig.set_mode(mode)
572
+ logger.debug(f"self.server.rig.setmode(mode) = {set_mode_result}")
573
+ return set_mode_result
533
574
  except (
534
575
  ConnectionRefusedError,
535
576
  xmlrpc.client.Fault,
536
577
  http.client.BadStatusLine,
537
578
  http.client.CannotSendRequest,
538
579
  http.client.ResponseNotReady,
580
+ AttributeError,
539
581
  ) as exception:
540
582
  self.online = False
541
- logger.debug("setmode_flrig: %s", f"{exception}")
583
+ logger.debug(f"{exception=}")
542
584
  return False
543
585
 
544
586
  def __setmode_rigctld(self, mode: str) -> bool:
@@ -579,6 +621,7 @@ class CAT:
579
621
  http.client.BadStatusLine,
580
622
  http.client.CannotSendRequest,
581
623
  http.client.ResponseNotReady,
624
+ AttributeError,
582
625
  ) as exception:
583
626
  self.online = False
584
627
  logger.debug("setpower_flrig: %s", f"{exception}")
@@ -637,6 +680,7 @@ class CAT:
637
680
  http.client.BadStatusLine,
638
681
  http.client.CannotSendRequest,
639
682
  http.client.ResponseNotReady,
683
+ AttributeError,
640
684
  ) as exception:
641
685
  self.online = False
642
686
  logger.debug("%s", f"{exception}")
@@ -675,6 +719,7 @@ class CAT:
675
719
  http.client.BadStatusLine,
676
720
  http.client.CannotSendRequest,
677
721
  http.client.ResponseNotReady,
722
+ AttributeError,
678
723
  ) as exception:
679
724
  self.online = False
680
725
  logger.debug("%s", f"{exception}")
not1mm/lib/cwinterface.py CHANGED
@@ -31,6 +31,9 @@ class CW:
31
31
  self.speed = 20
32
32
  self.winkeyer_functions = []
33
33
  if self.servertype == 2:
34
+ if not self.__check_sane_ip(self.host):
35
+ logger.critical(f"Bad IP: {self.host}")
36
+ return
34
37
  with ServerProxy(f"http://{self.host}:{self.port}") as proxy:
35
38
  try:
36
39
  self.winkeyer_functions = proxy.system.listMethods()
@@ -47,6 +50,16 @@ class CW:
47
50
  "http://%s:%s, xmlrpc Connection Refused", self.host, self.port
48
51
  )
49
52
 
53
+ def __check_sane_ip(self, ip: str) -> bool:
54
+ """check if IP address look normal"""
55
+ x = ip.split(".")
56
+ if len(x) != 4:
57
+ return False
58
+ for y in x:
59
+ if not y.isnumeric():
60
+ return False
61
+ return True
62
+
50
63
  def sendcw(self, texttosend):
51
64
  """sends cw to k1el"""
52
65
  logger.debug(f"{texttosend=} {self.servertype=}")
@@ -61,7 +74,7 @@ class CW:
61
74
  def _sendcw_xmlrpc(self, texttosend):
62
75
  """sends cw to xmlrpc"""
63
76
  logger.debug("xmlrpc: %s", texttosend)
64
- if texttosend:
77
+ if texttosend and self.__check_sane_ip(self.host):
65
78
  with ServerProxy(f"http://{self.host}:{self.port}") as proxy:
66
79
  try:
67
80
  proxy.k1elsendstring(texttosend)
@@ -76,23 +89,35 @@ class CW:
76
89
  logger.debug(
77
90
  "http://%s:%s, xmlrpc Connection Refused", self.host, self.port
78
91
  )
92
+ else:
93
+ logger.critical(f"Bad IP: {self.host}")
79
94
 
80
95
  def _sendcw_udp(self, texttosend):
81
96
  """send cw to udp port"""
82
97
  logger.debug("UDP: %s", texttosend)
83
- if texttosend:
98
+ if texttosend and self.__check_sane_ip(self.host):
84
99
  server_address_port = (self.host, self.port)
85
100
  # bufferSize = 1024
86
101
  udp_client_socket = socket.socket(
87
102
  family=socket.AF_INET, type=socket.SOCK_DGRAM
88
103
  )
89
- udp_client_socket.sendto(bytes(texttosend, "utf-8"), server_address_port)
104
+ try:
105
+ udp_client_socket.sendto(
106
+ bytes(texttosend, "utf-8"), server_address_port
107
+ )
108
+ except socket.gaierror:
109
+ ...
110
+ else:
111
+ logger.critical(f"Bad IP: {self.host.encode()}")
90
112
 
91
113
  def _sendcwcat(self, texttosend):
92
114
  """..."""
93
115
 
94
116
  def set_winkeyer_speed(self, speed):
95
117
  """doc"""
118
+ if not self.__check_sane_ip(self.host):
119
+ logger.critical(f"Bad IP: {self.host}")
120
+ return
96
121
  with ServerProxy(f"http://{self.host}:{self.port}") as proxy:
97
122
  try:
98
123
  if "setspeed" in self.winkeyer_functions:
not1mm/lib/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """It's the version"""
2
2
 
3
- __version__ = "24.11.6"
3
+ __version__ = "24.11.12.1"
@@ -137,7 +137,6 @@ def prefill(self):
137
137
  def check_call_history(self):
138
138
  """"""
139
139
  result = self.database.fetch_call_history(self.callsign.text())
140
- print(f"{result=}")
141
140
  if result:
142
141
  self.history_info.setText(f"{result.get('UserText','')}")
143
142
  if self.other_2.text() == "":
@@ -540,7 +540,6 @@ def ft8_handler(the_packet: dict):
540
540
  def check_call_history(self):
541
541
  """"""
542
542
  result = self.database.fetch_call_history(self.callsign.text())
543
- print(f"{result=}")
544
543
  if result:
545
544
  self.history_info.setText(f"{result.get('UserText','')}")
546
545
  if self.other_2.text() == "":
@@ -508,7 +508,6 @@ def ft8_handler(the_packet: dict):
508
508
  def check_call_history(self):
509
509
  """"""
510
510
  result = self.database.fetch_call_history(self.callsign.text())
511
- print(f"{result=}")
512
511
  if result:
513
512
  self.history_info.setText(f"{result.get('UserText','')}")
514
513
  if self.other_2.text() == "":
@@ -508,7 +508,6 @@ def ft8_handler(the_packet: dict):
508
508
  def check_call_history(self):
509
509
  """"""
510
510
  result = self.database.fetch_call_history(self.callsign.text())
511
- print(f"{result=}")
512
511
  if result:
513
512
  self.history_info.setText(f"{result.get('UserText','')}")
514
513
  if self.other_2.text() == "":
@@ -453,7 +453,6 @@ def recalculate_mults(self):
453
453
  def check_call_history(self):
454
454
  """"""
455
455
  result = self.database.fetch_call_history(self.callsign.text())
456
- print(f"{result=}")
457
456
  if result:
458
457
  self.history_info.setText(f"{result.get('UserText','')}")
459
458
  # if self.other_1.text() == "":
@@ -453,7 +453,6 @@ def recalculate_mults(self):
453
453
  def check_call_history(self):
454
454
  """"""
455
455
  result = self.database.fetch_call_history(self.callsign.text())
456
- print(f"{result=}")
457
456
  if result:
458
457
  self.history_info.setText(f"{result.get('UserText','')}")
459
458
  # if self.other_1.text() == "":
@@ -523,7 +523,6 @@ def process_esm(self, new_focused_widget=None, with_enter=False):
523
523
  def check_call_history(self):
524
524
  """"""
525
525
  result = self.database.fetch_call_history(self.callsign.text())
526
- print(f"{result=}")
527
526
  if result:
528
527
  self.history_info.setText(f"{result.get('UserText','')}")
529
528
  if self.other_2.text() == "":
@@ -642,7 +642,6 @@ def process_esm(self, new_focused_widget=None, with_enter=False):
642
642
  def check_call_history(self):
643
643
  """"""
644
644
  result = self.database.fetch_call_history(self.callsign.text())
645
- print(f"{result=}")
646
645
  if result:
647
646
  self.history_info.setText(f"{result.get('UserText','')}")
648
647
  if self.other_1.text() == "":
@@ -511,7 +511,6 @@ def process_esm(self, new_focused_widget=None, with_enter=False):
511
511
  def check_call_history(self):
512
512
  """"""
513
513
  result = self.database.fetch_call_history(self.callsign.text())
514
- print(f"{result=}")
515
514
  if result:
516
515
  self.history_info.setText(f"{result.get('UserText','')}")
517
516
  if self.other_2.text() == "":
@@ -496,7 +496,6 @@ def process_esm(self, new_focused_widget=None, with_enter=False):
496
496
  def check_call_history(self):
497
497
  """"""
498
498
  result = self.database.fetch_call_history(self.callsign.text())
499
- print(f"{result=}")
500
499
  if result:
501
500
  self.history_info.setText(f"{result.get('UserText','')}")
502
501
  if self.other_1.text() == "":
not1mm/plugins/naqp_cw.py CHANGED
@@ -546,7 +546,6 @@ def process_esm(self, new_focused_widget=None, with_enter=False):
546
546
  def check_call_history(self):
547
547
  """"""
548
548
  result = self.database.fetch_call_history(self.callsign.text())
549
- print(f"{result=}")
550
549
  if result:
551
550
  self.history_info.setText(f"{result.get('UserText','')}")
552
551
  if self.other_1.text() == "":
@@ -653,7 +653,6 @@ def process_esm(self, new_focused_widget=None, with_enter=False):
653
653
  def check_call_history(self):
654
654
  """"""
655
655
  result = self.database.fetch_call_history(self.callsign.text())
656
- print(f"{result=}")
657
656
  if result:
658
657
  self.history_info.setText(f"{result.get('UserText','')}")
659
658
  if self.other_1.text() == "":
@@ -516,7 +516,6 @@ def process_esm(self, new_focused_widget=None, with_enter=False):
516
516
  def check_call_history(self):
517
517
  """"""
518
518
  result = self.database.fetch_call_history(self.callsign.text())
519
- print(f"{result=}")
520
519
  if result:
521
520
  self.history_info.setText(f"{result.get('UserText','')}")
522
521
  if self.other_1.text() == "":
not1mm/plugins/raem.py CHANGED
@@ -494,7 +494,6 @@ def recalculate_mults(self):
494
494
  def check_call_history(self):
495
495
  """"""
496
496
  result = self.database.fetch_call_history(self.callsign.text())
497
- print(f"{result=}")
498
497
  if result:
499
498
  self.history_info.setText(f"{result.get('UserText','')}")
500
499
  if self.other_2.text() == "":
@@ -369,7 +369,6 @@ def recalculate_mults(self):
369
369
  def check_call_history(self):
370
370
  """"""
371
371
  result = self.database.fetch_call_history(self.callsign.text())
372
- print(f"{result=}")
373
372
  if result:
374
373
  self.history_info.setText(f"{result.get('UserText','')}")
375
374
  if self.other_1.text() == "":
@@ -577,7 +577,6 @@ def process_esm(self, new_focused_widget=None, with_enter=False):
577
577
  def check_call_history(self):
578
578
  """"""
579
579
  result = self.database.fetch_call_history(self.callsign.text())
580
- print(f"{result=}")
581
580
  if result:
582
581
  self.history_info.setText(f"{result.get('UserText','')}")
583
582
  if self.other_1.text() == "":
@@ -444,7 +444,6 @@ def process_esm(self, new_focused_widget=None, with_enter=False):
444
444
  def check_call_history(self):
445
445
  """"""
446
446
  result = self.database.fetch_call_history(self.callsign.text())
447
- print(f"{result=}")
448
447
  if result:
449
448
  self.history_info.setText(f"{result.get('UserText','')}")
450
449
  if self.other_1.text() == "":
not1mm/radio.py CHANGED
@@ -35,7 +35,16 @@ class Radio(QObject):
35
35
  host = None
36
36
  port = None
37
37
  modes = ""
38
+ cw_list = ["CW", "CW-L", "CW-U", "CWR"]
39
+ rtty_list = [
40
+ "RTTY",
41
+ "DIGI-L",
42
+ "PKTLSB",
43
+ "LSB-D",
44
+ ]
38
45
  last_data_mode = "RTTY"
46
+ last_cw_mode = "CW"
47
+ last_ph_mode = "SSB"
39
48
 
40
49
  def __init__(self, interface: str, host: str, port: int) -> None:
41
50
  super().__init__()
@@ -49,6 +58,15 @@ class Radio(QObject):
49
58
  self.cat = CAT(self.interface, self.host, self.port)
50
59
  self.online = self.cat.online
51
60
  self.modes = self.cat.get_mode_list()
61
+ for pos_cw in self.cw_list:
62
+ if pos_cw in self.modes:
63
+ self.last_cw_mode = pos_cw
64
+ break
65
+ for pos_rtty in self.rtty_list:
66
+ if pos_rtty in self.modes:
67
+ self.last_data_mode = pos_rtty
68
+ break
69
+
52
70
  except ConnectionResetError:
53
71
  ...
54
72
  while not self.time_to_quit:
@@ -97,6 +115,7 @@ class Radio(QObject):
97
115
  "USB-D",
98
116
  "AM-D",
99
117
  "FM-D",
118
+ "FSK",
100
119
  "DIGI-U",
101
120
  "DIGI-L",
102
121
  "RTTYR",
@@ -106,6 +125,11 @@ class Radio(QObject):
106
125
  if the_mode in datamodes:
107
126
  self.last_data_mode = the_mode
108
127
 
128
+ cwmodes = ["CW", "CW-L", "CW-U", "CWR"]
129
+
130
+ if the_mode in cwmodes:
131
+ self.last_cw_mode = the_mode
132
+
109
133
  def sendcw(self, texttosend):
110
134
  """..."""
111
135
  logger.debug(f"Send CW: {texttosend}")
not1mm/test.py CHANGED
@@ -1,61 +1,39 @@
1
- from not1mm.lib.database import DataBase
2
- from json import loads
3
- import not1mm.fsutils as fsutils
4
- import os
5
-
6
- dbname = None
7
- pref = {}
8
-
9
-
10
- def load_pref():
11
- try:
12
- if os.path.exists(fsutils.CONFIG_FILE):
13
- with open(fsutils.CONFIG_FILE, "rt", encoding="utf-8") as file_descriptor:
14
- pref = loads(file_descriptor.read())
15
- else:
16
- pref["current_database"] = "ham.db"
17
-
18
- except IOError:
19
- ...
20
-
21
-
22
- load_pref()
23
- dbname = fsutils.USER_DATA_PATH / pref.get("current_database", "ham.db")
24
- database = DataBase(dbname, fsutils.USER_DATA_PATH)
25
-
26
- database.create_callhistory_table()
27
- database.delete_callhistory()
28
-
29
- try:
30
- with open(
31
- "/home/mbridak/call_history/CWOPS_3634-AAA.txt", "rt", encoding="utf-8"
32
- ) as file_descriptor:
33
- lines = file_descriptor.readlines()
34
- if "!!Order!!" in lines[0]:
35
- item_names = lines[0].strip().split(",")
36
- # ['!!Order!!', 'Call', 'Sect', 'State', 'CK', 'UserText', '']
37
- item_names = item_names[1:-1]
38
- # ['Call', 'Sect', 'State', 'CK', 'UserText']
39
- lines = lines[1:]
40
- group_list = []
41
- for line in lines:
42
- if line.startswith("#"):
43
- continue
44
- group = {}
45
- fields = line.strip().split(",")
46
- # ['4U1WB','MDC','DC','89','']
47
- count = 0
48
- try:
49
- for item in item_names:
50
- if item == "":
51
- continue
52
- group[item] = fields[count]
53
- count += 1
54
- group_list.append(group)
55
- # database.add_callhistory_item(group)
56
- # print(f"{group=}")
57
- except IndexError:
58
- ...
59
- database.add_callhistory_items(group_list)
60
- except FileNotFoundError:
61
- print("error")
1
+ class CW:
2
+ """
3
+
4
+ An interface to cwdaemon and PyWinkeyerSerial
5
+
6
+ servertype: int 1=cwdaemon, 2=pywinkeyer, 3=rigctld
7
+
8
+ """
9
+
10
+ def __init__(self, servertype: int, host: str, port: int) -> None:
11
+ self.servertype = servertype
12
+ self.cat = None
13
+ self.host = host
14
+ self.port = port
15
+ self.speed = 20
16
+ self.winkeyer_functions = []
17
+
18
+ def __check_sane_ip(self, ip: str) -> bool:
19
+ """check if IP address look normal"""
20
+ print(f"{type(self.host)} {self.host}")
21
+
22
+ x = ip.split(".")
23
+
24
+ print(f"{x=} {len(x)=}")
25
+
26
+ if len(x) != 4:
27
+ return False
28
+ for y in x:
29
+ if not y.isnumeric():
30
+ return False
31
+ return True
32
+
33
+ def test(self):
34
+ """"""
35
+ print(f"{self.__check_sane_ip(self.host)=}")
36
+
37
+
38
+ x = CW(1, "127.0.0.1", 6789)
39
+ x.test()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: not1mm
3
- Version: 24.11.6
3
+ Version: 24.11.12.1
4
4
  Summary: NOT1MM Logger
5
5
  Author-email: Michael Bridak <michael.bridak@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/mbridak/not1mm
@@ -236,6 +236,8 @@ generated, 'cause I'm lazy, list of those who've submitted PR's.
236
236
 
237
237
  ## Recent Changes (Polishing the Turd)
238
238
 
239
+ - [24-11-12] add check for ipv4 address for CAT.
240
+ - [24-11-10] ReJiggered CAT/flrig interface to hopefull make it more workable.
239
241
  - [24-11-6] Added Call history to ARRL VHF, CQ160, CQWW, StewPerry, Weekly RTTY
240
242
  - [24-11-5] Fix crash with bad qrz credentials.
241
243
  - [24-11-3-1] Fixed CWT ESM, Add Call History to CWT, Helvetia, WFD, NAQP, K1USN. Add ESM Helvetia.
@@ -1,12 +1,12 @@
1
1
  not1mm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- not1mm/__main__.py,sha256=T0hdB2sKV_bRA293-WeRf9vloO3HDODI4o1TY5Pf47I,141356
2
+ not1mm/__main__.py,sha256=8y8-omwssJQfuPTGEebpnXtLjxr4IAO7eG2_RfC-82A,142100
3
3
  not1mm/bandmap.py,sha256=X6mMHXS1kXBbUPZCaKgiVJ6Dz6DE6LEQqtEXfT3telg,30811
4
4
  not1mm/checkwindow.py,sha256=F6hNCbVSLG2PPY2afgmwlBWeqr1Uj4-n__AivDLVQ_0,9670
5
5
  not1mm/fsutils.py,sha256=ukHKxKTeNKxKwqRaJjtzRShL4X5Xl0jRBbADyy3Ifp8,1701
6
6
  not1mm/logwindow.py,sha256=TvpzQTNB92hISlUO3iWBqtlPmlebdhOkAArx0DNGcOs,43966
7
7
  not1mm/lookupservice.py,sha256=4c36x_1G3Sy69gQfJ6El7vHLIKTjLGH67ziPPoeYweM,2648
8
- not1mm/radio.py,sha256=eu1kBjmttREFZ5tAJpNuQSMrmWeKw4Tb-sjgjChUYUs,4693
9
- not1mm/test.py,sha256=o3DH2wQ3C_HwK650dBqNFf0eYCg5D6s8_GvJht1WjVo,1924
8
+ not1mm/radio.py,sha256=khmyESHaXUDF2hGQ5v3atEOd1YACrOAsuoIA4XgNtlc,5340
9
+ not1mm/test.py,sha256=kTELpu6EFmMIgUrEIwFyvPAKoDokDDFq6U_9yJ7pnOs,865
10
10
  not1mm/vfo.py,sha256=ggPyWtxMbdSE5RwdK0nDRwDNqOxdpb_pvnzZdbzZVQE,11136
11
11
  not1mm/voice_keying.py,sha256=sA3gw5_k7kShTg2qhG7HkKDM5M6KheJVRkAc_C7mxDk,3006
12
12
  not1mm/data/JetBrainsMono-ExtraLight.ttf,sha256=g5Hn7BPounWMGDj1a8zZcyKMz03HSqW__pUluRR7Evg,274144
@@ -95,8 +95,8 @@ not1mm/data/phonetics/yourcall.wav,sha256=4kheHJmCiRDL2kjhlgXQ8_u_eEMgKxiNGu5UBk
95
95
  not1mm/data/phonetics/z.wav,sha256=arafCi7fwmBLdVDI-PRyaL4U-03PIQDhffwY5noJ_2c,51768
96
96
  not1mm/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
97
97
  not1mm/lib/about.py,sha256=sWycfGcruN3SaEe4JmaJ61K6D8Itq0WxpUYT-lEcmYM,416
98
- not1mm/lib/cat_interface.py,sha256=Vkxq4JvWguP8O95JYUfQ5hrllzggm6jeFXFiTBKvnw8,22534
99
- not1mm/lib/cwinterface.py,sha256=3H_Ur5qtZCg6AA-CBURdnS3IUcvs3YAcwYLO0S8SUBg,3621
98
+ not1mm/lib/cat_interface.py,sha256=ZSWXajCu38RI4d-VpGIUUjAMYSSMIPjaUrHkejJKmts,23908
99
+ not1mm/lib/cwinterface.py,sha256=yQL8Dif9oOIynaRItHgvcmu4mYv1TnTpqCHPtkeb09o,4472
100
100
  not1mm/lib/database.py,sha256=nqWp2eJ7JfUTqaQ9AVbx3XjgtlRnYY9ruTQCv2YRreY,48310
101
101
  not1mm/lib/edit_contact.py,sha256=Ki9bGPpqyQQBB1cU8VIBDCal3lbXeQ6qxhzklmhE2_w,353
102
102
  not1mm/lib/edit_macro.py,sha256=raKWBwsHInj5EUKmvyLQ6gqc3ZFDlstsD3xqoM4PC8E,517
@@ -110,12 +110,11 @@ not1mm/lib/lookup.py,sha256=KECMDi9tflRDzgTLeDfDl7HGWWRHvW3HCjNHyyjoWaY,10835
110
110
  not1mm/lib/multicast.py,sha256=KJcruI-bOuHfHXPjl3SGQhL6I9sKrygy-sdFSvxffUM,3255
111
111
  not1mm/lib/n1mm.py,sha256=H54mpgJF0GAmKavM-nb5OAq2SJFWYkux4eMWWiSRxJc,6288
112
112
  not1mm/lib/new_contest.py,sha256=IznTDMq7yXHB6zBoGUEC_WDYPCPpsSZW4wwMJi16zK0,816
113
- not1mm/lib/playsound.py,sha256=kxkcitBFbZCXJ2wxQ1lxg4rBwfxiSpuNpJSXHOPCoXA,9241
114
113
  not1mm/lib/plugin_common.py,sha256=TbFUbftjELFt4QRdsjSHbqnXSngZOlSwlCTClqosDXA,9727
115
114
  not1mm/lib/select_contest.py,sha256=WsptLuwkouIHeocJL3oZ6-eUfEnhpwdc-x7eMZ_TIVM,359
116
115
  not1mm/lib/settings.py,sha256=Xt0WE2ro_kUYdugQ0Pe1SQX07MHrJ0jyQqDqAKKqxuU,13564
117
116
  not1mm/lib/super_check_partial.py,sha256=hwT2NRwobu0PLDyw6ltmbmcAtGBD02CKGFbgGWjXMqA,2334
118
- not1mm/lib/version.py,sha256=KCXyExImS-3bja9rsBnV3HWDykHK-FHT3DF7HIjhb88,48
117
+ not1mm/lib/version.py,sha256=-iSAzL-z5BgttDF8dYIz7xmh50rd4o4UomAp5Djzy90,51
119
118
  not1mm/lib/versiontest.py,sha256=8vDNptuBBunn-1IGkjNaquehqBYUJyjrPSF8Igmd4_Y,1286
120
119
  not1mm/plugins/10_10_fall_cw.py,sha256=gNgTnafjM99cFvZ-6qBfWoOvd5Zj2Ehx6XjJvrHjm40,11872
121
120
  not1mm/plugins/10_10_spring_cw.py,sha256=QME8LyLyTnHsA5sjGG19n_64-0gdgBMRRi9C8LpgQzs,11877
@@ -127,20 +126,20 @@ not1mm/plugins/arrl_dx_cw.py,sha256=zAnVa3spbxjlDXMrV_RvvqyUAaYBZ6IZjxhsfLdc-9Q,
127
126
  not1mm/plugins/arrl_dx_ssb.py,sha256=Xxa9t2mu9IhY71jevvY95CMLXOBHBOGstNFWUwsVQkE,18017
128
127
  not1mm/plugins/arrl_field_day.py,sha256=N68dy5FhPCDYxTIP8PNQh4p5rZDJlHmhQqJyH6Qi4qo,16967
129
128
  not1mm/plugins/arrl_rtty_ru.py,sha256=hKUS4isjdXo3EYxQrsqsDupPp2chW8fpoWj0T1pTgJ4,7994
130
- not1mm/plugins/arrl_ss_cw.py,sha256=G1WT2_nF-IISHwteZYT0gJJ6vu_QqUE4hg8gm4tLxss,17596
129
+ not1mm/plugins/arrl_ss_cw.py,sha256=JEr3fxpn-R0R70uBdpMpFBH-mdx7Vrt9e6UQuvR0X9I,17572
131
130
  not1mm/plugins/arrl_ss_phone.py,sha256=zIZidB3li8n7dxCsqGechkSmxjQM7TBHeLvSv2l5QdY,17246
132
- not1mm/plugins/arrl_vhf_jan.py,sha256=HZLqRUGX2aROnmVPfkspx9sd3GPPKdcVIQT1_TiX7Hk,16952
133
- not1mm/plugins/arrl_vhf_jun.py,sha256=XaqQ871dd8qF5OKnlgiiwWh7IDDbp1Lx65kfCLrFjmo,16044
134
- not1mm/plugins/arrl_vhf_sep.py,sha256=PF6kvOUnnW7B43cONYYa-Te1s1PKK8hNeZ_lovGvrNw,16044
131
+ not1mm/plugins/arrl_vhf_jan.py,sha256=LE1X0G9m6YrgUMB3CV12lxkh1bf9QiRU-gMi51NFtLs,16928
132
+ not1mm/plugins/arrl_vhf_jun.py,sha256=RlQLXWASs6mNUhVnvlUp35N5bGYbQGaDJj8UJantSok,16020
133
+ not1mm/plugins/arrl_vhf_sep.py,sha256=xIUgHIXMZ0euMCaTr2Ftk3U5yu4cFxP2YOeQvLUnGgE,16020
135
134
  not1mm/plugins/canada_day.py,sha256=kFwrJ6T9Zz34KmaWL88bj8MDSYNpO2q_KNWLkLLZKlI,15993
136
- not1mm/plugins/cq_160_cw.py,sha256=QoUtKX2HM4Iq3u-3Za4go9oSrptxSWAezb9a9tg4dsk,15803
137
- not1mm/plugins/cq_160_ssb.py,sha256=vyZ6g_mEOVsPmEl5pVcDUk-Y32k7xrB_4R4SxGwuP1A,15846
135
+ not1mm/plugins/cq_160_cw.py,sha256=axx9oFhJ3SgfuMC3nmSId0_xUcldKK1Jxq2Rew7em-M,15779
136
+ not1mm/plugins/cq_160_ssb.py,sha256=SE2caAhJ_VdlONEMcamEFebAzybRGX98koW8LaWG0vY,15822
138
137
  not1mm/plugins/cq_wpx_cw.py,sha256=g_d7i1DrQjAeGuof6SoKAr9HkR9ru3EBEKxLSBjNF4o,18508
139
138
  not1mm/plugins/cq_wpx_rtty.py,sha256=t5ZAWAtsjda6rAHJ2N2mfenazhsSo2yzqRGV-TKZEbA,21327
140
139
  not1mm/plugins/cq_wpx_ssb.py,sha256=f7cCyheb7VqkDMrM-wevcOCUXjrIXyBI0p2ynVYnDO0,17158
141
- not1mm/plugins/cq_ww_cw.py,sha256=h7JJSeEVMwgyY7uU8Wz2pHeHmKyJxRjPl8kmLfMupDE,17724
142
- not1mm/plugins/cq_ww_rtty.py,sha256=30WtyazZFqFnGsT51Dhh6kAH07HAN-FvuoLny6W--zQ,22359
143
- not1mm/plugins/cq_ww_ssb.py,sha256=5MlMnvVfkCW3ER-K2WwYMT_EK2icPuTq1h0RtlTHe5E,17323
140
+ not1mm/plugins/cq_ww_cw.py,sha256=1sxwrvHGbzErQSXFSWw7wpg6DEKXrjHfE7SK2noCcjE,17700
141
+ not1mm/plugins/cq_ww_rtty.py,sha256=DaofxRXnbt095FJ-uZ4B_Wx6P12mdO6c_nQFhpMp0RU,22335
142
+ not1mm/plugins/cq_ww_ssb.py,sha256=QKOTw842eF9a4WiNZB-f16YE4zm2mIc0JxmU46URXUk,17299
144
143
  not1mm/plugins/cwt.py,sha256=duG8CgTWaFCjly7zOmhOJo-sH3amby0OarAjswhz1Cc,17370
145
144
  not1mm/plugins/general_logging.py,sha256=IHcgZ1YJEEaxPUn7jyTIUpQAsa1jaHO5zfHvcaqbF34,3494
146
145
  not1mm/plugins/helvetia.py,sha256=Q-dQgJ5-81jwip0J_JE2XdUtkY1dVlD8bPfVbllwGLU,19925
@@ -150,20 +149,20 @@ not1mm/plugins/iaru_hf.py,sha256=Lg1rNWcLKDXR_AAFoMs-velZxNMTO_7kiJeT-j0A2wo,124
150
149
  not1mm/plugins/icwc_mst.py,sha256=N41Qg1sGAGkjx87uWUE77U-VvJmNXkCR_WfKdKg5nSk,12750
151
150
  not1mm/plugins/jidx_cw.py,sha256=Mw5U25jxXJiHx8YEBXz6VPVVQ7rQb9LREYic6hVaMs0,13043
152
151
  not1mm/plugins/jidx_ph.py,sha256=qhF_NQzx2RiM23CY9qPd4Vg_zFmnzSivvPXv4HpPLBY,12073
153
- not1mm/plugins/k1usn_sst.py,sha256=P0HxQe_E38-jycpC17QuvzymkHG3Y8XkFkSSf3udRT0,16705
154
- not1mm/plugins/naqp_cw.py,sha256=_51kobPyfxEuL7wU3sG42g0gqRW9hP5r09wM9DzHvJM,18630
155
- not1mm/plugins/naqp_rtty.py,sha256=wMWoHW0VwydkrnKc6vSTazXTKf1iFsbLqeVhR8rtuts,22314
156
- not1mm/plugins/naqp_ssb.py,sha256=ZAhpJtcmlTqZ-BGDS1jIirG64KWqG5TpvXeYZisawnE,17547
152
+ not1mm/plugins/k1usn_sst.py,sha256=Z7r8Bz2vsWKbA4MNVoXdfdH3oSOvuftoWY2_X_hDJWc,16681
153
+ not1mm/plugins/naqp_cw.py,sha256=mV32k78KE07YlyALW26y-TAITkVwDOn1gEoMrHj-7Qs,18606
154
+ not1mm/plugins/naqp_rtty.py,sha256=P70eIvjZHMNTjYcZZ8IOXPXtNzGmLWJdYxwOqnzCThg,22290
155
+ not1mm/plugins/naqp_ssb.py,sha256=6DbOUsbKMxDHw8m1OcyqtjVUL3qqZ2A5bqQPjYbbJ6M,17523
157
156
  not1mm/plugins/phone_weekly_test.py,sha256=q0n8RJ39KF9uzkgTwrRqTlnS1l_xpm86UTsYRtD6Qok,13244
158
- not1mm/plugins/raem.py,sha256=yCpBRFmDZ3W4o3e2LUCgBHCGDSHtpsTCmlgWpdZPAw0,19474
157
+ not1mm/plugins/raem.py,sha256=7CSTaMqINB3QVxzqWRZxpxuoXQoCx10J_V49GyXAHWI,19450
159
158
  not1mm/plugins/ref_cw.py,sha256=bLu1BIHnMfJJAw-pcLDVSi93vTMBY8Io1I4SkJSRX0E,20916
160
159
  not1mm/plugins/ref_ssb.py,sha256=Z6ZqNInyGFwWNSHXrzCDlokMxZ6NQQ2Yi1c8CGfmNWE,20922
161
- not1mm/plugins/stew_perry_topband.py,sha256=Q0yPpX-0Ua1dn4II-wkC-nezffJmi5aLK3aN6MkZyfw,11903
162
- not1mm/plugins/weekly_rtty.py,sha256=BRunxOiI_itSFjq6g442QmmP-KUjjis6jpIi7CKExIM,19520
163
- not1mm/plugins/winter_field_day.py,sha256=B3HkoInwV7sJLnUUso9nQvU1nZAtNTAeXZV5H5Yl-2Y,14915
164
- not1mm-24.11.6.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
165
- not1mm-24.11.6.dist-info/METADATA,sha256=ePkUxibsgwdZbtetkk6ocxFVRVq_xrPOP6cdV2kKjBc,34175
166
- not1mm-24.11.6.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
167
- not1mm-24.11.6.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
168
- not1mm-24.11.6.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
169
- not1mm-24.11.6.dist-info/RECORD,,
160
+ not1mm/plugins/stew_perry_topband.py,sha256=DXbJYLJ7JpPotdvax74d2YOX5HaMUc-Fk1XvBXMhl9I,11879
161
+ not1mm/plugins/weekly_rtty.py,sha256=OLiLW3Xd-tylSy9onOXCxQwWfijx-O5PAgjHh7_vG1o,19496
162
+ not1mm/plugins/winter_field_day.py,sha256=E4Rn7bOpN2LNoRi4_aRDHd_8p5lxn1vG_ubGdzn5zB0,14891
163
+ not1mm-24.11.12.1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
164
+ not1mm-24.11.12.1.dist-info/METADATA,sha256=Fo3V88_JTL80XCoXagVEDBGDiKu3HxUxA3KZKeTsXR0,34306
165
+ not1mm-24.11.12.1.dist-info/WHEEL,sha256=a7TGlA-5DaHMRrarXjVbQagU3Man_dCnGIWMJr5kRWo,91
166
+ not1mm-24.11.12.1.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
167
+ not1mm-24.11.12.1.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
168
+ not1mm-24.11.12.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.3.0)
2
+ Generator: setuptools (75.4.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
not1mm/lib/playsound.py DELETED
@@ -1,296 +0,0 @@
1
- import logging
2
- from platform import system
3
-
4
- logger = logging.getLogger(__name__)
5
-
6
-
7
- class PlaysoundException(Exception):
8
- pass
9
-
10
-
11
- def _canonicalizePath(path):
12
- """
13
- Support passing in a pathlib.Path-like object by converting to str.
14
- """
15
- import sys
16
-
17
- if sys.version_info[0] >= 3:
18
- return str(path)
19
- else:
20
- # On earlier Python versions, str is a byte string, so attempting to
21
- # convert a unicode string to str will fail. Leave it alone in this case.
22
- return path
23
-
24
-
25
- def _playsoundWin(sound, block=True):
26
- """
27
- Utilizes windll.winmm. Tested and known to work with MP3 and WAVE on
28
- Windows 7 with Python 2.7. Probably works with more file formats.
29
- Probably works on Windows XP thru Windows 10. Probably works with all
30
- versions of Python.
31
-
32
- Inspired by (but not copied from) Michael Gundlach <gundlach@gmail.com>'s mp3play:
33
- https://github.com/michaelgundlach/mp3play
34
-
35
- I never would have tried using windll.winmm without seeing his code.
36
- """
37
- sound = '"' + _canonicalizePath(sound) + '"'
38
-
39
- from ctypes import create_unicode_buffer, windll, wintypes
40
-
41
- windll.winmm.mciSendStringW.argtypes = [
42
- wintypes.LPCWSTR,
43
- wintypes.LPWSTR,
44
- wintypes.UINT,
45
- wintypes.HANDLE,
46
- ]
47
- windll.winmm.mciGetErrorStringW.argtypes = [
48
- wintypes.DWORD,
49
- wintypes.LPWSTR,
50
- wintypes.UINT,
51
- ]
52
-
53
- def winCommand(*command):
54
- bufLen = 600
55
- buf = create_unicode_buffer(bufLen)
56
- command = " ".join(command)
57
- errorCode = int(
58
- windll.winmm.mciSendStringW(command, buf, bufLen - 1, 0)
59
- ) # use widestring version of the function
60
- if errorCode:
61
- errorBuffer = create_unicode_buffer(bufLen)
62
- windll.winmm.mciGetErrorStringW(
63
- errorCode, errorBuffer, bufLen - 1
64
- ) # use widestring version of the function
65
- exceptionMessage = (
66
- "\n Error " + str(errorCode) + " for command:"
67
- "\n " + command + "\n " + errorBuffer.value
68
- )
69
- logger.error(exceptionMessage)
70
- raise PlaysoundException(exceptionMessage)
71
- return buf.value
72
-
73
- try:
74
- logger.debug("Starting")
75
- winCommand("open {}".format(sound))
76
- winCommand("play {}{}".format(sound, " wait" if block else ""))
77
- logger.debug("Returning")
78
- finally:
79
- try:
80
- winCommand("close {}".format(sound))
81
- except PlaysoundException:
82
- logger.warning("Failed to close the file: {}".format(sound))
83
- # If it fails, there's nothing more that can be done...
84
- pass
85
-
86
-
87
- def _handlePathOSX(sound):
88
- sound = _canonicalizePath(sound)
89
-
90
- if "://" not in sound:
91
- if not sound.startswith("/"):
92
- from os import getcwd
93
-
94
- sound = getcwd() + "/" + sound
95
- sound = "file://" + sound
96
-
97
- try:
98
- # Don't double-encode it.
99
- sound.encode("ascii")
100
- return sound.replace(" ", "%20")
101
- except UnicodeEncodeError:
102
- try:
103
- from urllib.parse import quote # Try the Python 3 import first...
104
- except ImportError:
105
- from urllib import (
106
- quote,
107
- ) # Try using the Python 2 import before giving up entirely...
108
-
109
- parts = sound.split("://", 1)
110
- return parts[0] + "://" + quote(parts[1].encode("utf-8")).replace(" ", "%20")
111
-
112
-
113
- def _playsoundOSX(sound, block=True):
114
- """
115
- Utilizes AppKit.NSSound. Tested and known to work with MP3 and WAVE on
116
- OS X 10.11 with Python 2.7. Probably works with anything QuickTime supports.
117
- Probably works on OS X 10.5 and newer. Probably works with all versions of
118
- Python.
119
-
120
- Inspired by (but not copied from) Aaron's Stack Overflow answer here:
121
- http://stackoverflow.com/a/34568298/901641
122
-
123
- I never would have tried using AppKit.NSSound without seeing his code.
124
- """
125
- try:
126
- from AppKit import NSSound
127
- except ImportError:
128
- logger.warning(
129
- "playsound could not find a copy of AppKit - falling back to using macOS's system copy."
130
- )
131
- sys.path.append(
132
- "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC"
133
- )
134
- from AppKit import NSSound
135
-
136
- from Foundation import NSURL
137
- from time import sleep
138
-
139
- sound = _handlePathOSX(sound)
140
- url = NSURL.URLWithString_(sound)
141
- if not url:
142
- raise PlaysoundException("Cannot find a sound with filename: " + sound)
143
-
144
- for i in range(5):
145
- nssound = NSSound.alloc().initWithContentsOfURL_byReference_(url, True)
146
- if nssound:
147
- break
148
- else:
149
- logger.debug("Failed to load sound, although url was good... " + sound)
150
- else:
151
- raise PlaysoundException(
152
- "Could not load sound with filename, although URL was good... " + sound
153
- )
154
- nssound.play()
155
-
156
- if block:
157
- sleep(nssound.duration())
158
-
159
-
160
- def _playsoundNix(sound, block=True):
161
- """Play a sound using GStreamer.
162
-
163
- Inspired by this:
164
- https://gstreamer.freedesktop.org/documentation/tutorials/playback/playbin-usage.html
165
- """
166
- sound = _canonicalizePath(sound)
167
-
168
- # pathname2url escapes non-URL-safe characters
169
- from os.path import abspath, exists
170
-
171
- try:
172
- from urllib.request import pathname2url
173
- except ImportError:
174
- # python 2
175
- from urllib import pathname2url
176
-
177
- import gi
178
-
179
- gi.require_version("Gst", "1.0")
180
- from gi.repository import Gst
181
-
182
- Gst.init(None)
183
-
184
- playbin = Gst.ElementFactory.make("playbin", "playbin")
185
- if sound.startswith(("http://", "https://")):
186
- playbin.props.uri = sound
187
- else:
188
- path = abspath(sound)
189
- if not exists(path):
190
- raise PlaysoundException("File not found: {}".format(path))
191
- playbin.props.uri = "file://" + pathname2url(path)
192
-
193
- set_result = playbin.set_state(Gst.State.PLAYING)
194
- if set_result != Gst.StateChangeReturn.ASYNC:
195
- raise PlaysoundException("playbin.set_state returned " + repr(set_result))
196
-
197
- # FIXME: use some other bus method than poll() with block=False
198
- # https://lazka.github.io/pgi-docs/#Gst-1.0/classes/Bus.html
199
- logger.debug("Starting play")
200
- if block:
201
- bus = playbin.get_bus()
202
- try:
203
- bus.poll(Gst.MessageType.EOS, Gst.CLOCK_TIME_NONE)
204
- finally:
205
- playbin.set_state(Gst.State.NULL)
206
-
207
- logger.debug("Finishing play")
208
-
209
-
210
- def _playsoundAnotherPython(otherPython, sound, block=True, macOS=False):
211
- """
212
- Mostly written so that when this is run on python3 on macOS, it can invoke
213
- python2 on macOS... but maybe this idea could be useful on linux, too.
214
- """
215
- from inspect import getsourcefile
216
- from os.path import abspath, exists
217
- from subprocess import check_call
218
- from threading import Thread
219
-
220
- sound = _canonicalizePath(sound)
221
-
222
- class PropogatingThread(Thread):
223
- def run(self):
224
- self.exc = None
225
- try:
226
- self.ret = self._target(*self._args, **self._kwargs)
227
- except BaseException as e:
228
- self.exc = e
229
-
230
- def join(self, timeout=None):
231
- super().join(timeout)
232
- if self.exc:
233
- raise self.exc
234
- return self.ret
235
-
236
- # Check if the file exists...
237
- if not exists(abspath(sound)):
238
- raise PlaysoundException("Cannot find a sound with filename: " + sound)
239
-
240
- playsoundPath = abspath(getsourcefile(lambda: 0))
241
- t = PropogatingThread(
242
- target=lambda: check_call(
243
- [otherPython, playsoundPath, _handlePathOSX(sound) if macOS else sound]
244
- )
245
- )
246
- t.start()
247
- if block:
248
- t.join()
249
-
250
-
251
- system = system()
252
-
253
- if system == "Windows":
254
- playsound = _playsoundWin
255
- elif system == "Darwin":
256
- playsound = _playsoundOSX
257
- import sys
258
-
259
- if sys.version_info[0] > 2:
260
- try:
261
- from AppKit import NSSound
262
- except ImportError:
263
- logger.warning(
264
- "playsound is relying on a python 2 subprocess. Please use `pip3 install PyObjC` if you want playsound to run more efficiently."
265
- )
266
- playsound = lambda sound, block=True: _playsoundAnotherPython(
267
- "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python",
268
- sound,
269
- block,
270
- macOS=True,
271
- )
272
- else:
273
- playsound = _playsoundNix
274
- if (
275
- __name__ != "__main__"
276
- ): # Ensure we don't infinitely recurse trying to get another python instance.
277
- try:
278
- import gi
279
-
280
- gi.require_version("Gst", "1.0")
281
- from gi.repository import Gst
282
- except:
283
- logger.warning(
284
- "playsound is relying on another python subprocess. Please use `pip install pygobject` if you want playsound to run more efficiently."
285
- )
286
- playsound = lambda sound, block=True: _playsoundAnotherPython(
287
- "/usr/bin/python3", sound, block, macOS=False
288
- )
289
-
290
- del system
291
-
292
- if __name__ == "__main__":
293
- # block is always True if you choose to run this from the command line.
294
- from sys import argv
295
-
296
- playsound(argv[1])