Skip to content

Commit

Permalink
0.13.2 Release
Browse files Browse the repository at this point in the history
  • Loading branch information
Adminiuga committed Feb 8, 2020
2 parents 34af0e1 + 4ac2e6d commit e8c2080
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 31 deletions.
102 changes: 78 additions & 24 deletions tests/test_appdb.py
Expand Up @@ -25,41 +25,41 @@ def make_ieee(init=0):


class FakeCustomDevice(CustomDevice):
def __init__(self, application, ieee, nwk, replaces):
super().__init__(application, ieee, nwk, replaces)
pass


async def _initialize(self):
self.status = Status.ENDPOINTS_INIT
self.initializing = False
self._application.device_initialized(self)
def mock_dev_init(status: Status):
"""Device schedule_initialize mock factory."""

def _initialize(self):
self.status = status
self.initializing = False
self._application.device_initialized(self)
self.node_desc = zdo_t.NodeDescriptor(0, 1, 2, 3, 4, 5, 6, 7, 8)

return _initialize


def fake_get_device(device):
if device.endpoints.get(1) is not None and device[1].profile_id == 65535:
return FakeCustomDevice(
device.application,
make_ieee(1),
199,
Device(device.application, make_ieee(1), 199),
)
return FakeCustomDevice(device.application, make_ieee(1), 199, device)
return device


@pytest.mark.asyncio
async def test_database(tmpdir, monkeypatch):
monkeypatch.setattr(Device, "_initialize", _initialize)
monkeypatch.setattr(
Device, "schedule_initialize", mock_dev_init(Status.ENDPOINTS_INIT)
)
db = os.path.join(str(tmpdir), "test.db")
app = make_app(db)
# TODO: Leaks a task on dev.initialize, I think?
ieee = make_ieee()
relays_1 = [t.NWK(0x1234), t.NWK(0x2345)]
relays_2 = [t.NWK(0x3456), t.NWK(0x4567)]
app.handle_join(99, ieee, 0)
app.handle_join(99, ieee, 0)

dev = app.get_device(ieee)
dev.node_desc, _ = zdo_t.NodeDescriptor.deserialize(b"1234567890123")
ep = dev.add_endpoint(1)
ep.profile_id = 260
ep.device_type = profiles.zha.DeviceType.PUMP
Expand Down Expand Up @@ -136,9 +136,11 @@ async def mockleave(*args, **kwargs):
def _test_null_padded(tmpdir, test_manufacturer=None, test_model=None):
db = os.path.join(str(tmpdir), "test.db")
app = make_app(db)
# TODO: Leaks a task on dev.initialize, I think?
ieee = make_ieee()
with mock.patch("zigpy.device.Device.schedule_initialize"):
with mock.patch(
"zigpy.device.Device.schedule_initialize",
new=mock_dev_init(Status.ENDPOINTS_INIT),
):
app.handle_join(99, ieee, 0)
app.handle_join(99, ieee, 0)

Expand Down Expand Up @@ -210,13 +212,17 @@ def test_appdb_str_model(tmpdir):
assert dev.endpoints[3].model == "Mock Model"


@pytest.mark.parametrize(
"status, success",
((Status.ENDPOINTS_INIT, True), (Status.ZDO_INIT, False), (Status.NEW, False)),
)
@pytest.mark.asyncio
async def test_node_descriptor_updated(tmpdir, monkeypatch):
monkeypatch.setattr(Device, "_initialize", _initialize)
async def test_node_descriptor_updated(tmpdir, status, success):
db = os.path.join(str(tmpdir), "test_nd.db")
app = make_app(db)
nd_ieee = make_ieee(2)
app.handle_join(299, nd_ieee, 0)
with mock.patch.object(Device, "schedule_initialize", new=mock_dev_init(status)):
app.handle_join(299, nd_ieee, 0)

dev = app.get_device(nd_ieee)
ep = dev.add_endpoint(1)
Expand All @@ -239,16 +245,22 @@ async def mock_get_node_descriptor():
assert dev.get_node_descriptor.call_count == 1

app2 = make_app(db)
dev = app2.get_device(nd_ieee)
assert dev.node_desc.is_valid
assert dev.node_desc.serialize() == b"abcdefghijklm"
if success:
dev = app2.get_device(nd_ieee)
assert dev.status == status
assert dev.node_desc.is_valid
assert dev.node_desc.serialize() == b"abcdefghijklm"
else:
assert nd_ieee not in app2.devices

os.unlink(db)


@pytest.mark.asyncio
async def test_groups(tmpdir, monkeypatch):
monkeypatch.setattr(Device, "_initialize", _initialize)
monkeypatch.setattr(
Device, "schedule_initialize", mock_dev_init(Status.ENDPOINTS_INIT)
)

group_id, group_name = 0x1221, "app db Test Group 0x1221"

Expand Down Expand Up @@ -324,3 +336,45 @@ async def mock_request(*args, **kwargs):

app5 = 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):
"""Test attribute update for initialized and uninitialized devices."""

db = os.path.join(str(tmpdir), "test.db")
app = make_app(db)
ieee = make_ieee()
with mock.patch(
"zigpy.device.Device.schedule_initialize", new=mock_dev_init(status)
):
app.handle_join(99, ieee, 0)

test_manufacturer = "Test Manufacturer"
test_model = "Test Model"

dev = app.get_device(ieee)
ep = dev.add_endpoint(3)
ep.profile_id = 260
ep.device_type = profiles.zha.DeviceType.PUMP
clus = ep.add_input_cluster(0)
ep.add_output_cluster(1)
clus._update_attribute(4, test_manufacturer)
clus._update_attribute(5, test_model)
app.device_initialized(dev)

# Everything should've been saved - check that it re-loads
app2 = make_app(db)
if success:
dev = app2.get_device(ieee)
assert dev.status == status
assert dev.endpoints[3].device_type == profiles.zha.DeviceType.PUMP
assert dev.endpoints[3].in_clusters[0]._attr_cache[4] == test_manufacturer
assert dev.endpoints[3].in_clusters[0]._attr_cache[5] == test_model
else:
assert ieee not in app2.devices

os.unlink(db)
2 changes: 1 addition & 1 deletion zigpy/__init__.py
@@ -1,6 +1,6 @@
# coding: utf-8
MAJOR_VERSION = 0
MINOR_VERSION = 13
PATCH_VERSION = "1"
PATCH_VERSION = "2"
__short_version__ = "{}.{}".format(MAJOR_VERSION, MINOR_VERSION)
__version__ = "{}.{}".format(__short_version__, PATCH_VERSION)
30 changes: 24 additions & 6 deletions zigpy/appdb.py
Expand Up @@ -171,8 +171,8 @@ def _create_table_attributes(self):
"attributes",
(
"(ieee ieee, endpoint_id, cluster, attrid, value, "
"FOREIGN KEY(ieee, endpoint_id, cluster) "
"REFERENCES clusters(ieee, endpoint_Id, cluster) "
"FOREIGN KEY(ieee, endpoint_id) "
"REFERENCES endpoints(ieee, endpoint_id) "
"ON DELETE CASCADE)"
),
)
Expand Down Expand Up @@ -218,6 +218,14 @@ def _remove_device(self, device):
self._db.commit()

def _save_device(self, device):
if device.status != zigpy.device.Status.ENDPOINTS_INIT:
LOGGER.warning(
"Not saving uninitialized %s/%s device: %s",
device.ieee,
device.nwk,
device.status,
)
return
q = "INSERT OR REPLACE INTO devices (ieee, nwk, status) VALUES (?, ?, ?)"
self.execute(q, (device.ieee, device.nwk, device.status))
self._save_node_descriptor(device)
Expand All @@ -230,6 +238,7 @@ def _save_device(self, device):
# ZDO
continue
self._save_input_clusters(ep)
self._save_attribute_cache(ep)
self._save_output_clusters(ep)
self._db.commit()

Expand All @@ -249,10 +258,12 @@ def _save_endpoints(self, device):
)
endpoints.append(eprow)
self._cursor.executemany(q, endpoints)
self._db.commit()

def _save_node_descriptor(self, device):
if not device.node_desc.is_valid:
if (
device.status != zigpy.device.Status.ENDPOINTS_INIT
or not device.node_desc.is_valid
):
return
q = "INSERT OR REPLACE INTO node_descriptors VALUES (?, ?)"
self.execute(q, (device.ieee, device.node_desc.serialize()))
Expand All @@ -264,7 +275,15 @@ def _save_input_clusters(self, endpoint):
for cluster in endpoint.in_clusters.values()
]
self._cursor.executemany(q, clusters)
self._db.commit()

def _save_attribute_cache(self, ep):
q = "INSERT OR REPLACE INTO attributes VALUES (?, ?, ?, ?, ?)"
clusters = [
(ep.device.ieee, ep.endpoint_id, cluster.cluster_id, attrid, value)
for cluster in ep.in_clusters.values()
for attrid, value in cluster._attr_cache.items()
]
self._cursor.executemany(q, clusters)

def _save_output_clusters(self, endpoint):
q = "INSERT OR REPLACE INTO output_clusters VALUES (?, ?, ?)"
Expand All @@ -273,7 +292,6 @@ def _save_output_clusters(self, endpoint):
for cluster in endpoint.out_clusters.values()
]
self._cursor.executemany(q, clusters)
self._db.commit()

def _save_attribute(self, ieee, endpoint_id, cluster_id, attrid, value):
q = "INSERT OR REPLACE INTO attributes VALUES (?, ?, ?, ?, ?)"
Expand Down
3 changes: 3 additions & 0 deletions zigpy/zcl/__init__.py
Expand Up @@ -6,6 +6,7 @@
import zigpy.types as t
from zigpy import util
from zigpy.zcl import foundation
import zigpy.device


LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -439,6 +440,8 @@ def __init__(self, applistener, cluster):
self._cluster = cluster

def attribute_updated(self, attrid, value):
if self._cluster.endpoint.device.status != zigpy.device.Status.ENDPOINTS_INIT:
return
self._applistener.attribute_updated(self._cluster, attrid, value)

def cluster_command(self, *args, **kwargs):
Expand Down

0 comments on commit e8c2080

Please sign in to comment.