__init__.py 4.1 KB

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