Skip to content

Commit

Permalink
0.30.0 Release
Browse files Browse the repository at this point in the history
  • Loading branch information
Adminiuga authored May 10, 2022
2 parents 5672c50 + 75d66f2 commit 0a0e4da
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 26 deletions.
24 changes: 16 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ jobs:
${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
steps.python.outputs.python-version }}-${{
hashFiles('setup.py') }}-${{
hashFiles('requirements_test.txt') }}
hashFiles('requirements_test.txt') }}-${{
hashFiles('.pre-commit-config.yaml') }}
restore-keys: |
${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{ steps.python.outputs.python-version }}-
- name: Create Python virtual environment
Expand Down Expand Up @@ -69,7 +70,8 @@ jobs:
${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
steps.python.outputs.python-version }}-${{
hashFiles('setup.py') }}-${{
hashFiles('requirements_test.txt') }}
hashFiles('requirements_test.txt') }}-${{
hashFiles('.pre-commit-config.yaml') }}
- name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
Expand Down Expand Up @@ -111,7 +113,8 @@ jobs:
${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
steps.python.outputs.python-version }}-${{
hashFiles('setup.py') }}-${{
hashFiles('requirements_test.txt') }}
hashFiles('requirements_test.txt') }}-${{
hashFiles('.pre-commit-config.yaml') }}
- name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
Expand Down Expand Up @@ -155,7 +158,8 @@ jobs:
${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
steps.python.outputs.python-version }}-${{
hashFiles('setup.py') }}-${{
hashFiles('requirements_test.txt') }}
hashFiles('requirements_test.txt') }}-${{
hashFiles('.pre-commit-config.yaml') }}
- name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
Expand Down Expand Up @@ -202,7 +206,8 @@ jobs:
${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
steps.python.outputs.python-version }}-${{
hashFiles('setup.py') }}-${{
hashFiles('requirements_test.txt') }}
hashFiles('requirements_test.txt') }}-${{
hashFiles('.pre-commit-config.yaml') }}
- name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
Expand Down Expand Up @@ -246,7 +251,8 @@ jobs:
${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
steps.python.outputs.python-version }}-${{
hashFiles('setup.py') }}-${{
hashFiles('requirements_test.txt') }}
hashFiles('requirements_test.txt') }}-${{
hashFiles('.pre-commit-config.yaml') }}
- name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
Expand Down Expand Up @@ -297,7 +303,8 @@ jobs:
${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
steps.python.outputs.python-version }}-${{
hashFiles('setup.py') }}-${{
hashFiles('requirements_test.txt') }}
hashFiles('requirements_test.txt') }}-${{
hashFiles('.pre-commit-config.yaml') }}
- name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
Expand Down Expand Up @@ -360,7 +367,8 @@ jobs:
${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
steps.python.outputs.python-version }}-${{
hashFiles('setup.py') }}-${{
hashFiles('requirements_test.txt') }}
hashFiles('requirements_test.txt') }}-${{
hashFiles('.pre-commit-config.yaml') }}
- name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/psf/black
rev: 20.8b1
rev: 22.3.0
hooks:
- id: black
args:
Expand Down
2 changes: 1 addition & 1 deletion bellows/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
MAJOR_VERSION = 0
MINOR_VERSION = 29
MINOR_VERSION = 30
PATCH_VERSION = "0"
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__ = f"{__short_version__}.{PATCH_VERSION}"
2 changes: 1 addition & 1 deletion bellows/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# flake8: noqa
from . import application, backup, dump, ncp, network
from . import application, backup, dump, ncp, network, stream, tone
2 changes: 1 addition & 1 deletion bellows/cli/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ async def bind(ctx, endpoint, cluster):
return

try:
v = await dev.zdo.bind(endpoint, cluster)
v = await dev.zdo.bind(clust)
click.echo(v)
except zigpy.exceptions.ZigbeeException as e:
click.echo(e)
Expand Down
59 changes: 59 additions & 0 deletions bellows/cli/stream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import asyncio
import logging
import time

import click

from . import util
from .main import main

LOGGER = logging.getLogger(__name__)


@main.command()
@click.option(
"-c", "--channel", type=click.IntRange(11, 26), metavar="CHANNEL", required=True
)
@click.option(
"-p", "--power", type=click.IntRange(-100, 20), metavar="POWER", required=True
)
@click.pass_context
def stream(ctx, channel, power):
"""Transmit random stream of characters on CHANNEL with POWER (in dBm)."""
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(_stream(ctx, channel, power))
except KeyboardInterrupt:
start_time = ctx.obj.get("start_time", None)
if start_time:
duration = time.time() - start_time
click.echo(
"\nStreamed on channel %d for %0.2fs" % (channel, duration), err=True
)
finally:
if "ezsp" in ctx.obj:
s = ctx.obj["ezsp"]
loop.run_until_complete(s.mfglibStopStream())
loop.run_until_complete(s.mfglibEnd())
s.close()


async def _stream(ctx, channel, power):
s = await util.setup(ctx.obj["device"], ctx.obj["baudrate"])
ctx.obj["ezsp"] = s

v = await s.mfglibStart(False)
util.check(v[0], "Unable to start mfglib")

v = await s.mfglibSetChannel(channel)
util.check(v[0], "Unable to set channel")

v = await s.mfglibSetPower(0, power)
util.check(v[0], "Unable to set power")

v = await s.mfglibStartStream()
util.check(v[0], "Unable to start stream")
click.echo("Started transmitting random stream of characters", err=True)
ctx.obj["start_time"] = time.time()
while True:
await asyncio.sleep(3600)
59 changes: 59 additions & 0 deletions bellows/cli/tone.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import asyncio
import logging
import time

import click

from . import util
from .main import main

LOGGER = logging.getLogger(__name__)


@main.command()
@click.option(
"-c", "--channel", type=click.IntRange(11, 26), metavar="CHANNEL", required=True
)
@click.option(
"-p", "--power", type=click.IntRange(-100, 20), metavar="POWER", required=True
)
@click.pass_context
def tone(ctx, channel, power):
"""Transmit continuous unmodulated tone on CHANNEL with POWER (in dBm)."""
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(_tone(ctx, channel, power))
except KeyboardInterrupt:
start_time = ctx.obj.get("start_time", None)
if start_time:
duration = time.time() - start_time
click.echo(
"\nStreamed on channel %d for %0.2fs" % (channel, duration), err=True
)
finally:
if "ezsp" in ctx.obj:
s = ctx.obj["ezsp"]
loop.run_until_complete(s.mfglibStopTone())
loop.run_until_complete(s.mfglibEnd())
s.close()


async def _tone(ctx, channel, power):
s = await util.setup(ctx.obj["device"], ctx.obj["baudrate"])
ctx.obj["ezsp"] = s

v = await s.mfglibStart(False)
util.check(v[0], "Unable to start mfglib")

v = await s.mfglibSetChannel(channel)
util.check(v[0], "Unable to set channel")

v = await s.mfglibSetPower(0, power)
util.check(v[0], "Unable to set power")

v = await s.mfglibStartTone()
util.check(v[0], "Unable to start tone")
click.echo("Started transmitting unmodulated tone", err=True)
ctx.obj["start_time"] = time.time()
while True:
await asyncio.sleep(3600)
9 changes: 9 additions & 0 deletions bellows/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
from zigpy.config import ( # noqa: F401 pylint: disable=unused-import
CONF_DEVICE,
CONF_DEVICE_PATH,
CONF_NWK,
CONF_NWK_CHANNEL,
CONF_NWK_CHANNELS,
CONF_NWK_EXTENDED_PAN_ID,
CONF_NWK_KEY,
CONF_NWK_PAN_ID,
CONF_NWK_TC_ADDRESS,
CONF_NWK_TC_LINK_KEY,
CONF_NWK_UPDATE_ID,
CONFIG_SCHEMA,
SCHEMA_DEVICE,
cv_boolean,
Expand Down
15 changes: 10 additions & 5 deletions bellows/ezsp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from . import v4, v5, v6, v7, v8

EZSP_LATEST = v8.EZSP_VERSION
PROBE_TIMEOUT = 2
PROBE_TIMEOUT = 3
NETWORK_OPS_TIMEOUT = 10
LOGGER = logging.getLogger(__name__)
MTOR_MIN_INTERVAL = 10
Expand Down Expand Up @@ -77,7 +77,7 @@ async def _probe(self) -> None:

@classmethod
async def initialize(cls, zigpy_config: Dict) -> "EZSP":
"""Return initialized EZSP instance. """
"""Return initialized EZSP instance."""
ezsp = cls(zigpy_config[CONF_DEVICE])
await ezsp.connect()
await ezsp.reset()
Expand Down Expand Up @@ -219,9 +219,14 @@ def connection_lost(self, exc):

def enter_failed_state(self, error):
"""UART received error frame."""
LOGGER.error("NCP entered failed state. Requesting APP controller restart")
self.close()
self.handle_callback("_reset_controller_application", (error,))
if self._callbacks:
LOGGER.error("NCP entered failed state. Requesting APP controller restart")
self.close()
self.handle_callback("_reset_controller_application", (error,))
else:
LOGGER.info(
"NCP entered failed state. No application handler registered, ignoring..."
)

def __getattr__(self, name: str) -> Callable:
if name not in self._protocol.COMMANDS:
Expand Down
2 changes: 1 addition & 1 deletion bellows/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


class EventLoopThread:
""" Run a parallel event loop in a separate thread """
"""Run a parallel event loop in a separate thread."""

def __init__(self):
self.loop = None
Expand Down
2 changes: 1 addition & 1 deletion bellows/types/named.py
Original file line number Diff line number Diff line change
Expand Up @@ -1141,7 +1141,7 @@ class EzspSourceRouteOverheadInformation(basic.enum8):


class EmberKeyData(basic.fixed_list(16, basic.uint8_t)):
"""A 128-bit key. """
"""A 128-bit key."""


class EmberCertificateData(basic.fixed_list(48, basic.uint8_t)):
Expand Down
11 changes: 7 additions & 4 deletions bellows/zigbee/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,13 @@ async def startup(self, auto_form=False):
output_clusters=[zigpy.zcl.clusters.security.IasZone.cluster_id]
)

brd_manuf, brd_name, version = await self._ezsp.get_board_info()
LOGGER.info("EZSP Radio manufacturer: %s", brd_manuf)
LOGGER.info("EZSP Radio board name: %s", brd_name)
LOGGER.info("EmberZNet version: %s", version)
try:
brd_manuf, brd_name, version = await self._ezsp.get_board_info()
LOGGER.info("EZSP Radio manufacturer: %s", brd_manuf)
LOGGER.info("EZSP Radio board name: %s", brd_name)
LOGGER.info("EmberZNet version: %s", version)
except EzspError as exc:
LOGGER.info("EZSP Radio does not support getMfgToken command: %s", str(exc))

v = await ezsp.networkInit()
if v[0] != t.EmberStatus.SUCCESS:
Expand Down
16 changes: 13 additions & 3 deletions tests/test_ezsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,22 @@ def test_connection_lost(ezsp_f):

async def test_enter_failed_state(ezsp_f):
ezsp_f.stop_ezsp = MagicMock(spec_set=ezsp_f.stop_ezsp)
ezsp_f.handle_callback = MagicMock(spec_set=ezsp_f.handle_callback)
cb = MagicMock(spec_set=ezsp_f.handle_callback)
ezsp_f.add_callback(cb)
ezsp_f.enter_failed_state(sentinel.error)
await asyncio.sleep(0)
assert ezsp_f.stop_ezsp.call_count == 1
assert ezsp_f.handle_callback.call_count == 1
assert ezsp_f.handle_callback.call_args[0][1][0] == sentinel.error
assert cb.call_count == 1
assert cb.call_args[0][1][0] == sentinel.error


async def test_no_close_without_callback(ezsp_f):
ezsp_f.stop_ezsp = MagicMock(spec_set=ezsp_f.stop_ezsp)
ezsp_f.close = MagicMock(spec_set=ezsp_f.close)
ezsp_f.enter_failed_state(sentinel.error)
await asyncio.sleep(0)
assert ezsp_f.stop_ezsp.call_count == 0
assert ezsp_f.close.call_count == 0


@patch.object(ezsp.EZSP, "reset", new_callable=AsyncMock)
Expand Down

0 comments on commit 0a0e4da

Please sign in to comment.