Skip to content

Commit fdd8e17

Browse files
committed
Add devcontainer and ignore new .venv
1 parent 41533b1 commit fdd8e17

File tree

9 files changed

+128
-21
lines changed

9 files changed

+128
-21
lines changed

.devcontainer/devcontainer.json

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
2+
// README at: https://github.com/devcontainers/templates/tree/main/src/python
3+
{
4+
"name": "Python 3",
5+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6+
"image": "mcr.microsoft.com/devcontainers/python:3.12",
7+
"features": {
8+
"ghcr.io/devcontainers-contrib/features/pylint:2": {}
9+
},
10+
// Features to add to the dev container. More info: https://containers.dev/features.
11+
// "features": {},
12+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
13+
// "forwardPorts": [],
14+
// Use 'postCreateCommand' to run commands after the container is created.
15+
"postCreateCommand": "git config --global --add safe.directory /workspaces/plugwise_usb-beta && scripts/python-venv.sh && . .venv/bin/activate",
16+
// Configure tool-specific properties.
17+
// "customizations": {},
18+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
19+
// "remoteUser": "root"
20+
// Customizations
21+
"customizations": {
22+
"vscode": {
23+
"extensions": [
24+
"ms-python.python",
25+
"ms-python.pylint",
26+
"github.vscode-pull-request-github",
27+
"ryanluker.vscode-coverage-gutters",
28+
"ms-python.vscode-pylance",
29+
"donjayamanne.python-extension-pack"
30+
],
31+
"settings": {
32+
"terminal.integrated.defaultProfile.linux": "bash",
33+
"terminal.integrated.profiles.linux": {
34+
"bash": {
35+
"path": "bash",
36+
"icon": "terminal-bash"
37+
}
38+
},
39+
"files.eol": "\n",
40+
"editor.tabSize": 4,
41+
"python.pythonPath": "/usr/bin/python3",
42+
"python.analysis.autoSearchPaths": false,
43+
// This makes sure the home assistant types are loaded into the editor
44+
"python.analysis.extraPaths": [
45+
"/home/vscode/.local/lib/python3.12/site-packages/"
46+
],
47+
"python.linting.pylintEnabled": true,
48+
"python.linting.enabled": true,
49+
"python.formatting.provider": "ruff",
50+
"editor.formatOnPaste": false,
51+
"editor.formatOnSave": true,
52+
"editor.formatOnType": true,
53+
"files.trimTrailingWhitespace": true
54+
}
55+
}
56+
},
57+
"appPort": 8123
58+
}

.gitattributes

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Autodetect text files
2+
* text=auto eol=lf
3+
4+
# ...Unless the name matches the following
5+
# overriding patterns
6+
7+
# Definitively text files
8+
*.py text
9+
*.sh lf

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ __pycache__
22
.vscode
33
ha-core
44
venv
5+
.venv
56
*.sedbck
67
*.swp
78
*.rej
89
*.orig
910
manual_clone_ha
11+
.coverage

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ repos:
1919
args:
2020
- --fix
2121
- repo: https://github.com/pre-commit/pre-commit-hooks
22-
rev: v4.5.0
22+
rev: v5.0.0
2323
hooks:
2424
- id: check-executables-have-shebangs
2525
stages: [manual]

custom_components/plugwise_usb/config_flow.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ class PlugwiseUSBConfigFlow(ConfigFlow, domain=DOMAIN):
5757
"""Handle a config flow for Plugwise USB."""
5858

5959
VERSION = 1
60+
MINOR_VERSION = 0
61+
62+
# no async_step_zeroconf this USB is physical
6063

6164
async def async_step_user(
6265
self, user_input: dict[str, Any] | None = None
@@ -83,7 +86,8 @@ async def async_step_user(
8386
)
8487
errors, mac_stick = await validate_usb_connection(self.hass, device_path)
8588
if not errors:
86-
await self.async_set_unique_id(mac_stick)
89+
await self.async_set_unique_id(unique_id=mac_stick, raise_on_progress=False)
90+
self._abort_if_unique_id_configured()
8791
return self.async_create_entry(
8892
title="Stick", data={CONF_USB_PATH: device_path}
8993
)

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,6 @@ max-complexity = 25
216216

217217
[tool.pytest]
218218
addopts = [
219-
"--auto",
220219
"--cov=custom_components"
221220
]
222221

scripts/tests_and_coverage.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ fi
2828

2929
if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "linting" ] ; then
3030
# Black first to ensure nothings roughing up ruff
31-
echo "... ruff checking ..."
31+
echo "... ruff checking ..."
3232
ruff check -fix
3333

3434
# TODO: Skip ruff checks as there are too many for now (mainly missing docstrings)
3535
# echo "... ruff-ing ..."
3636
# ruff check --fix plugwise_usb/ tests
37-
echo "... ruff format ..."
37+
echo "... ruff format ..."
3838
ruff format
3939
fi

tests/conftest.py

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from pytest_homeassistant_custom_component.common import MockConfigEntry
1111

1212
from homeassistant.core import HomeAssistant
13+
from plugwise_usb.exceptions import StickError
1314

1415

1516
@pytest.fixture
@@ -63,18 +64,57 @@ async def init_integration(
6364

6465

6566
@pytest.fixture
66-
def mock_usb() -> Generator[MagicMock]:
67+
def mock_usb_stick() -> Generator[MagicMock]:
6768
"""Return a mocked usb_mock."""
6869

6970
with patch(
7071
"custom_components.plugwise_usb.config_flow.Stick",
72+
autospec=True
7173
) as mock_usb:
72-
mock_usb.return_value.connect = AsyncMock(return_value=None)
73-
mock_usb.return_value.initialize = AsyncMock(return_value=None)
74-
mock_usb.return_value.disconnect = AsyncMock(return_value=None)
75-
mock_usb.return_value.mac_stick = MagicMock(return_value="01:23:45:67:AB")
74+
usb = mock_usb.return_value
7675

77-
yield mock_usb
76+
usb.connect = AsyncMock(return_value=None)
77+
usb.initialize = AsyncMock(return_value=None)
78+
usb.disconnect = AsyncMock(return_value=None)
79+
usb.mac_stick = "01:23:45:67:AB"
80+
81+
yield usb
82+
83+
84+
@pytest.fixture
85+
def mock_usb_stick_error() -> Generator[MagicMock]:
86+
"""Return a mocked usb_mock."""
87+
88+
with patch(
89+
"custom_components.plugwise_usb.config_flow.Stick",
90+
autospec=True
91+
) as mock_usb:
92+
usb = mock_usb.return_value
93+
94+
usb.connect = AsyncMock(side_effect=(StickError))
95+
usb.initialize = AsyncMock(return_value=None)
96+
usb.disconnect = AsyncMock(return_value=None)
97+
usb.mac_stick = "01:23:45:67:AB"
98+
99+
yield usb
100+
101+
102+
@pytest.fixture
103+
def mock_usb_stick_init_error() -> Generator[MagicMock]:
104+
"""Return a mocked usb_mock."""
105+
106+
with patch(
107+
"custom_components.plugwise_usb.config_flow.Stick",
108+
autospec=True
109+
) as mock_usb:
110+
usb = mock_usb.return_value
111+
112+
usb.connect = AsyncMock(return_value=None)
113+
usb.initialize = AsyncMock(side_effect=(StickError))
114+
usb.disconnect = AsyncMock(return_value=None)
115+
usb.mac_stick = "01:23:45:67:AB"
116+
117+
yield usb
78118

79119

80120
async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:

tests/test_config_flow.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Test the Plugwise config flow."""
22

3-
from unittest.mock import AsyncMock, MagicMock, patch
3+
from unittest.mock import MagicMock, patch
44

55
from custom_components.plugwise_usb.config_flow import CONF_MANUAL_PATH
66
from custom_components.plugwise_usb.const import CONF_USB_PATH, DOMAIN
@@ -11,7 +11,6 @@
1111
from homeassistant.config_entries import SOURCE_USER
1212
from homeassistant.const import CONF_SOURCE
1313
from homeassistant.data_entry_flow import FlowResultType, InvalidData
14-
from plugwise_usb.exceptions import StickError
1514

1615
TEST_USBPORT = "/dev/ttyUSB1"
1716
TEST_USBPORT2 = "/dev/ttyUSB2"
@@ -29,7 +28,7 @@ def com_port():
2928

3029

3130
@patch("serial.tools.list_ports.comports", MagicMock(return_value=[com_port()]))
32-
async def test_user_flow_select(hass, mock_usb: MagicMock):
31+
async def test_user_flow_select(hass, mock_usb_stick: MagicMock):
3332
"""Test user flow when USB-stick is selected from list."""
3433
port = com_port()
3534
port_select = f"{port}, s/n: {port.serial_number} - {port.manufacturer}"
@@ -78,7 +77,7 @@ async def test_user_flow_manual_selected_show_form(hass):
7877

7978

8079
async def test_user_flow_manual(
81-
hass, mock_usb: MagicMock, init_integration: MockConfigEntry
80+
hass, mock_usb_stick: MagicMock, init_integration: MockConfigEntry
8281
):
8382
"""Test user flow when USB-stick is manually entered."""
8483

@@ -146,9 +145,7 @@ async def test_empty_connection(hass):
146145
assert result.get("errors") == {}
147146

148147

149-
@patch("plugwise_usb.Stick.connect", AsyncMock(side_effect=(StickError)))
150-
@patch("plugwise_usb.Stick.initialize", AsyncMock(return_value=None))
151-
async def test_failed_connect(hass):
148+
async def test_failed_connect(hass, mock_usb_stick_error: MagicMock):
152149
"""Test we handle failed connection."""
153150
result = await hass.config_entries.flow.async_init(
154151
DOMAIN,
@@ -168,9 +165,7 @@ async def test_failed_connect(hass):
168165
assert result.get("errors") == {"base": "cannot_connect"}
169166

170167

171-
@patch("plugwise_usb.Stick.connect", AsyncMock(return_value=None))
172-
@patch("plugwise_usb.Stick.initialize", AsyncMock(side_effect=(StickError)))
173-
async def test_failed_initialization(hass):
168+
async def test_failed_initialization(hass, mock_usb_stick_init_error: MagicMock):
174169
"""Test we handle failed initialization of Plugwise USB-stick."""
175170
result = await hass.config_entries.flow.async_init(
176171
DOMAIN,

0 commit comments

Comments
 (0)