Skip to content

Commit

Permalink
Merge branch 'release/0.0.60'
Browse files Browse the repository at this point in the history
  • Loading branch information
dmulcahey committed Aug 26, 2021
2 parents ccf3d1c + 4e44f6b commit 369c2aa
Show file tree
Hide file tree
Showing 45 changed files with 2,275 additions and 1,005 deletions.
72 changes: 72 additions & 0 deletions .github/workflows/stale.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Stale

# yamllint disable-line rule:truthy
on:
schedule:
- cron: "0 * * * *"
workflow_dispatch:

jobs:
stale:
runs-on: ubuntu-latest
steps:
# The 180 day stale policy
# Used for:
# - Issues & PRs
# - No PRs marked as no-stale
# - No issues marked as no-stale or help-wanted
- name: 180 days stale issues & PRs policy
uses: actions/stale@v4
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 180
days-before-close: 7
operations-per-run: 150
remove-stale-when-updated: true
stale-issue-label: "stale"
exempt-issue-labels: "no-stale,help-wanted"
stale-issue-message: >
There hasn't been any activity on this issue recently. Due to the
high number of incoming GitHub notifications, we have to clean some
of the old issues, as many of them have already been resolved with
the latest updates.
Please make sure to update to the latest version and check if that
solves the issue. Let us know if that works for you by adding a
comment 👍
This issue has now been marked as stale and will be closed if no
further activity occurs. Thank you for your contributions.
stale-pr-label: "stale"
exempt-pr-labels: "no-stale"
stale-pr-message: >
There hasn't been any activity on this pull request recently. This
pull request has been automatically marked as stale because of that
and will be closed if no further activity occurs within 7 days.
Thank you for your contributions.
# The 60 day stale policy for issues
# Used for:
# - Issues that are pending more information (incomplete issues)
# - No Issues marked as no-stale or help-wanted
# - No PRs (-1)
- name: Needs more information stale issues policy
uses: actions/stale@v4
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
only-labels: "needs-more-information"
days-before-stale: 60
days-before-close: 7
days-before-pr-close: -1
operations-per-run: 50
remove-stale-when-updated: true
stale-issue-label: "stale"
exempt-issue-labels: "no-stale,help-wanted"
stale-issue-message: >
There hasn't been any activity on this issue recently. Due to the
high number of incoming GitHub notifications, we have to clean some
of the old issues, as many of them have already been resolved with
the latest updates.
Please make sure to update to the latest version and check if that
solves the issue. Let us know if that works for you by adding a
comment 👍
This issue has now been marked as stale and will be closed if no
further activity occurs. Thank you for your contributions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from setuptools import find_packages, setup

VERSION = "0.0.57"
VERSION = "0.0.60"


def readme():
Expand Down
35 changes: 24 additions & 11 deletions tests/test_tuya.py
Original file line number Diff line number Diff line change
Expand Up @@ -1059,17 +1059,30 @@ async def async_success(*args, **kwargs):
@pytest.mark.parametrize(
"quirk, manufacturer",
(
(zhaquirks.tuya.ts0042.TuyaSmartRemote0042, "_TZ3000_owgcnkrh"),
(zhaquirks.tuya.ts0042.TuyaSmartRemote0042, "_TZ3400_keyjhapk"),
(zhaquirks.tuya.ts0042.TuyaSmartRemote0042, "_some_random_manuf"),
(zhaquirks.tuya.ts0042.BenexmartRemote0042, "_TZ3000_adkvzooy"),
(zhaquirks.tuya.ts0042.BenexmartRemote0042, "_TZ3400_keyjhapk"),
(zhaquirks.tuya.ts0042.BenexmartRemote0042, "another random manufacturer"),
(zhaquirks.tuya.ts0043.TuyaSmartRemote0043, "_TZ3000_bi6lpsew"),
(zhaquirks.tuya.ts0043.TuyaSmartRemote0043, "_TZ3000_a7ouggvs"),
(zhaquirks.tuya.ts0043.TuyaSmartRemote0043, "another random manufacturer"),
(zhaquirks.tuya.ts0043.BenexmartRemote0043, "_TZ3000_qzjcsmar"),
(zhaquirks.tuya.ts0043.BenexmartRemote0043, "another random manufacturer"),
(zhaquirks.tuya.ts0041.TuyaSmartRemote0041TI, "_TZ3000_awgcnkrh"),
(zhaquirks.tuya.ts0041.TuyaSmartRemote0041TI, "_TZ3400_deyjhapk"),
(zhaquirks.tuya.ts0041.TuyaSmartRemote0041TI, "_some_random_manuf"),
(zhaquirks.tuya.ts0041.TuyaSmartRemote0041TO, "_TZ3000_pwgcnkrh"),
(zhaquirks.tuya.ts0041.TuyaSmartRemote0041TO, "_TZ3400_leyjhapk"),
(zhaquirks.tuya.ts0041.TuyaSmartRemote0041TO, "_some_random_manuf"),
(zhaquirks.tuya.ts0042.TuyaSmartRemote0042TI, "_TZ3000_owgcnkrh"),
(zhaquirks.tuya.ts0042.TuyaSmartRemote0042TI, "_TZ3400_keyjhapk"),
(zhaquirks.tuya.ts0042.TuyaSmartRemote0042TI, "_some_random_manuf"),
(zhaquirks.tuya.ts0042.TuyaSmartRemote0042TO, "_TZ3000_adkvzooy"),
(zhaquirks.tuya.ts0042.TuyaSmartRemote0042TO, "_TZ3400_keyjhapk"),
(zhaquirks.tuya.ts0042.TuyaSmartRemote0042TO, "another random manufacturer"),
(zhaquirks.tuya.ts0043.TuyaSmartRemote0043TI, "_TZ3000_bi6lpsew"),
(zhaquirks.tuya.ts0043.TuyaSmartRemote0043TI, "_TZ3000_a7ouggvs"),
(zhaquirks.tuya.ts0043.TuyaSmartRemote0043TI, "another random manufacturer"),
(zhaquirks.tuya.ts0043.TuyaSmartRemote0043TO, "_TZ3000_qzjcsmar"),
(zhaquirks.tuya.ts0043.TuyaSmartRemote0043TO, "_TZ3000_qzjcsmhd"),
(zhaquirks.tuya.ts0043.TuyaSmartRemote0043TO, "another random manufacturer"),
(zhaquirks.tuya.ts0044.TuyaSmartRemote0044TI, "_TZ3000_hjgcnkgs"),
(zhaquirks.tuya.ts0044.TuyaSmartRemote0044TI, "_TZ3000_ojgcnkkl"),
(zhaquirks.tuya.ts0044.TuyaSmartRemote0044TI, "_some_random_manuf"),
(zhaquirks.tuya.ts0044.TuyaSmartRemote0044TO, "_TZ3400_cdyjhasw"),
(zhaquirks.tuya.ts0044.TuyaSmartRemote0044TO, "_TZ3400_pdyjhapl"),
(zhaquirks.tuya.ts0044.TuyaSmartRemote0044TO, "_some_random_manuf"),
),
)
async def test_tuya_wildcard_manufacturer(zigpy_device_from_quirk, quirk, manufacturer):
Expand Down
61 changes: 61 additions & 0 deletions tests/test_tuya_air.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Test Tuya Air quality sensor."""

from unittest import mock

import pytest
import zigpy.profiles.zha

import zhaquirks
from zhaquirks.tuya import TuyaNewManufCluster
from zhaquirks.tuya.air.ts0601_air_quality import TuyaCO2Sensor

zhaquirks.setup()


@pytest.fixture
def air_quality_device(zigpy_device_from_quirk):
"""Tuya Air Quality Sensor."""
dev = zigpy_device_from_quirk(TuyaCO2Sensor)
cluster = dev.endpoints[1].in_clusters[TuyaNewManufCluster.cluster_id]
with mock.patch.object(cluster, "send_default_rsp"):
yield dev


@pytest.mark.parametrize(
"data, ep_attr, expected_value",
(
(
b"\t2\x01\x00\x02\x02\x02\x00\x04\x00\x00\x01r",
"carbon_dioxide_concentration",
370 * 1e-6,
),
(
b"\t$\x01\x00\x00\x13\x02\x00\x04\x00\x00\x02\xd6",
"humidity",
7260,
),
(
b"\t\x03\x01\x00\x01\x15\x02\x00\x04\x00\x00\x00\x01",
"voc_level",
1 * 1e-6,
),
(
b"\t\x02\x01\x00\x01\x16\x02\x00\x04\x00\x00\x00\x02",
"formaldehyde_concentration",
2 * 1e-6,
),
(
b"\t\x02\x01\x00\x00\x12\x02\x00\x04\x00\x00\x01 ",
"temperature",
2880,
),
),
)
def test_co2_sensor(air_quality_device, data, ep_attr, expected_value):
"""Test Tuya Air Quality Sensor."""

air_quality_device.handle_message(
zigpy.profiles.zha.PROFILE_ID, TuyaNewManufCluster.cluster_id, 1, 1, data
)
cluster = getattr(air_quality_device.endpoints[1], ep_attr)
assert cluster.get("measured_value") == expected_value
189 changes: 189 additions & 0 deletions tests/test_tuya_clusters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
"""Test units for new Tuya cluster framework."""

from unittest import mock

import pytest
import zigpy.zcl.foundation as zcl_f

from zhaquirks.tuya import (
TUYA_ACTIVE_STATUS_RPT,
TUYA_GET_DATA,
TUYA_SET_DATA_RESPONSE,
TUYA_SET_TIME,
TuyaCommand,
TuyaData,
TuyaNewManufCluster,
)


@pytest.fixture(name="TuyaCluster")
def tuya_cluster(zigpy_device_mock):
"""Mock of the new Tuya manufacturer cluster."""
device = zigpy_device_mock()
endpoint = device.add_endpoint(1)
cluster = TuyaNewManufCluster(endpoint)
return cluster


def test_tuya_data_value():
"""Test tuya "Value" datatype."""

data = b"\x02\x00\x04\x00\x00\x02\xdb"
extra = b"extra data"

r, rest = TuyaData.deserialize(data + extra)
assert rest == extra

assert r.dp_type == 2
assert r.raw == b"\xdb\x02\x00\x00"
assert r.payload == 731


def test_tuya_data_bool():
"""Test tuya Bool datatype."""

data = b"\x01\x00\x01\x00"
extra = b"extra data"

r, rest = TuyaData.deserialize(data + extra)
assert rest == extra

assert r.dp_type == 1
assert r.raw == b"\x00"
assert not r.payload

data = b"\x01\x00\x01\x01"
extra = b"extra data"

r, rest = TuyaData.deserialize(data + extra)
assert rest == extra

assert r.dp_type == 1
assert r.raw == b"\x01"
assert r.payload


def test_tuya_data_enum():
"""Test tuya Enum datatype."""

data = b"\x04\x00\x01\x40"
extra = b"extra data"

r, rest = TuyaData.deserialize(data + extra)
assert rest == extra

assert r.dp_type == 4
assert r.raw == b"\x40"
assert r.payload == 0x40


def test_tuya_data_string():
"""Test tuya String datatype."""

data = b"\x03\x00\x04Tuya"
extra = b"extra data"

r, rest = TuyaData.deserialize(data + extra)
assert rest == extra

assert r.dp_type == 3
assert r.raw == b"Tuya"
assert r.payload == "Tuya"


def test_tuya_data_bitmap():
"""Test tuya Bitmap datatype."""

data = b"\x05\x00\x01\x40"
extra = b"extra data"

r, rest = TuyaData.deserialize(data + extra)
assert rest == extra

assert r.dp_type == 5
assert r.raw == b"\x40"
assert r.payload == 0x40

data = b"\x05\x00\x02\x40\x02"
r, _ = TuyaData.deserialize(data)
r.payload == 0x4002

data = b"\x05\x00\x04\x40\x02\x80\x01"
r, _ = TuyaData.deserialize(data)
r.payload == 0x40028001


def test_tuya_data_bitmap_invalid():
"""Test tuya Bitmap datatype."""

data = b"\x05\x00\x03\x4012"
extra = b"extra data"

r, rest = TuyaData.deserialize(data + extra)
assert rest == extra

with pytest.raises(ValueError):
r.payload


@pytest.mark.parametrize(
"cmd_id, handler_name, args",
(
(
TUYA_GET_DATA,
"handle_get_data",
(TuyaCommand(0, 2, 2, TuyaData(1, 0, b"\x01\x01")),),
),
(
TUYA_SET_DATA_RESPONSE,
"handle_set_data_response",
(TuyaCommand(0, 2, 2, TuyaData(1, 0, b"\x01\x01")),),
),
(
TUYA_ACTIVE_STATUS_RPT,
"handle_active_status_report",
(TuyaCommand(0, 2, 2, TuyaData(1, 0, b"\x01\x01")),),
),
(TUYA_SET_TIME, "handle_set_time_request", (0x1234,)),
),
)
@mock.patch("zhaquirks.tuya.TuyaNewManufCluster.send_default_rsp")
def test_tuya_cluster_request(
default_rsp_mock, cmd_id, handler_name, args, TuyaCluster
):
"""Test cluster specific request."""

hdr = zcl_f.ZCLHeader.general(1, cmd_id, is_reply=True)
hdr.frame_control.disable_default_response = False

with mock.patch.object(TuyaCluster, handler_name) as handler:
handler.return_value = mock.sentinel.status
TuyaCluster.handle_cluster_request(hdr, args)
assert handler.call_count == 1
assert default_rsp_mock.call_count == 1
assert default_rsp_mock.call_args[1]["status"] is mock.sentinel.status


@mock.patch("zhaquirks.tuya.TuyaNewManufCluster.send_default_rsp")
def test_tuya_cluster_request_unk_command(default_rsp_mock, TuyaCluster):
"""Test cluster specific request handler -- no handler."""

hdr = zcl_f.ZCLHeader.general(1, 0xFE, is_reply=True)
hdr.frame_control.disable_default_response = False

TuyaCluster.handle_cluster_request(hdr, (mock.sentinel.args,))
assert default_rsp_mock.call_count == 1
assert default_rsp_mock.call_args[1]["status"] == zcl_f.Status.UNSUP_CLUSTER_COMMAND


@mock.patch("zhaquirks.tuya.TuyaNewManufCluster.send_default_rsp")
def test_tuya_cluster_request_no_handler(default_rsp_mock, TuyaCluster):
"""Test cluster specific request handler -- no handler."""

hdr = zcl_f.ZCLHeader.general(1, 0xFE, is_reply=True)
hdr.frame_control.disable_default_response = False

TuyaCluster.client_commands[0xFE] = ("no_such_handler", (), True)
TuyaCluster.handle_cluster_request(hdr, (mock.sentinel.args,))
assert default_rsp_mock.call_count == 1
assert default_rsp_mock.call_args[1]["status"] == zcl_f.Status.UNSUP_CLUSTER_COMMAND
Loading

0 comments on commit 369c2aa

Please sign in to comment.