3 Commits 760808ec73 ... 4414282f12

Author SHA1 Message Date
  Fabian Peter Hammerle 4414282f12 test against python3.9 3 years ago
  Fabian Peter Hammerle 2f871fcb9e release v2.7.0 3 years ago
  Fabian Peter Hammerle 3345c1b9b1 all commands: added parameter -p/--output-power 3 years ago
6 changed files with 103 additions and 24 deletions
  1. 6 3
      .github/workflows/python.yml
  2. 5 1
      CHANGELOG.md
  3. 23 1
      cc1101/_cli.py
  4. 1 0
      setup.py
  5. 32 9
      tests/test_cli_export_config.py
  6. 36 10
      tests/test_cli_transmit.py

+ 6 - 3
.github/workflows/python.yml

@@ -39,6 +39,7 @@ jobs:
         - 3.6
         - 3.7
         - 3.8
+        - 3.9
       fail-fast: false
     steps:
     - uses: actions/checkout@v1
@@ -50,11 +51,13 @@ jobs:
       env:
         PYTHON_VERSION: ${{ matrix.python-version }}
     - run: pipenv graph
-    - run: pipenv run pytest --cov=cc1101 --cov-report=term-missing --cov-fail-under=94
-    - run: pipenv run pylint --load-plugins=pylint_import_requirements cc1101
+    - run: pipenv run pytest --cov="$(cat *.egg-info/top_level.txt)" --cov-report=term-missing --cov-fail-under=94
+    # https://github.com/PyCQA/pylint/issues/3882
+    - run: python3 -c 'import sys; sys.exit(sys.version_info < (3, 9))'
+        || pipenv run pylint --load-plugins=pylint_import_requirements "$(cat *.egg-info/top_level.txt)"
     # https://github.com/PyCQA/pylint/issues/352
     - run: pipenv run pylint tests/*
-    - run: pipenv run mypy cc1101 tests
+    - run: pipenv run mypy "$(cat *.egg-info/top_level.txt)" tests
     # >=1.9.0 to detect branch name
     # https://github.com/coveralls-clients/coveralls-python/pull/207
     # https://github.com/coverallsapp/github-action/issues/4#issuecomment-547036866

+ 5 - 1
CHANGELOG.md

@@ -5,9 +5,12 @@ 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).
 
 ## [Unreleased]
+
+## [2.7.0] - 2021-01-24
 ### Added
 - method `set/get_output_power` to configure/retrieve output power settings
   (`PATABLE` and `FREND0.PA_POWER`)
+- all commands: added parameter `-p/--output-power`
 - command `cc1101-export-config`: append values of `PATABLE` register as comment
 
 ### Fixed
@@ -93,7 +96,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - disabled data whitening
 - automatic calibration
 
-[Unreleased]: https://github.com/fphammerle/python-cc1101/compare/v2.6.1...HEAD
+[Unreleased]: https://github.com/fphammerle/python-cc1101/compare/v2.7.0...HEAD
+[2.7.0]: https://github.com/fphammerle/python-cc1101/compare/v2.6.1...v2.7.0
 [2.6.1]: https://github.com/fphammerle/python-cc1101/compare/v2.6.0...v2.6.1
 [2.6.0]: https://github.com/fphammerle/python-cc1101/compare/v2.5.0...v2.6.0
 [2.5.0]: https://github.com/fphammerle/python-cc1101/compare/v2.4.0...v2.5.0

+ 23 - 1
cc1101/_cli.py

@@ -42,6 +42,26 @@ def _add_common_args(argparser: argparse.ArgumentParser) -> None:
         choices=[m.name.lower() for m in cc1101.options.PacketLengthMode],
     )
     argparser.add_argument("--disable-checksum", action="store_true")
+    argparser.add_argument(
+        "-p",
+        "--output-power",
+        metavar="SETTING",
+        dest="output_power_settings",
+        type=int,
+        nargs="+",
+        help="Configures output power levels by setting PATABLE and FREND0.PA_POWER."
+        " Up to 8 bytes may be provided."
+        # add when making _set_modulation_format() public
+        # ' "[PATABLE] provides flexible PA power ramp up and ramp down'
+        # " at the start and end of transmission when using 2-FSK, GFSK,"
+        # ' 4-FSK, and MSK modulation as well as ASK modulation shaping."'
+        " For OOK modulation, exactly 2 bytes must be provided:"
+        " 0 to turn off the transmission for logical 0,"
+        " and a level > 0 to turn on the transmission for logical 1"
+        " (e.g., --output-power 0 198)."
+        ' See "Table 39: Optimum PATABLE Settings for Various Output Power Levels [...]"'
+        ' and section "24 Output Power Programming".',
+    )
     argparser.add_argument("-d", "--debug", action="store_true")
 
 
@@ -82,6 +102,8 @@ def _configure_via_args(
             transceiver.set_packet_length_bytes(packet_length_if_fixed)
     if args.disable_checksum:
         transceiver.disable_checksum()
+    if args.output_power_settings:
+        transceiver.set_output_power(args.output_power_settings)
 
 
 def _export_config():
@@ -122,7 +144,7 @@ def _export_config():
 def _transmit():
     argparser = argparse.ArgumentParser(
         description="Transmits the payload provided via standard input (stdin)"
-        " OOK-modulated in big-endian bit order.",
+        " ASK/OOK-modulated in big-endian bit order.",
         allow_abbrev=False,
     )
     _add_common_args(argparser)

+ 1 - 0
setup.py

@@ -54,6 +54,7 @@ setuptools.setup(
         "Programming Language :: Python :: 3.6",
         "Programming Language :: Python :: 3.7",
         "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
         "Topic :: Home Automation",
         "Topic :: Communications",
     ],

+ 32 - 9
tests/test_cli_export_config.py

@@ -54,6 +54,31 @@ def test_configure_device(args, packet_length_mode, checksum_disabled):
         transceiver_mock.disable_checksum.assert_not_called()
 
 
+@pytest.mark.parametrize(
+    ("args", "output_power_settings"),
+    (
+        ([""], None),
+        (["", "-p", "192"], [192]),
+        (["", "--output-power", "192"], [192]),
+        (["", "-p", "0", "192"], [0, 192]),  # OOK
+        (
+            ["", "-p", "3", "15", "30", "39", "80", "129", "203", "194"],
+            [3, 15, 30, 39, 80, 129, 203, 194],
+        ),
+    ),
+)
+def test_configure_device_output_power_settings(args, output_power_settings):
+    with unittest.mock.patch("cc1101.CC1101") as transceiver_mock:
+        with unittest.mock.patch("sys.argv", args):
+            cc1101._cli._export_config()
+    if output_power_settings is None:
+        transceiver_mock().__enter__().set_output_power.assert_not_called()
+    else:
+        transceiver_mock().__enter__().set_output_power.assert_called_with(
+            output_power_settings
+        )
+
+
 def test_export_python_list(capsys, caplog):
     with unittest.mock.patch("cc1101.CC1101") as transceiver_mock:
         transceiver_mock().__enter__().get_configuration_register_values.return_value = {
@@ -103,12 +128,10 @@ def test_logging(caplog):
     ) as transceiver_mock, caplog.at_level(logging.DEBUG):
         transceiver_mock().__enter__().__str__.return_value = "dummystr"
         cc1101._cli._export_config()
-    assert caplog.record_tuples == [
-        (
-            "cc1101._cli",
-            logging.DEBUG,
-            "args=Namespace(base_frequency_hertz=None, debug=False, disable_checksum=False, "
-            "format='python-list', packet_length_mode=None, symbol_rate_baud=None, sync_mode=None)",
-        ),
-        ("cc1101._cli", logging.INFO, "dummystr"),
-    ]
+    assert len(caplog.records) == 2
+    assert caplog.records[0].levelno == logging.DEBUG
+    assert caplog.records[0].name == "cc1101._cli"
+    assert caplog.records[0].message.startswith(
+        "args=Namespace(base_frequency_hertz=None, "
+    )
+    assert caplog.record_tuples[1] == ("cc1101._cli", logging.INFO, "dummystr")

+ 36 - 10
tests/test_cli_transmit.py

@@ -145,6 +145,35 @@ def test_configure_device(
         transceiver_mock().__enter__().disable_checksum.assert_not_called()
 
 
+@pytest.mark.parametrize(
+    ("args", "output_power_settings"),
+    (
+        ([""], None),
+        (["", "-p", "198"], [198]),  # default
+        (["", "--output-power", "198"], [198]),
+        (["", "-p", "0", "198"], [0, 198]),  # OOK
+        (
+            ["", "-p", "18", "14", "29", "52", "96", "132", "200", "192"],
+            [18, 14, 29, 52, 96, 132, 200, 192],
+        ),
+    ),
+)
+def test_configure_device_output_power(args, output_power_settings):
+    with unittest.mock.patch("cc1101.CC1101") as transceiver_mock:
+        stdin_mock = unittest.mock.MagicMock()
+        stdin_mock.buffer = io.BytesIO(b"message")
+        with unittest.mock.patch("sys.stdin", stdin_mock):
+            with unittest.mock.patch("sys.argv", args):
+                cc1101._cli._transmit()
+    transceiver_mock.assert_called_once_with(lock_spi_device=True)
+    if output_power_settings:
+        transceiver_mock().__enter__().set_output_power.assert_called_with(
+            output_power_settings
+        )
+    else:
+        transceiver_mock().__enter__().set_output_power.assert_not_called()
+
+
 @pytest.mark.parametrize(
     ("args", "root_log_level", "log_format"),
     (
@@ -182,13 +211,10 @@ def test_logging(caplog, payload):
     ):
         transceiver_mock().__enter__().__str__.return_value = "dummy"
         cc1101._cli._transmit()
-    assert caplog.record_tuples == [
-        (
-            "cc1101._cli",
-            logging.DEBUG,
-            "args=Namespace(base_frequency_hertz=None, debug=False, "
-            "disable_checksum=False, packet_length_mode=None, symbol_rate_baud=None, "
-            "sync_mode=None)",
-        ),
-        ("cc1101._cli", logging.INFO, "dummy"),
-    ]
+    assert len(caplog.records) == 2
+    assert caplog.records[0].name == "cc1101._cli"
+    assert caplog.records[0].levelno == logging.DEBUG
+    assert caplog.records[0].message.startswith(
+        "args=Namespace(base_frequency_hertz=None, "
+    )
+    assert caplog.record_tuples[1] == ("cc1101._cli", logging.INFO, "dummy")