Skip to content
Closed
2 changes: 1 addition & 1 deletion homeassistant/components/binary_sensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ class BinarySensorEntity(Entity):
@property
def is_on(self) -> bool | None:
"""Return true if the binary sensor is on."""
return None
return self._attr_is_on

@property
def state(self) -> StateType:
Expand Down
18 changes: 16 additions & 2 deletions homeassistant/components/coinbase/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def setup(hass, config):

if not hasattr(coinbase_data, "accounts"):
return False
for account in coinbase_data.accounts.data:
for account in coinbase_data.accounts:
if account_currencies is None or account.currency in account_currencies:
load_platform(hass, "sensor", DOMAIN, {"account": account}, config)
for currency in exchange_currencies:
Expand Down Expand Up @@ -90,7 +90,21 @@ def update(self):
"""Get the latest data from coinbase."""

try:
self.accounts = self.client.get_accounts()
response = self.client.get_accounts()
accounts = response["data"]

# Most of Coinbase's API seems paginated now (25 items per page, but first page has 24).
# This API gives a 'next_starting_after' property to send back as a 'starting_after' param.
# Their API documentation is not up to date when writing these lines (2021-05-20)
next_starting_after = response.pagination.next_starting_after

while next_starting_after:
response = self.client.get_accounts(starting_after=next_starting_after)
accounts = accounts + response["data"]
next_starting_after = response.pagination.next_starting_after

self.accounts = accounts

self.exchange_rates = self.client.get_exchange_rates()
except AuthenticationError as coinbase_error:
_LOGGER.error(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/coinbase/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def extra_state_attributes(self):
def update(self):
"""Get the latest state of the sensor."""
self._coinbase_data.update()
for account in self._coinbase_data.accounts["data"]:
for account in self._coinbase_data.accounts:
if self._name == f"Coinbase {account['name']}":
self._state = account["balance"]["amount"]
self._native_balance = account["native_balance"]["amount"]
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/light/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ class LightEntity(ToggleEntity):
_attr_rgb_color: tuple[int, int, int] | None = None
_attr_rgbw_color: tuple[int, int, int, int] | None = None
_attr_rgbww_color: tuple[int, int, int, int, int] | None = None
_attr_supported_color_modes: set | None = None
_attr_supported_color_modes: set[str] | None = None
_attr_supported_features: int = 0
_attr_xy_color: tuple[float, float] | None = None

Expand Down Expand Up @@ -821,7 +821,7 @@ def _light_internal_supported_color_modes(self) -> set:
return supported_color_modes

@property
def supported_color_modes(self) -> set | None:
def supported_color_modes(self) -> set[str] | None:
"""Flag supported color modes."""
return self._attr_supported_color_modes

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/mill/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"domain": "mill",
"name": "Mill",
"documentation": "https://www.home-assistant.io/integrations/mill",
"requirements": ["millheater==0.4.0"],
"requirements": ["millheater==0.4.1"],
"codeowners": ["@danielhiversen"],
"config_flow": true,
"iot_class": "cloud_polling"
Expand Down
12 changes: 7 additions & 5 deletions homeassistant/components/sentry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from __future__ import annotations

import re
from types import MappingProxyType
from typing import Any

import sentry_sdk
from sentry_sdk.integrations.aiohttp import AioHttpIntegration
Expand Down Expand Up @@ -120,19 +122,19 @@ def get_channel(version: str) -> str:

def process_before_send(
hass: HomeAssistant,
options,
options: MappingProxyType[str, Any],
channel: str,
huuid: str,
system_info: dict[str, bool | str],
custom_components: dict[str, Integration],
event,
hint,
event: dict[str, Any],
hint: dict[str, Any],
):
"""Process a Sentry event before sending it to Sentry."""
# Filter out handled events by default
if (
"tags" in event
and event.tags.get("handled", "no") == "yes"
and event["tags"].get("handled", "no") == "yes"
and not options.get(CONF_EVENT_HANDLED)
):
return None
Expand Down Expand Up @@ -204,7 +206,7 @@ def process_before_send(
"channel": channel,
"custom_components": "\n".join(sorted(custom_components)),
"integrations": "\n".join(sorted(integrations)),
**system_info,
**system_info, # type: ignore[arg-type]
},
}
)
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/tcp/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.template import Template
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.typing import ConfigType, StateType

from .const import (
CONF_BUFFER_SIZE,
Expand Down Expand Up @@ -112,7 +112,7 @@ def name(self) -> str:
return self._config[CONF_NAME]

@property
def state(self) -> str | None:
def state(self) -> StateType:
"""Return the state of the device."""
return self._state

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/vlc_telnet/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"domain": "vlc_telnet",
"name": "VLC media player Telnet",
"documentation": "https://www.home-assistant.io/integrations/vlc-telnet",
"documentation": "https://www.home-assistant.io/integrations/vlc_telnet",
"requirements": ["python-telnet-vlc==2.0.1"],
"codeowners": ["@rodripf", "@dmcc"],
"iot_class": "local_polling"
Expand Down
4 changes: 3 additions & 1 deletion homeassistant/components/volvooncall/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ class VolvoSensor(VolvoEntity, BinarySensorEntity):

@property
def is_on(self):
"""Return True if the binary sensor is on."""
"""Return True if the binary sensor is on, but invert for the 'Door lock'."""
if self.instrument.attr == "is_locked":
return not self.instrument.is_on
return self.instrument.is_on

@property
Expand Down
3 changes: 0 additions & 3 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1267,9 +1267,6 @@ ignore_errors = true
[mypy-homeassistant.components.sense.*]
ignore_errors = true

[mypy-homeassistant.components.sentry.*]
ignore_errors = true

[mypy-homeassistant.components.sesame.*]
ignore_errors = true

Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,7 @@ mficlient==0.3.0
miflora==0.7.0

# homeassistant.components.mill
millheater==0.4.0
millheater==0.4.1

# homeassistant.components.minio
minio==4.0.9
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ meteofrance-api==1.0.2
mficlient==0.3.0

# homeassistant.components.mill
millheater==0.4.0
millheater==0.4.1

# homeassistant.components.minio
minio==4.0.9
Expand Down
1 change: 0 additions & 1 deletion script/hassfest/mypy_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@
"homeassistant.components.script.*",
"homeassistant.components.search.*",
"homeassistant.components.sense.*",
"homeassistant.components.sentry.*",
"homeassistant.components.sesame.*",
"homeassistant.components.sharkiq.*",
"homeassistant.components.sma.*",
Expand Down
10 changes: 7 additions & 3 deletions tests/components/sentry/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
"""Configuration for Sonos tests."""
"""Configuration for Sentry tests."""
from __future__ import annotations

from typing import Any

import pytest

from homeassistant.components.sentry import DOMAIN
Expand All @@ -7,12 +11,12 @@


@pytest.fixture(name="config_entry")
def config_entry_fixture():
def config_entry_fixture() -> MockConfigEntry:
"""Create a mock config entry."""
return MockConfigEntry(domain=DOMAIN, title="Sentry")


@pytest.fixture(name="config")
def config_fixture():
def config_fixture() -> dict[str, Any]:
"""Create hass config fixture."""
return {DOMAIN: {"dsn": "http://public@sentry.local/1"}}
51 changes: 30 additions & 21 deletions tests/components/sentry/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,26 @@
DOMAIN,
)
from homeassistant.config_entries import SOURCE_USER
from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_FORM
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import (
RESULT_TYPE_ABORT,
RESULT_TYPE_CREATE_ENTRY,
RESULT_TYPE_FORM,
)
from homeassistant.setup import async_setup_component

from tests.common import MockConfigEntry


async def test_full_user_flow_implementation(hass):
async def test_full_user_flow_implementation(hass: HomeAssistant) -> None:
"""Test we get the form."""
await async_setup_component(hass, "persistent_notification", {})
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] == RESULT_TYPE_FORM
assert result["errors"] == {}
assert result.get("type") == RESULT_TYPE_FORM
assert result.get("errors") == {}
assert "flow_id" in result

with patch("homeassistant.components.sentry.config_flow.Dsn"), patch(
"homeassistant.components.sentry.async_setup_entry",
Expand All @@ -40,32 +46,33 @@ async def test_full_user_flow_implementation(hass):
{"dsn": "http://public@sentry.local/1"},
)

assert result2["type"] == "create_entry"
assert result2["title"] == "Sentry"
assert result2["data"] == {
assert result2.get("type") == "create_entry"
assert result2.get("title") == "Sentry"
assert result2.get("data") == {
"dsn": "http://public@sentry.local/1",
}
await hass.async_block_till_done()

assert len(mock_setup_entry.mock_calls) == 1


async def test_integration_already_exists(hass):
async def test_integration_already_exists(hass: HomeAssistant) -> None:
"""Test we only allow a single config flow."""
MockConfigEntry(domain=DOMAIN).add_to_hass(hass)

result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] == RESULT_TYPE_ABORT
assert result["reason"] == "single_instance_allowed"
assert result.get("type") == RESULT_TYPE_ABORT
assert result.get("reason") == "single_instance_allowed"


async def test_user_flow_bad_dsn(hass):
async def test_user_flow_bad_dsn(hass: HomeAssistant) -> None:
"""Test we handle bad dsn error."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert "flow_id" in result

with patch(
"homeassistant.components.sentry.config_flow.Dsn",
Expand All @@ -76,15 +83,16 @@ async def test_user_flow_bad_dsn(hass):
{"dsn": "foo"},
)

assert result2["type"] == RESULT_TYPE_FORM
assert result2["errors"] == {"base": "bad_dsn"}
assert result2.get("type") == RESULT_TYPE_FORM
assert result2.get("errors") == {"base": "bad_dsn"}


async def test_user_flow_unkown_exception(hass):
async def test_user_flow_unkown_exception(hass: HomeAssistant) -> None:
"""Test we handle any unknown exception error."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert "flow_id" in result

with patch(
"homeassistant.components.sentry.config_flow.Dsn",
Expand All @@ -95,11 +103,11 @@ async def test_user_flow_unkown_exception(hass):
{"dsn": "foo"},
)

assert result2["type"] == RESULT_TYPE_FORM
assert result2["errors"] == {"base": "unknown"}
assert result2.get("type") == RESULT_TYPE_FORM
assert result2.get("errors") == {"base": "unknown"}


async def test_options_flow(hass):
async def test_options_flow(hass: HomeAssistant) -> None:
"""Test options config flow."""
entry = MockConfigEntry(
domain=DOMAIN,
Expand All @@ -113,8 +121,9 @@ async def test_options_flow(hass):

result = await hass.config_entries.options.async_init(entry.entry_id)

assert result["type"] == RESULT_TYPE_FORM
assert result["step_id"] == "init"
assert result.get("type") == RESULT_TYPE_FORM
assert result.get("step_id") == "init"
assert "flow_id" in result

result = await hass.config_entries.options.async_configure(
result["flow_id"],
Expand All @@ -130,8 +139,8 @@ async def test_options_flow(hass):
},
)

assert result["type"] == "create_entry"
assert result["data"] == {
assert result.get("type") == RESULT_TYPE_CREATE_ENTRY
assert result.get("data") == {
CONF_ENVIRONMENT: "Test",
CONF_EVENT_CUSTOM_COMPONENTS: True,
CONF_EVENT_HANDLED: True,
Expand Down
14 changes: 4 additions & 10 deletions tests/components/sentry/test_init.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Tests for Sentry integration."""
import logging
from unittest.mock import MagicMock, Mock, patch
from unittest.mock import Mock, patch

import pytest

Expand Down Expand Up @@ -112,12 +112,12 @@ async def test_setup_entry_with_tracing(hass: HomeAssistant) -> None:
("0.115.0dev0", "dev"),
],
)
async def test_get_channel(version, channel) -> None:
async def test_get_channel(version: str, channel: str) -> None:
"""Test if channel detection works from Home Assistant version number."""
assert get_channel(version) == channel


async def test_process_before_send(hass: HomeAssistant):
async def test_process_before_send(hass: HomeAssistant) -> None:
"""Test regular use of the Sentry process before sending function."""
hass.config.components.add("puppies")
hass.config.components.add("a_integration")
Expand Down Expand Up @@ -308,20 +308,14 @@ async def test_filter_log_events(hass: HomeAssistant, logger, options, event):
)
async def test_filter_handled_events(hass: HomeAssistant, handled, options, event):
"""Tests filtering of handled events based on configuration options."""

event_mock = MagicMock()
event_mock.__iter__ = ["tags"]
event_mock.__contains__ = lambda _, val: val == "tags"
event_mock.tags = {"handled": handled}

result = process_before_send(
hass,
options=options,
channel="test",
huuid="12345",
system_info={"installation_type": "pytest"},
custom_components=[],
event=event_mock,
event={"tags": {"handled": handled}},
hint={},
)

Expand Down