Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ These are XBee Zigbee based radios that have been tested with the [zigpy-xbee](h
- Digi XBee Series 2 (S2) modules. Note: These will need to be manually flashed with the Zigbee Coordinator API firmware via XCTU.
- Digi XBee Series 3 (xbee3-24) modules

# Releases of zigpy-xbee-homeassistant via PyPI
Tagged versions of zigpy-xbee-homeassistant are also released via PyPI
# Releases of zigpy-xbee via PyPI
Tagged versions of zigpy-xbee are also released via PyPI

- https://pypi.org/project/zigpy-xbee-homeassistant/
- https://pypi.org/project/zigpy-xbee-homeassistant/#history
- https://pypi.org/project/zigpy-xbee-homeassistant/#files
- https://pypi.org/project/zigpy-xbee/
- https://pypi.org/project/zigpy-xbee/#history
- https://pypi.org/project/zigpy-xbee/#files

# How to contribute

Expand Down
12 changes: 10 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
"""Setup module for zigpy-xbee"""

import os

from setuptools import find_packages, setup

import zigpy_xbee

this_directory = os.path.join(os.path.abspath(os.path.dirname(__file__)))
with open(os.path.join(this_directory, "README.md"), encoding="utf-8") as f:
long_description = f.read()

setup(
name="zigpy-xbee-homeassistant",
name="zigpy-xbee",
version=zigpy_xbee.__version__,
description="A library which communicates with XBee radios for zigpy",
long_description=long_description,
long_description_content_type="text/markdown",
url="http://github.com/zigpy/zigpy-xbee",
author="Russell Cloran",
author_email="rcloran@gmail.com",
license="GPL-3.0",
packages=find_packages(exclude=["*.tests"]),
install_requires=["pyserial-asyncio", "zigpy-homeassistant >= 0.17.0"],
install_requires=["pyserial-asyncio", "zigpy>= 0.20.a1"],
tests_require=["pytest"],
)
52 changes: 31 additions & 21 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,26 @@
import zigpy.exceptions

from zigpy_xbee import api as xbee_api, types as t, uart
import zigpy_xbee.config
from zigpy_xbee.zigbee.application import ControllerApplication

DEVICE_CONFIG = zigpy_xbee.config.SCHEMA_DEVICE(
{zigpy_xbee.config.CONF_DEVICE_PATH: "/dev/null"}
)


@pytest.fixture
def api():
api = xbee_api.XBee()
api = xbee_api.XBee(DEVICE_CONFIG)
api._uart = mock.MagicMock()
return api


@pytest.mark.asyncio
async def test_connect(monkeypatch):
api = xbee_api.XBee()
dev = mock.MagicMock()
monkeypatch.setattr(
uart, "connect", mock.MagicMock(side_effect=asyncio.coroutine(mock.MagicMock()))
)
await api.connect(dev, 115200)
api = xbee_api.XBee(DEVICE_CONFIG)
monkeypatch.setattr(uart, "connect", CoroutineMock())
await api.connect()


def test_close(api):
Expand Down Expand Up @@ -542,14 +544,13 @@ def test_handle_many_to_one_rri(api):

@pytest.mark.asyncio
async def test_reconnect_multiple_disconnects(monkeypatch, caplog):
api = xbee_api.XBee()
dev = mock.sentinel.uart
api = xbee_api.XBee(DEVICE_CONFIG)
connect_mock = CoroutineMock()
connect_mock.return_value = asyncio.Future()
connect_mock.return_value.set_result(True)
monkeypatch.setattr(uart, "connect", connect_mock)

await api.connect(dev, 115200)
await api.connect()

caplog.set_level(logging.DEBUG)
connected = asyncio.Future()
Expand All @@ -568,14 +569,13 @@ async def test_reconnect_multiple_disconnects(monkeypatch, caplog):

@pytest.mark.asyncio
async def test_reconnect_multiple_attempts(monkeypatch, caplog):
api = xbee_api.XBee()
dev = mock.sentinel.uart
api = xbee_api.XBee(DEVICE_CONFIG)
connect_mock = CoroutineMock()
connect_mock.return_value = asyncio.Future()
connect_mock.return_value.set_result(True)
monkeypatch.setattr(uart, "connect", connect_mock)

await api.connect(dev, 115200)
await api.connect()

caplog.set_level(logging.DEBUG)
connected = asyncio.Future()
Expand All @@ -597,11 +597,11 @@ async def test_reconnect_multiple_attempts(monkeypatch, caplog):
async def test_probe_success(mock_connect, mock_at_cmd):
"""Test device probing."""

res = await xbee_api.XBee.probe(mock.sentinel.uart, mock.sentinel.baud)
res = await xbee_api.XBee.probe(DEVICE_CONFIG)
assert res is True
assert mock_connect.call_count == 1
assert mock_connect.await_count == 1
assert mock_connect.call_args[0][0] is mock.sentinel.uart
assert mock_connect.call_args[0][0] == DEVICE_CONFIG
assert mock_at_cmd.call_count == 1
assert mock_connect.return_value.close.call_count == 1

Expand All @@ -613,11 +613,11 @@ async def test_probe_success(mock_connect, mock_at_cmd):
async def test_probe_success_api_mode(mock_connect, mock_at_cmd, mock_api_mode):
"""Test device probing."""

res = await xbee_api.XBee.probe(mock.sentinel.uart, mock.sentinel.baud)
res = await xbee_api.XBee.probe(DEVICE_CONFIG)
assert res is True
assert mock_connect.call_count == 1
assert mock_connect.await_count == 1
assert mock_connect.call_args[0][0] is mock.sentinel.uart
assert mock_connect.call_args[0][0] == DEVICE_CONFIG
assert mock_at_cmd.call_count == 1
assert mock_api_mode.call_count == 1
assert mock_connect.return_value.close.call_count == 1
Expand All @@ -638,11 +638,11 @@ async def test_probe_fail(mock_connect, mock_at_cmd, mock_api_mode, exception):
mock_api_mode.reset_mock()
mock_at_cmd.reset_mock()
mock_connect.reset_mock()
res = await xbee_api.XBee.probe(mock.sentinel.uart, mock.sentinel.baud)
res = await xbee_api.XBee.probe(DEVICE_CONFIG)
assert res is False
assert mock_connect.call_count == 1
assert mock_connect.await_count == 1
assert mock_connect.call_args[0][0] is mock.sentinel.uart
assert mock_connect.call_args[0][0] == DEVICE_CONFIG
assert mock_at_cmd.call_count == 1
assert mock_api_mode.call_count == 1
assert mock_connect.return_value.close.call_count == 1
Expand All @@ -658,11 +658,21 @@ async def test_probe_fail_api_mode(mock_connect, mock_at_cmd, mock_api_mode):
mock_api_mode.reset_mock()
mock_at_cmd.reset_mock()
mock_connect.reset_mock()
res = await xbee_api.XBee.probe(mock.sentinel.uart, mock.sentinel.baud)
res = await xbee_api.XBee.probe(DEVICE_CONFIG)
assert res is False
assert mock_connect.call_count == 1
assert mock_connect.await_count == 1
assert mock_connect.call_args[0][0] is mock.sentinel.uart
assert mock_connect.call_args[0][0] == DEVICE_CONFIG
assert mock_at_cmd.call_count == 1
assert mock_api_mode.call_count == 1
assert mock_connect.return_value.close.call_count == 1


@pytest.mark.asyncio
@mock.patch.object(xbee_api.XBee, "connect")
async def test_xbee_new(conn_mck):
"""Test new class method."""
api = await xbee_api.XBee.new(mock.sentinel.application, DEVICE_CONFIG)
assert isinstance(api, xbee_api.XBee)
assert conn_mck.call_count == 1
assert conn_mck.await_count == 1
47 changes: 24 additions & 23 deletions tests/test_application.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
import asyncio
from unittest import mock

from asynctest import CoroutineMock, mock
import pytest
from zigpy import types as t
from zigpy.zdo.types import ZDOCmd

from zigpy_xbee.api import ModemStatus, XBee
import zigpy_xbee.config as config
import zigpy_xbee.types as xbee_t
from zigpy_xbee.zigbee import application

APP_CONFIG = {
config.CONF_DEVICE: {
config.CONF_DEVICE_PATH: "/dev/null",
config.CONF_DEVICE_BAUDRATE: 115200,
},
config.CONF_DATABASE: None,
}


@pytest.fixture
def app(monkeypatch, database_file=None):
def app(monkeypatch):
monkeypatch.setattr(application, "TIMEOUT_TX_STATUS", 0.1)
monkeypatch.setattr(application, "TIMEOUT_REPLY", 0.1)
monkeypatch.setattr(application, "TIMEOUT_REPLY_EXTENDED", 0.1)
return application.ControllerApplication(XBee(), database_file=database_file)
app = application.ControllerApplication(APP_CONFIG)
api = XBee(APP_CONFIG[config.CONF_DEVICE])
monkeypatch.setattr(api, "_command", CoroutineMock())
app._api = api
return app


def test_modem_status(app):
Expand Down Expand Up @@ -188,11 +201,7 @@ async def test_broadcast(app):
b"\x02\x01\x00",
)

app._api._command = mock.MagicMock(
side_effect=asyncio.coroutine(
mock.MagicMock(return_value=xbee_t.TXStatus.SUCCESS)
)
)
app._api._command.return_value = xbee_t.TXStatus.SUCCESS

r = await app.broadcast(profile, cluster, src_ep, dst_ep, grpid, radius, tsn, data)
assert r[0] == xbee_t.TXStatus.SUCCESS
Expand All @@ -202,17 +211,11 @@ async def test_broadcast(app):
assert app._api._command.call_args[0][4] == dst_ep
assert app._api._command.call_args[0][9] == data

app._api._command = mock.MagicMock(
side_effect=asyncio.coroutine(
mock.MagicMock(return_value=xbee_t.TXStatus.ADDRESS_NOT_FOUND)
)
)
app._api._command.return_value = xbee_t.TXStatus.ADDRESS_NOT_FOUND
r = await app.broadcast(profile, cluster, src_ep, dst_ep, grpid, radius, tsn, data)
assert r[0] != xbee_t.TXStatus.SUCCESS

app._api._command = mock.MagicMock(
side_effect=asyncio.coroutine(mock.MagicMock(side_effect=asyncio.TimeoutError))
)
app._api._command.side_effect = asyncio.TimeoutError
r = await app.broadcast(profile, cluster, src_ep, dst_ep, grpid, radius, tsn, data)
assert r[0] != xbee_t.TXStatus.SUCCESS

Expand Down Expand Up @@ -304,19 +307,17 @@ async def _at_command_mock(cmd, *args):
"ZS": zs,
}.get(cmd, None)

app._api._at_command = mock.MagicMock(
spec=XBee._at_command, side_effect=_at_command_mock
)

async def init_api_mode_mock():
nonlocal api_mode
api_mode = api_config_succeeds
return api_config_succeeds

app._api.init_api_mode = mock.MagicMock(side_effect=init_api_mode_mock)
app.form_network = mock.MagicMock(side_effect=asyncio.coroutine(mock.MagicMock()))
app.form_network = CoroutineMock()

await app.startup(auto_form=auto_form)
with mock.patch.object(XBee, "new") as api:
api.return_value._at_command = CoroutineMock(side_effect=_at_command_mock)
api.return_value.init_api_mode = CoroutineMock(side_effect=init_api_mode_mock)
await app.startup(auto_form=auto_form)
return app


Expand Down
8 changes: 6 additions & 2 deletions tests/test_uart.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
import serial_asyncio

from zigpy_xbee import uart
import zigpy_xbee.config

DEVICE_CONFIG = zigpy_xbee.config.SCHEMA_DEVICE(
{zigpy_xbee.config.CONF_DEVICE_PATH: "/dev/null"}
)


@pytest.fixture
Expand All @@ -29,7 +34,6 @@ def test_baudrate_fail(gw):
@pytest.mark.asyncio
async def test_connect(monkeypatch):
api = mock.MagicMock()
portmock = mock.MagicMock()

async def mock_conn(loop, protocol_factory, **kwargs):
protocol = protocol_factory()
Expand All @@ -38,7 +42,7 @@ async def mock_conn(loop, protocol_factory, **kwargs):

monkeypatch.setattr(serial_asyncio, "create_serial_connection", mock_conn)

await uart.connect(portmock, 57600, api)
await uart.connect(DEVICE_CONFIG, api)


def test_command_mode_rsp(gw):
Expand Down
2 changes: 1 addition & 1 deletion zigpy_xbee/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
MAJOR_VERSION = 0
MINOR_VERSION = 11
MINOR_VERSION = 12
PATCH_VERSION = "0"
__short_version__ = "{}.{}".format(MAJOR_VERSION, MINOR_VERSION)
__version__ = "{}.{}".format(__short_version__, PATCH_VERSION)
Loading