Skip to content

Commit 4e0383d

Browse files
committed
Merge branch 'release/0.0.73'
2 parents 00bdde2 + 1cb6211 commit 4e0383d

23 files changed

+802
-232
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,32 @@ A clear and concise description of what you expected to happen.
2323
**Screenshots**
2424
If applicable, add screenshots to help explain your problem.
2525

26+
<details>
27+
<summary>Device signature - this can be acquired by clicking on the "Zigbee Device Signature" button in the device settings</summary>
28+
29+
```
30+
Paste the device signature here.
31+
```
32+
33+
</details>
34+
35+
<details>
36+
<summary>Diagnostic information - this can be acquired by clicking on the "Download Diagnostics" button in the device settings</summary>
37+
38+
```
39+
Paste the diagnostic information here.
40+
```
41+
42+
</details>
43+
44+
<details>
45+
<summary>Additional logs</summary>
46+
47+
```
48+
Paste any additional debug logs here.
49+
```
50+
51+
</details>
52+
2653
**Additional context**
27-
Add any other context about the problem here. Please include full debug logs as well as the device signature.
54+
Add any other context about the problem here.

.github/ISSUE_TEMPLATE/device-support-request.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,32 @@ A clear and concise description of what the problem is. Ex. I'm always frustrate
1313
**Describe the solution you'd like**
1414
A clear and concise description of what you want to happen.
1515

16-
**Device signature - this can be acquired by removing the device from ZHA and pairing it again from the add devices screen. Be sure to add the entire content of the log panel after pairing the device to a code block below this line.**
16+
<details>
17+
<summary>Device signature - this can be acquired by clicking on the "Zigbee Device Signature" button in the device settings</summary>
18+
19+
```
20+
Paste the device signature here.
21+
```
22+
23+
</details>
24+
25+
<details>
26+
<summary>Diagnostic information - this can be acquired by clicking on the "Download Diagnostics" button in the device settings</summary>
27+
28+
```
29+
Paste the diagnostic information here.
30+
```
31+
32+
</details>
33+
34+
<details>
35+
<summary>Additional logs</summary>
36+
37+
```
38+
Paste any additional debug logs here.
39+
```
40+
41+
</details>
1742

1843
**Additional context**
1944
Add any other context or screenshots about the feature request here.

.vscode/extensions.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"recommendations": ["ms-python.python", "ms-python.vscode-pylance"]
3+
}

.vscode/settings.default.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"python.formatting.provider": "black",
3+
// Added --no-cov to work around TypeError: message must be set
4+
// https://github.com/microsoft/vscode-python/issues/14067
5+
"python.testing.pytestArgs": ["--no-cov"],
6+
// https://code.visualstudio.com/docs/python/testing#_pytest-configuration-settings
7+
"python.testing.pytestEnabled": false
8+
}

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ ZHA device handlers and it's provided Quirks allow Zigpy, ZHA and Home Assistant
1717

1818
## What are these specifications
1919

20-
[Zigbee Specification](https://zigbeealliance.org/wp-content/uploads/2019/11/docs-05-3474-21-0csg-zigbee-specification.pdf)
20+
[Zigbee PRO 2017 (R22) Protocol Specification](https://zigbeealliance.org/wp-content/uploads/2019/11/docs-05-3474-21-0csg-zigbee-specification.pdf)
2121

22-
[Zigbee Cluster Library](https://zigbeealliance.org/wp-content/uploads/2019/12/07-5123-06-zigbee-cluster-library-specification.pdf)
22+
[Zigbee Cluster Library (R8)](https://zigbeealliance.org/wp-content/uploads/2021/10/07-5123-08-Zigbee-Cluster-Library.pdf)
2323

24-
[Zigbee Base Device Specification](https://zigbeealliance.org/wp-content/uploads/zip/zigbee-base-device-behavior-bdb-v1-0.zip)
24+
[Zigbee Base Device Behavior Specification (V1.0)](https://zigbeealliance.org/wp-content/uploads/zip/zigbee-base-device-behavior-bdb-v1-0.zip)
25+
26+
[Zigbee Lighting & Occupancy Device Specification (V1.0)](https://zigbeealliance.org/wp-content/uploads/2019/11/docs-15-0014-05-0plo-Lighting-OccupancyDevice-Specification-V1.0.pdf)
2527

2628
[Zigbee Primer](https://docs.smartthings.com/en/latest/device-type-developers-guide/zigbee-primer.html)
2729

@@ -422,7 +424,9 @@ If you look at another example for the same device:
422424

423425
You can see a pattern that illustrates how to match a more complex event. In this case the step command is used for the dim up and dim down buttons so we need to match more of the event data to uniquely match the event.
424426

425-
## Testing using unit tests
427+
## Setting up the development environment
428+
429+
Open a terminal at the root of the project and run the setup script: `script/setup` This script will install all necessary dependencies and it will install the precommit hook.
426430

427431
The tests use the [pytest](https://docs.pytest.org/en/latest/) framework.
428432

@@ -467,7 +471,7 @@ You can read more about fixtures [here](https://docs.pytest.org/en/latest/how-to
467471
You can find the common fixtures in files named `conftest.py`. Pytest will list them for you as follows:
468472

469473
```bash
470-
$ pytest --fxitures
474+
$ pytest --fixtures
471475
[...]
472476
--- fixtures defined from tests.conftest ---
473477
MockAppController

requirements_test_all.txt

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
# Home Assistant test
2-
# linters such as flake8 and pylint should be pinned, as new releases
3-
# make new things fail. Manually update these pins when pulling in a
4-
# new version
5-
asynctest==0.13.0
6-
codecov==2.1.10
7-
coveralls==2.2.0
8-
mock-open==1.4.0
9-
mypy==0.790
10-
pre-commit==2.9.2
11-
pylint==2.6.0
12-
pytest-aiohttp==0.3.0
13-
pytest-cov==2.10.1
14-
pytest-sugar==0.9.4
15-
pytest-timeout==1.4.2
16-
pytest==6.1.2
17-
requests_mock==1.8.0
1+
asynctest
2+
isort
3+
codecov
4+
colorlog
5+
codespell
6+
coveralls
7+
mypy==0.942
8+
pre-commit
9+
pylint
10+
pytest-cov
11+
pytest-sugar
12+
pytest-timeout
13+
pytest
14+
zigpy

script/setup

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env bash
2+
# Setup the repository.
3+
4+
# Stop on errors
5+
set -e
6+
7+
cd "$(dirname "$0")/.."
8+
9+
# Add default vscode settings if not existing
10+
SETTINGS_FILE=./.vscode/settings.json
11+
SETTINGS_TEMPLATE_FILE=./.vscode/settings.default.json
12+
if [ ! -f "$SETTINGS_FILE" ]; then
13+
echo "Copy $SETTINGS_TEMPLATE_FILE to $SETTINGS_FILE."
14+
cp "$SETTINGS_TEMPLATE_FILE" "$SETTINGS_FILE"
15+
fi
16+
17+
python3 -m venv venv
18+
source venv/bin/activate
19+
20+
pip install -r requirements_test_all.txt
21+
pre-commit install
22+
23+
python3 -m pip install -e .

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from setuptools import find_packages, setup
66

7-
VERSION = "0.0.72"
7+
VERSION = "0.0.73"
88

99

1010
setup(

tests/test_kof.py

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,22 @@
44
import zigpy.device
55
import zigpy.endpoint
66
import zigpy.quirks
7+
from zigpy.zcl import foundation
8+
import zigpy.zdo.types as zdo_t
79

810
import zhaquirks
911
import zhaquirks.kof.kof_mr101z
1012

13+
from tests.conftest import CoroutineMock
14+
1115
zhaquirks.setup()
1216

17+
Default_Response = foundation.GENERAL_COMMANDS[
18+
foundation.GeneralCommand.Default_Response
19+
].schema
20+
1321

14-
def test_kof_no_reply():
22+
async def test_kof_no_reply():
1523
"""Test KOF No reply."""
1624

1725
class TestCluster(
@@ -20,36 +28,40 @@ class TestCluster(
2028
"""Test Cluster Class."""
2129

2230
cluster_id = 0x1234
23-
void_input_commands = [0x0002]
31+
void_input_commands = {0x02}
2432
server_commands = {
25-
0x0001: ("noop", (), False),
26-
0x0002: ("noop_noreply", (), False),
33+
0x01: foundation.ZCLCommandDef("noop", {}, False),
34+
0x02: foundation.ZCLCommandDef("noop_noreply", {}, False),
2735
}
2836
client_commands = {}
2937

30-
end_point = mock.MagicMock()
31-
cluster = TestCluster(end_point)
38+
ep = CoroutineMock()
39+
ep.device.application.get_sequence = mock.MagicMock(return_value=4)
3240

33-
cluster.command(0x0001)
34-
end_point.request.assert_called_with(
35-
mock.ANY, mock.ANY, mock.ANY, expect_reply=True, command_id=mock.ANY
36-
)
37-
end_point.reset_mock()
41+
cluster = TestCluster(ep)
3842

39-
cluster.command(0x0001, expect_reply=False)
40-
end_point.request.assert_called_with(
41-
mock.ANY, mock.ANY, mock.ANY, expect_reply=False, command_id=mock.ANY
42-
)
43-
end_point.reset_mock()
43+
async def mock_req(*args, expect_reply=True, **kwargs):
44+
if not expect_reply:
45+
return None
46+
else:
47+
return mock.sentinel.real_response
4448

45-
cluster.command(0x0002)
46-
end_point.request.assert_called_with(
47-
mock.ANY, mock.ANY, mock.ANY, expect_reply=False, command_id=mock.ANY
48-
)
49-
end_point.reset_mock()
49+
ep.request.side_effect = mock_req
5050

51-
cluster.command(0x0002, expect_reply=True)
52-
end_point.request.assert_called_with(
53-
mock.ANY, mock.ANY, mock.ANY, expect_reply=True, command_id=mock.ANY
51+
rsp = await cluster.noop()
52+
assert rsp is mock.sentinel.real_response
53+
54+
rsp = await cluster.noop(expect_reply=True)
55+
assert rsp is mock.sentinel.real_response
56+
57+
rsp = await cluster.noop_noreply()
58+
assert rsp == Default_Response(
59+
command_id=TestCluster.commands_by_name["noop_noreply"].id,
60+
status=zdo_t.Status.SUCCESS,
5461
)
55-
end_point.reset_mock()
62+
63+
rsp = await cluster.noop_noreply(expect_reply=False)
64+
assert rsp is None
65+
66+
rsp = await cluster.noop_noreply(expect_reply=True)
67+
assert rsp is mock.sentinel.real_response

zhaquirks/elko/smart_super_thermostat.py

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -53,27 +53,29 @@ class Sensor(t.enum8):
5353
PROTECTION = 0x03
5454

5555
attributes = ElkoThermostatCluster.attributes.copy()
56-
attributes = {
57-
UNKNOWN_1: ("unknown_1", t.uint16_t, True),
58-
DISPLAY_TEXT: ("display_text", t.CharacterString, True),
59-
ACTIVE_SENSOR: ("active_sensor", Sensor, True),
60-
UNKNOWN_2: ("unknown_2", t.uint8_t, True),
61-
REGULATOR_MODE: ("regulator_mode", t.Bool, True),
62-
DEVICE_ON: ("device_on", t.Bool, True),
63-
UNKNOWN_3: ("unknown_3", t.LongOctetString, True),
64-
POWER_CONSUMPTION: ("power_consumtion", t.uint16_t, True),
65-
FLOOR_SENSOR_TEMPERATURE: ("floor_sensor_temperature", t.int16s, True),
66-
UNKNOWN_4: ("unknown_4", t.uint16_t, True),
67-
NIGHT_LOWERING: ("night_lowering", t.Bool, True),
68-
UNKNOWN_5: ("unknown_5", t.Bool, True),
69-
CHILD_LOCK: ("child_lock", t.Bool, True),
70-
PROTECTION_MAX_TEMP: ("protection_max_temp", t.uint8_t, True),
71-
HEATING_ACTIVE: ("heating_active", t.Bool, True),
72-
UNKNOWN_6: ("unknown_6", t.LongOctetString, True),
73-
UNKNOWN_7: ("unknown_7", t.int8s, True),
74-
UNKNOWN_8: ("unknown_8", t.uint8_t, True),
75-
UNKNOWN_9: ("unknown_9", t.uint8_t, True),
76-
}
56+
attributes.update(
57+
{
58+
UNKNOWN_1: ("unknown_1", t.uint16_t, True),
59+
DISPLAY_TEXT: ("display_text", t.CharacterString, True),
60+
ACTIVE_SENSOR: ("active_sensor", Sensor, True),
61+
UNKNOWN_2: ("unknown_2", t.uint8_t, True),
62+
REGULATOR_MODE: ("regulator_mode", t.Bool, True),
63+
DEVICE_ON: ("device_on", t.Bool, True),
64+
UNKNOWN_3: ("unknown_3", t.LongOctetString, True),
65+
POWER_CONSUMPTION: ("power_consumtion", t.uint16_t, True),
66+
FLOOR_SENSOR_TEMPERATURE: ("floor_sensor_temperature", t.int16s, True),
67+
UNKNOWN_4: ("unknown_4", t.uint16_t, True),
68+
NIGHT_LOWERING: ("night_lowering", t.Bool, True),
69+
UNKNOWN_5: ("unknown_5", t.Bool, True),
70+
CHILD_LOCK: ("child_lock", t.Bool, True),
71+
PROTECTION_MAX_TEMP: ("protection_max_temp", t.uint8_t, True),
72+
HEATING_ACTIVE: ("heating_active", t.Bool, True),
73+
UNKNOWN_6: ("unknown_6", t.LongOctetString, True),
74+
UNKNOWN_7: ("unknown_7", t.int8s, True),
75+
UNKNOWN_8: ("unknown_8", t.uint8_t, True),
76+
UNKNOWN_9: ("unknown_9", t.uint8_t, True),
77+
}
78+
)
7779

7880
def __init__(self, *args, **kwargs):
7981
"""Init Elko thermostat."""

0 commit comments

Comments
 (0)