KdeConnect.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import subprocess
  2. import logging
  3. import re
  4. from pydantic import BaseModel
  5. class InvalidArgument(Exception):
  6. pass
  7. class InvalidDevice(Exception):
  8. pass
  9. class KDEConnectError(Exception):
  10. pass
  11. class KDEDevice(BaseModel):
  12. id: str
  13. name: str
  14. paired: bool
  15. reachable: bool
  16. class KDEConnect:
  17. device: KDEDevice
  18. def __init__(self, device_name=None, device_id=None):
  19. if device_name is None and device_id is None:
  20. raise InvalidArgument("device_name or device_id need to be provided")
  21. try:
  22. # Verify kdeconnect is available
  23. KDEConnect.run_kde_command(["-v"])
  24. except FileNotFoundError:
  25. raise KDEConnectError("KDEconnect-cli is not available on this computer")
  26. # verify the device exist
  27. devices = KDEConnect.get_device_list()
  28. device = None
  29. for d in devices:
  30. if (d.name == device_name) or (d.id == device_id):
  31. device = d
  32. break
  33. if device is None:
  34. raise InvalidDevice(f"No device found with id {device_id} or name {device_name}")
  35. if not device.paired:
  36. raise InvalidDevice(f"Device {device.name} is not paired with this computer")
  37. if not device.reachable:
  38. raise InvalidDevice(f"Device {device.name} is not reachable by this computer")
  39. self.device = device
  40. def send_sms(self, phone_number: str, sms_content: str):
  41. # Compact phone number by removing space
  42. phone = re.sub("[^+0-9]", "", phone_number)
  43. command = [
  44. "-d",
  45. self.device.id,
  46. "--send-sms",
  47. sms_content,
  48. "--destination",
  49. phone,
  50. ]
  51. KDEConnect.run_kde_command(command)
  52. @staticmethod
  53. def run_kde_command(args: list[str]) -> str:
  54. command: list[str] = ["kdeconnect-cli"]
  55. command.extend(args)
  56. process: subprocess.CompletedProcess = subprocess.run(command, capture_output=True)
  57. subprocess_response = process.stdout.decode("utf-8")
  58. logging.debug("Subprocess call : \n>> " + " ".join(command) + "\n" + subprocess_response)
  59. if process.stderr != b"":
  60. error_msg: str = process.stderr.decode("utf-8")
  61. if error_msg.endswith("devices found\n"):
  62. logging.info(error_msg)
  63. else:
  64. raise KDEConnectError(error_msg)
  65. return subprocess_response
  66. @staticmethod
  67. def get_device_list() -> list[KDEDevice]:
  68. """Return the list of devices known by kde connect of this computer"""
  69. KDEConnect.run_kde_command(["--refresh"])
  70. output: list[KDEDevice] = []
  71. device_entries: list[str] = KDEConnect.run_kde_command(["-l"]).strip().split("\n")
  72. for device_str in device_entries:
  73. if device_str == "":
  74. continue
  75. result = re.search("^- ([^:]+): ([a-z0-9_]+)", device_str)
  76. reachable = "reachable" in device_str
  77. paired = "paired" in device_str
  78. device = KDEDevice(
  79. name=result.group(1),
  80. id=result.group(2),
  81. reachable=reachable,
  82. paired=paired,
  83. )
  84. output.append(device)
  85. return output
  86. if __name__ == "__main__":
  87. for d in KDEConnect.get_device_list():
  88. print(d.name, d.reachable)