__init__.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import logging
  2. import time
  3. import cc1101
  4. _ADDRESS_LENGTH_BITS = 26
  5. _COMMAND_LENGTH_BITS = 2
  6. _BUTTON_INDEX_LENGTH_BITS = 4
  7. _MESSAGE_LENGTH_BITS = (
  8. _ADDRESS_LENGTH_BITS + _COMMAND_LENGTH_BITS + _BUTTON_INDEX_LENGTH_BITS
  9. )
  10. # > [...] default PATABLE setting (0xC6).
  11. # "24 Output Power Programming" in CC1101's docs
  12. DEFAULT_POWER_SETTING = 0xC6
  13. _LOGGER = logging.getLogger(__name__)
  14. def _cc1101_transmit(payload: bytes, power_setting: int, repeats: int = 3) -> None:
  15. with cc1101.CC1101(lock_spi_device=True) as transceiver:
  16. transceiver.set_base_frequency_hertz(433.93e6)
  17. transceiver.set_symbol_rate_baud(3942)
  18. transceiver.set_sync_mode(cc1101.SyncMode.NO_PREAMBLE_AND_SYNC_WORD)
  19. transceiver.set_packet_length_mode(cc1101.PacketLengthMode.FIXED)
  20. transceiver.set_packet_length_bytes(len(payload))
  21. transceiver.disable_checksum()
  22. transceiver.set_output_power((0, power_setting)) # OOK
  23. _LOGGER.info("%s", transceiver)
  24. for _ in range(repeats):
  25. transceiver.transmit(payload)
  26. time.sleep(0.08)
  27. def _encode_message(message: int) -> bytes:
  28. signal = [0b00000100, 0]
  29. assert 0 <= message < 2 ** _MESSAGE_LENGTH_BITS, message
  30. for bit_index in reversed(range(_MESSAGE_LENGTH_BITS)):
  31. if message & (0b1 << bit_index):
  32. signal.append(0b10000010)
  33. else:
  34. signal.append(0b10100000)
  35. signal.append(0b10000000)
  36. return bytes(signal)
  37. class RemoteControl:
  38. def __init__(self, address: int) -> None:
  39. if not isinstance(address, int):
  40. raise ValueError(
  41. "expected {}-bit unsigned integer as address, got {!r}".format(
  42. _ADDRESS_LENGTH_BITS, address
  43. )
  44. )
  45. if address < 0:
  46. raise ValueError("address must not be negative (got {!r})".format(address))
  47. if address >= 2 ** _ADDRESS_LENGTH_BITS:
  48. raise ValueError(
  49. "address must not exceed {} ({}-bit unsigned integer), got {!r}".format(
  50. 2 ** _ADDRESS_LENGTH_BITS, _ADDRESS_LENGTH_BITS, address
  51. )
  52. )
  53. self._address = address
  54. def _send_command(
  55. self, command: int, button_index: int, power_setting: int
  56. ) -> None:
  57. assert 0 <= command < 2 ** _COMMAND_LENGTH_BITS
  58. if not isinstance(button_index, int):
  59. raise ValueError(
  60. "expected {}-bit unsigned integer as button index, got {!r}".format(
  61. _BUTTON_INDEX_LENGTH_BITS, button_index
  62. )
  63. )
  64. if button_index < 0:
  65. raise ValueError(
  66. "button index must not be negative (got {!r})".format(button_index)
  67. )
  68. if button_index >= 2 ** _BUTTON_INDEX_LENGTH_BITS:
  69. raise ValueError(
  70. "button index must not exceed {} ({}-bit unsigned integer), got {!r}".format(
  71. 2 ** _BUTTON_INDEX_LENGTH_BITS,
  72. _BUTTON_INDEX_LENGTH_BITS,
  73. button_index,
  74. )
  75. )
  76. _cc1101_transmit(
  77. _encode_message(
  78. (self._address << _COMMAND_LENGTH_BITS | command)
  79. << _BUTTON_INDEX_LENGTH_BITS
  80. | button_index
  81. ),
  82. power_setting=power_setting,
  83. )
  84. def turn_on(
  85. self,
  86. button_index: int,
  87. power_setting: int = DEFAULT_POWER_SETTING,
  88. ) -> None:
  89. """
  90. Consult section "Table 39: Optimum PATABLE Settings
  91. for Various Output Power Levels and Frequency Bands [...]"
  92. in CC1101's official documentation for `power_setting`.
  93. """
  94. self._send_command(
  95. command=0b01,
  96. button_index=button_index,
  97. power_setting=power_setting,
  98. )
  99. def turn_off(
  100. self,
  101. button_index: int,
  102. power_setting: int = DEFAULT_POWER_SETTING,
  103. ) -> None:
  104. self._send_command(
  105. command=0b00,
  106. button_index=button_index,
  107. power_setting=power_setting,
  108. )