Browse Source

added optional kwarg `output_power_setting` to methods `turn_on/off`; added constant `DEFAULT_OUTPUT_POWER_SETTING`

Fabian Peter Hammerle 3 years ago
parent
commit
744d7265c7
4 changed files with 105 additions and 17 deletions
  1. 4 0
      CHANGELOG.md
  2. 38 8
      intertechno_cc1101/__init__.py
  3. 6 3
      tests/test_cc1101_config.py
  4. 57 6
      tests/test_remote_control.py

+ 4 - 0
CHANGELOG.md

@@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 
 ## [Unreleased]
 ## [Unreleased]
+### Added
+- constant `DEFAULT_OUTPUT_POWER_SETTING`
+- methods `RemoteControl.turn_on/off`: optional keyword argument `output_power_setting`
+
 ### Changed
 ### Changed
 - command `intertechno-cc1101`: no longer allow abbreviation of options / flags
 - command `intertechno-cc1101`: no longer allow abbreviation of options / flags
 - raise `ValueError` instead of `AssertionError` when provided with
 - raise `ValueError` instead of `AssertionError` when provided with

+ 38 - 8
intertechno_cc1101/__init__.py

@@ -10,10 +10,16 @@ _MESSAGE_LENGTH_BITS = (
     _ADDRESS_LENGTH_BITS + _COMMAND_LENGTH_BITS + _BUTTON_INDEX_LENGTH_BITS
     _ADDRESS_LENGTH_BITS + _COMMAND_LENGTH_BITS + _BUTTON_INDEX_LENGTH_BITS
 )
 )
 
 
+# > [...] default PATABLE setting (0xC6).
+# "24 Output Power Programming" in CC1101's docs
+DEFAULT_OUTPUT_POWER_SETTING = 0xC6
+
 _LOGGER = logging.getLogger(__name__)
 _LOGGER = logging.getLogger(__name__)
 
 
 
 
-def _cc1101_transmit(payload: bytes, repeats: int = 3) -> None:
+def _cc1101_transmit(
+    payload: bytes, output_power_setting: int, repeats: int = 3
+) -> None:
     with cc1101.CC1101(lock_spi_device=True) as transceiver:
     with cc1101.CC1101(lock_spi_device=True) as transceiver:
         transceiver.set_base_frequency_hertz(433.93e6)
         transceiver.set_base_frequency_hertz(433.93e6)
         transceiver.set_symbol_rate_baud(3942)
         transceiver.set_symbol_rate_baud(3942)
@@ -21,7 +27,7 @@ def _cc1101_transmit(payload: bytes, repeats: int = 3) -> None:
         transceiver.set_packet_length_mode(cc1101.PacketLengthMode.FIXED)
         transceiver.set_packet_length_mode(cc1101.PacketLengthMode.FIXED)
         transceiver.set_packet_length_bytes(len(payload))
         transceiver.set_packet_length_bytes(len(payload))
         transceiver.disable_checksum()
         transceiver.disable_checksum()
-        transceiver.set_output_power((0, 0xC6))  # OOK
+        transceiver.set_output_power((0, output_power_setting))  # OOK
         _LOGGER.info("%s", transceiver)
         _LOGGER.info("%s", transceiver)
         for _ in range(repeats):
         for _ in range(repeats):
             transceiver.transmit(payload)
             transceiver.transmit(payload)
@@ -58,7 +64,9 @@ class RemoteControl:
             )
             )
         self._address = address
         self._address = address
 
 
-    def _send_command(self, command: int, button_index: int) -> None:
+    def _send_command(
+        self, command: int, button_index: int, output_power_setting: int
+    ) -> None:
         assert 0 <= command < 2 ** _COMMAND_LENGTH_BITS
         assert 0 <= command < 2 ** _COMMAND_LENGTH_BITS
         if not isinstance(button_index, int):
         if not isinstance(button_index, int):
             raise ValueError(
             raise ValueError(
@@ -83,11 +91,33 @@ class RemoteControl:
                 (self._address << _COMMAND_LENGTH_BITS | command)
                 (self._address << _COMMAND_LENGTH_BITS | command)
                 << _BUTTON_INDEX_LENGTH_BITS
                 << _BUTTON_INDEX_LENGTH_BITS
                 | button_index
                 | button_index
-            )
+            ),
+            output_power_setting=output_power_setting,
         )
         )
 
 
-    def turn_on(self, button_index: int) -> None:
-        self._send_command(command=0b01, button_index=button_index)
+    def turn_on(
+        self,
+        button_index: int,
+        output_power_setting: int = DEFAULT_OUTPUT_POWER_SETTING,
+    ) -> None:
+        """
+        Consult section "Table 39: Optimum PATABLE Settings
+        for Various Output Power Levels and Frequency Bands [...]"
+        in CC1101's official documentation for `output_power_setting`.
+        """
+        self._send_command(
+            command=0b01,
+            button_index=button_index,
+            output_power_setting=output_power_setting,
+        )
 
 
-    def turn_off(self, button_index: int) -> None:
-        self._send_command(command=0b00, button_index=button_index)
+    def turn_off(
+        self,
+        button_index: int,
+        output_power_setting: int = DEFAULT_OUTPUT_POWER_SETTING,
+    ) -> None:
+        self._send_command(
+            command=0b00,
+            button_index=button_index,
+            output_power_setting=output_power_setting,
+        )

+ 6 - 3
tests/test_cc1101_config.py

@@ -15,9 +15,12 @@ import intertechno_cc1101
         b"\xa0\xa0\x82\xa0\x82\xa0\xa0\x82\x82\x82\xa0\xa0\x82\xa0\xa0\xa0\xa0\x80",
         b"\xa0\xa0\x82\xa0\x82\xa0\xa0\x82\x82\x82\xa0\xa0\x82\xa0\xa0\xa0\xa0\x80",
     ),
     ),
 )
 )
-def test__cc1101_transmit(payload):
+@pytest.mark.parametrize("output_power_setting", (0xC6, 0xC0))
+def test__cc1101_transmit(payload, output_power_setting):
     with unittest.mock.patch("cc1101.CC1101") as transceiver_class_mock:
     with unittest.mock.patch("cc1101.CC1101") as transceiver_class_mock:
-        intertechno_cc1101._cc1101_transmit(payload=payload)
+        intertechno_cc1101._cc1101_transmit(
+            payload=payload, output_power_setting=output_power_setting
+        )
     transceiver_class_mock.assert_called_once_with(lock_spi_device=True)
     transceiver_class_mock.assert_called_once_with(lock_spi_device=True)
     method_calls = transceiver_class_mock().__enter__().mock_calls
     method_calls = transceiver_class_mock().__enter__().mock_calls
     assert method_calls[:-3] == [
     assert method_calls[:-3] == [
@@ -27,7 +30,7 @@ def test__cc1101_transmit(payload):
         unittest.mock.call.set_packet_length_mode(cc1101.PacketLengthMode.FIXED),
         unittest.mock.call.set_packet_length_mode(cc1101.PacketLengthMode.FIXED),
         unittest.mock.call.set_packet_length_bytes(35),
         unittest.mock.call.set_packet_length_bytes(35),
         unittest.mock.call.disable_checksum(),
         unittest.mock.call.disable_checksum(),
-        unittest.mock.call.set_output_power((0, 0xC6)),
+        unittest.mock.call.set_output_power((0, output_power_setting)),
     ]
     ]
     transmit_call = method_calls[-3]
     transmit_call = method_calls[-3]
     assert transmit_call == ("transmit", (payload,), {})
     assert transmit_call == ("transmit", (payload,), {})

+ 57 - 6
tests/test_remote_control.py

@@ -17,23 +17,34 @@ def test___init___invalid_address(address):
     ("address", "button_index", "command", "expected_message"),
     ("address", "button_index", "command", "expected_message"),
     ((12345678, 0, 0b00, 790123392), (2 ** 26 - 1, 0b1111, 0b01, 4294967263)),
     ((12345678, 0, 0b00, 790123392), (2 ** 26 - 1, 0b1111, 0b01, 4294967263)),
 )
 )
-def test__send_command(address, button_index, command, expected_message):
+@pytest.mark.parametrize("output_power_setting", (0xC6, 0x84))
+def test__send_command(
+    address, button_index, command, expected_message, output_power_setting
+):
     remote_control = intertechno_cc1101.RemoteControl(address=address)
     remote_control = intertechno_cc1101.RemoteControl(address=address)
     with unittest.mock.patch(
     with unittest.mock.patch(
         "intertechno_cc1101._encode_message", return_value=b"dummy"
         "intertechno_cc1101._encode_message", return_value=b"dummy"
     ) as encode_message_mock, unittest.mock.patch(
     ) as encode_message_mock, unittest.mock.patch(
         "intertechno_cc1101._cc1101_transmit"
         "intertechno_cc1101._cc1101_transmit"
     ) as transmit_mock:
     ) as transmit_mock:
-        remote_control._send_command(button_index=button_index, command=command)
+        remote_control._send_command(
+            button_index=button_index,
+            command=command,
+            output_power_setting=output_power_setting,
+        )
     encode_message_mock.assert_called_once_with(expected_message)
     encode_message_mock.assert_called_once_with(expected_message)
-    transmit_mock.assert_called_once_with(b"dummy")
+    transmit_mock.assert_called_once_with(
+        b"dummy", output_power_setting=output_power_setting
+    )
 
 
 
 
 @pytest.mark.parametrize("button_index", (-1, 2 ** 6, 8.15))
 @pytest.mark.parametrize("button_index", (-1, 2 ** 6, 8.15))
 def test__send_command_invalid_button_index(button_index):
 def test__send_command_invalid_button_index(button_index):
     remote_control = intertechno_cc1101.RemoteControl(address=12345678)
     remote_control = intertechno_cc1101.RemoteControl(address=12345678)
     with pytest.raises(ValueError):
     with pytest.raises(ValueError):
-        remote_control._send_command(button_index=button_index, command=0b01)
+        remote_control._send_command(
+            button_index=button_index, command=0b01, output_power_setting=0xC6
+        )
 
 
 
 
 @pytest.mark.parametrize("address", [12345678])
 @pytest.mark.parametrize("address", [12345678])
@@ -44,7 +55,27 @@ def test_turn_on(address, button_index):
         remote_control, "_send_command"
         remote_control, "_send_command"
     ) as send_command_mock:
     ) as send_command_mock:
         remote_control.turn_on(button_index=button_index)
         remote_control.turn_on(button_index=button_index)
-    send_command_mock.assert_called_once_with(button_index=button_index, command=0b01)
+    send_command_mock.assert_called_once_with(
+        button_index=button_index, command=0b01, output_power_setting=0xC6
+    )
+
+
+@pytest.mark.parametrize("address", [12345678])
+@pytest.mark.parametrize("button_index", [7])
+@pytest.mark.parametrize("output_power_setting", [0x60])
+def test_turn_on_custom_output_power(address, button_index, output_power_setting):
+    remote_control = intertechno_cc1101.RemoteControl(address=address)
+    with unittest.mock.patch.object(
+        remote_control, "_send_command"
+    ) as send_command_mock:
+        remote_control.turn_on(
+            button_index=button_index, output_power_setting=output_power_setting
+        )
+    send_command_mock.assert_called_once_with(
+        button_index=button_index,
+        command=0b01,
+        output_power_setting=output_power_setting,
+    )
 
 
 
 
 @pytest.mark.parametrize("address", [12345678])
 @pytest.mark.parametrize("address", [12345678])
@@ -55,4 +86,24 @@ def test_turn_off(address, button_index):
         remote_control, "_send_command"
         remote_control, "_send_command"
     ) as send_command_mock:
     ) as send_command_mock:
         remote_control.turn_off(button_index=button_index)
         remote_control.turn_off(button_index=button_index)
-    send_command_mock.assert_called_once_with(button_index=button_index, command=0b00)
+    send_command_mock.assert_called_once_with(
+        button_index=button_index, command=0b00, output_power_setting=0xC6
+    )
+
+
+@pytest.mark.parametrize("address", [12345678])
+@pytest.mark.parametrize("button_index", [7])
+@pytest.mark.parametrize("output_power_setting", [0x60])
+def test_turn_off_custom_output_power(address, button_index, output_power_setting):
+    remote_control = intertechno_cc1101.RemoteControl(address=address)
+    with unittest.mock.patch.object(
+        remote_control, "_send_command"
+    ) as send_command_mock:
+        remote_control.turn_off(
+            button_index=button_index, output_power_setting=output_power_setting
+        )
+    send_command_mock.assert_called_once_with(
+        button_index=button_index,
+        command=0b00,
+        output_power_setting=output_power_setting,
+    )