Skip to content

Commit

Permalink
0.20.0 Release
Browse files Browse the repository at this point in the history
  • Loading branch information
Adminiuga committed Apr 15, 2020
2 parents ed52994 + 4c7d842 commit 3c1bdba
Show file tree
Hide file tree
Showing 19 changed files with 565 additions and 203 deletions.
3 changes: 3 additions & 0 deletions .coveragerc
@@ -1,2 +1,5 @@
[run]
source = zigpy

omit =
zigpy/typing.py
35 changes: 31 additions & 4 deletions README.md
Expand Up @@ -54,13 +54,40 @@ Packages of tagged versions are also released via PyPI
- https://pypi.org/project/zigpy-zigate/
- https://pypi.org/project/zigpy-cc/

## How to contribute
## How to contribute

You can contribute to this project either as a end-user, a tester (advanced user contributing constructive issue/bug-reports) or as a developer contibuting code.

### How to contribute as an end-user

If you think that you are having problems due to a bug then please see the section below on reporting issues as a tester, but be aware that reporting issues put higher responsibility on your active involment on your part as a tester.

Some developers might be also interested in receiving donations in the form of money or hardware such as Zigbee modules and devices, and even if such donations are most often donated with no strings attached it could in many cases help the developers motivation and indirect improve the development of this project.

Sometimes it might just be simpler to just donate money earmarked to specifically let an willing developer buy the exact same type Zigbee device that you are having issues with to be able to replicate the issue themselves in order to troubleshoot and hopefully also solve the problem.

Consider submitting a post on GitHub projects issues tracker about willingness to making a donation (please see section bellow on posing issues).

### How to report issues or bugs as a tester

Issues or bugs are normally first be submitted upstream to the software/project that it utilizing zigpy and its radio libraries, (like for example Home Assistant), however if and when the issue is determened to be in the zigpy or underlying radio library then you should continue by submitting a detailed issue/bug report via the GitHub projects issues tracker.

Always be sure to first check if there is not already an existing issue posted with the same description before posting a new issue.

- https://help.github.com/en/github/managing-your-work-on-github/creating-an-issue
- https://guides.github.com/features/issues/

### How to contribute as a developer

If you are looking to make a contribution as a developer to this project we suggest that you follow the steps in these guides:

If you are looking to make a contribution to this project we suggest that you follow the steps in these guides:
- https://github.com/firstcontributions/first-contributions/blob/master/README.md
- https://github.com/firstcontributions/first-contributions/blob/master/github-desktop-tutorial.md
- https://github.com/firstcontributions/first-contributions/blob/master/github-desktop-tutorial.md

Code changes or additions can then be submitted to this project on GitHub via pull requests:

Some developers might also be interested in receiving donations in the form of hardware such as Zigbee modules or devices, and even if such donations are most often donated with no strings attached it could in many cases help the developers motivation and indirect improve the development of this project.
- https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests
- https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request

## Developer references

Expand Down
8 changes: 7 additions & 1 deletion setup.py
@@ -1,10 +1,16 @@
"""Setup module for zigpy"""

from os import path

from setuptools import find_packages, setup
import zigpy

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

setup(
name="zigpy-homeassistant",
name="zigpy",
version=zigpy.__version__,
description="Library implementing a ZigBee stack",
url="http://github.com/zigpy/zigpy",
Expand Down
98 changes: 68 additions & 30 deletions tests/test_appdb.py
@@ -1,9 +1,10 @@
import os
from unittest import mock

from asynctest import CoroutineMock, mock
import pytest
from zigpy import profiles
from zigpy.application import ControllerApplication
import zigpy.application
from zigpy.config import CONF_DATABASE, ZIGPY_SCHEMA
from zigpy.device import Device, Status
import zigpy.ota
from zigpy.quirks import CustomDevice
Expand All @@ -13,9 +14,33 @@
from zigpy.zdo import types as zdo_t


def make_app(database_file):
with mock.patch("zigpy.ota.OTA", mock.MagicMock(spec_set=zigpy.ota.OTA)):
app = ControllerApplication(database_file)
async def make_app(database_file):
class App(zigpy.application.ControllerApplication):
async def shutdown(self):
pass

async def startup(self, auto_form=False):
pass

async def request(
self,
device,
profile,
cluster,
src_ep,
dst_ep,
sequence,
data,
expect_reply=True,
use_ieee=False,
):
pass

async def permit_ncp(self, time_s=60):
pass

with mock.patch("zigpy.ota.OTA.initialize", CoroutineMock()):
app = await App.new(ZIGPY_SCHEMA({CONF_DATABASE: database_file}))
return app


Expand Down Expand Up @@ -45,12 +70,25 @@ def fake_get_device(device):
return device


async def test_no_database(tmpdir):
with mock.patch("zigpy.appdb.PersistingListener") as db_mock:
db_mock.return_value.load.side_effect = CoroutineMock()
await make_app(None)
assert db_mock.return_value.load.call_count == 0

db = os.path.join(str(tmpdir), "test.db")
with mock.patch("zigpy.appdb.PersistingListener") as db_mock:
db_mock.return_value.load.side_effect = CoroutineMock()
await make_app(db)
assert db_mock.return_value.load.call_count == 1


async def test_database(tmpdir, monkeypatch):
monkeypatch.setattr(
Device, "schedule_initialize", mock_dev_init(Status.ENDPOINTS_INIT)
)
db = os.path.join(str(tmpdir), "test.db")
app = make_app(db)
app = await make_app(db)
ieee = make_ieee()
relays_1 = [t.NWK(0x1234), t.NWK(0x2345)]
relays_2 = [t.NWK(0x3456), t.NWK(0x4567)]
Expand Down Expand Up @@ -94,7 +132,7 @@ async def test_database(tmpdir, monkeypatch):

# Everything should've been saved - check that it re-loads
with mock.patch("zigpy.quirks.get_device", fake_get_device):
app2 = make_app(db)
app2 = await make_app(db)
dev = app2.get_device(ieee)
assert dev.endpoints[1].device_type == profiles.zha.DeviceType.PUMP
assert dev.endpoints[2].device_type == 0xFFFD
Expand All @@ -113,7 +151,7 @@ async def test_database(tmpdir, monkeypatch):

app.handle_leave(99, ieee)

app2 = make_app(db)
app2 = await make_app(db)
assert ieee in app2.devices

async def mockleave(*args, **kwargs):
Expand All @@ -123,7 +161,7 @@ async def mockleave(*args, **kwargs):
await app2.remove(ieee)
assert ieee not in app2.devices

app3 = make_app(db)
app3 = await make_app(db)
assert ieee not in app3.devices
dev = app2.get_device(custom_ieee)
assert dev.relays is None
Expand All @@ -132,9 +170,9 @@ async def mockleave(*args, **kwargs):


@mock.patch("zigpy.device.Device.schedule_group_membership_scan", mock.MagicMock())
def _test_null_padded(tmpdir, test_manufacturer=None, test_model=None):
async def _test_null_padded(tmpdir, test_manufacturer=None, test_model=None):
db = os.path.join(str(tmpdir), "test.db")
app = make_app(db)
app = await make_app(db)
ieee = make_ieee()
with mock.patch(
"zigpy.device.Device.schedule_initialize",
Expand All @@ -156,7 +194,7 @@ def _test_null_padded(tmpdir, test_manufacturer=None, test_model=None):
clus.listener_event("zdo_command")

# Everything should've been saved - check that it re-loads
app2 = make_app(db)
app2 = await make_app(db)
dev = app2.get_device(ieee)
assert dev.endpoints[3].device_type == profiles.zha.DeviceType.PUMP
assert dev.endpoints[3].in_clusters[0]._attr_cache[4] == test_manufacturer
Expand All @@ -167,43 +205,43 @@ def _test_null_padded(tmpdir, test_manufacturer=None, test_model=None):
return dev


def test_appdb_load_null_padded_manuf(tmpdir):
async def test_appdb_load_null_padded_manuf(tmpdir):
manufacturer = b"Mock Manufacturer\x00\x04\\\x00\\\x00\x00\x00\x00\x00\x07"
model = b"Mock Model"
dev = _test_null_padded(tmpdir, manufacturer, model)
dev = await _test_null_padded(tmpdir, manufacturer, model)

assert dev.manufacturer == "Mock Manufacturer"
assert dev.model == "Mock Model"
assert dev.endpoints[3].manufacturer == "Mock Manufacturer"
assert dev.endpoints[3].model == "Mock Model"


def test_appdb_load_null_padded_model(tmpdir):
async def test_appdb_load_null_padded_model(tmpdir):
manufacturer = b"Mock Manufacturer"
model = b"Mock Model\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
dev = _test_null_padded(tmpdir, manufacturer, model)
dev = await _test_null_padded(tmpdir, manufacturer, model)

assert dev.manufacturer == "Mock Manufacturer"
assert dev.model == "Mock Model"
assert dev.endpoints[3].manufacturer == "Mock Manufacturer"
assert dev.endpoints[3].model == "Mock Model"


def test_appdb_load_null_padded_manuf_model(tmpdir):
async def test_appdb_load_null_padded_manuf_model(tmpdir):
manufacturer = b"Mock Manufacturer\x00\x04\\\x00\\\x00\x00\x00\x00\x00\x07"
model = b"Mock Model\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
dev = _test_null_padded(tmpdir, manufacturer, model)
dev = await _test_null_padded(tmpdir, manufacturer, model)

assert dev.manufacturer == "Mock Manufacturer"
assert dev.model == "Mock Model"
assert dev.endpoints[3].manufacturer == "Mock Manufacturer"
assert dev.endpoints[3].model == "Mock Model"


def test_appdb_str_model(tmpdir):
async def test_appdb_str_model(tmpdir):
manufacturer = "Mock Manufacturer"
model = "Mock Model"
dev = _test_null_padded(tmpdir, manufacturer, model)
dev = await _test_null_padded(tmpdir, manufacturer, model)

assert dev.manufacturer == "Mock Manufacturer"
assert dev.model == "Mock Model"
Expand All @@ -217,7 +255,7 @@ def test_appdb_str_model(tmpdir):
)
async def test_node_descriptor_updated(tmpdir, status, success):
db = os.path.join(str(tmpdir), "test_nd.db")
app = make_app(db)
app = await make_app(db)
nd_ieee = make_ieee(2)
with mock.patch.object(Device, "schedule_initialize", new=mock_dev_init(status)):
app.handle_join(299, nd_ieee, 0)
Expand All @@ -242,7 +280,7 @@ async def mock_get_node_descriptor():

assert dev.get_node_descriptor.call_count == 1

app2 = make_app(db)
app2 = await make_app(db)
if success:
dev = app2.get_device(nd_ieee)
assert dev.status == status
Expand All @@ -267,7 +305,7 @@ async def mock_request(*args, **kwargs):
monkeypatch.setattr(zigpy.zcl.Cluster, "request", mock_request)

db = os.path.join(str(tmpdir), "test.db")
app = make_app(db)
app = await make_app(db)
ieee = make_ieee()
app.handle_join(99, ieee, 0)

Expand Down Expand Up @@ -296,7 +334,7 @@ async def mock_request(*args, **kwargs):
assert group_id in ep.member_of

# Everything should've been saved - check that it re-loads
app2 = make_app(db)
app2 = await make_app(db)
dev2 = app2.get_device(ieee)
assert group_id in app2.groups
group = app2.groups[group_id]
Expand All @@ -310,7 +348,7 @@ async def mock_request(*args, **kwargs):

# check member removal
await dev_b.remove_from_group(group_id)
app3 = make_app(db)
app3 = await make_app(db)
dev3 = app3.get_device(ieee)
assert group_id in app3.groups
group = app3.groups[group_id]
Expand All @@ -324,26 +362,26 @@ async def mock_request(*args, **kwargs):

# check group removal
await dev3.remove_from_group(group_id)
app4 = make_app(db)
app4 = await make_app(db)
dev4 = app4.get_device(ieee)
assert group_id in app4.groups
assert not app4.groups[group_id]
assert group_id not in dev4.endpoints[1].member_of
app4.groups.pop(group_id)

app5 = make_app(db)
app5 = await make_app(db)
assert not app5.groups


@pytest.mark.parametrize(
"status, success",
((Status.ENDPOINTS_INIT, True), (Status.ZDO_INIT, False), (Status.NEW, False)),
)
def test_attribute_update(tmpdir, status, success):
async def test_attribute_update(tmpdir, status, success):
"""Test attribute update for initialized and uninitialized devices."""

db = os.path.join(str(tmpdir), "test.db")
app = make_app(db)
app = await make_app(db)
ieee = make_ieee()
with mock.patch(
"zigpy.device.Device.schedule_initialize", new=mock_dev_init(status)
Expand All @@ -364,7 +402,7 @@ def test_attribute_update(tmpdir, status, success):
app.device_initialized(dev)

# Everything should've been saved - check that it re-loads
app2 = make_app(db)
app2 = await make_app(db)
if success:
dev = app2.get_device(ieee)
assert dev.status == status
Expand Down

0 comments on commit 3c1bdba

Please sign in to comment.