Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions homeassistant/components/google_mail/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Support for Google Mail."""
# pylint: disable=home-assistant-use-runtime-data # Uses legacy hass.data[DOMAIN] pattern

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_NAME, Platform
Expand All @@ -26,7 +25,7 @@

async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Google Mail integration."""
hass.data.setdefault(DOMAIN, {})[DATA_HASS_CONFIG] = config
hass.data[DATA_HASS_CONFIG] = config

async_setup_services(hass)

Expand All @@ -53,9 +52,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: GoogleMailConfigEntry) -
Platform.NOTIFY,
DOMAIN,
{DATA_AUTH: auth, CONF_NAME: entry.title},
# Uses legacy hass.data[DOMAIN] pattern
# pylint: disable-next=home-assistant-use-runtime-data
hass.data[DOMAIN][DATA_HASS_CONFIG],
hass.data[DATA_HASS_CONFIG],
)
)

Expand Down
8 changes: 7 additions & 1 deletion homeassistant/components/google_mail/const.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
"""Constants for Google Mail integration."""

from typing import Any

from homeassistant.util.hass_dict import HassKey

ATTR_BCC = "bcc"
ATTR_CC = "cc"
ATTR_ENABLED = "enabled"
Expand All @@ -16,7 +20,9 @@
ATTR_TITLE = "title"

DATA_AUTH = "auth"
DATA_HASS_CONFIG = "hass_config"
# Domain-level root HA config for legacy notify platform discovery.
# Not per config entry — entries store auth on entry.runtime_data.
DATA_HASS_CONFIG: HassKey[dict[str, Any]] = HassKey("google_mail_hass_config")
DEFAULT_ACCESS = [
"https://www.googleapis.com/auth/gmail.compose",
"https://www.googleapis.com/auth/gmail.settings.basic",
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/jellyfin/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "local_polling",
"loggers": ["jellyfin_apiclient_python"],
"requirements": ["jellyfin-apiclient-python==1.11.0"]
"requirements": ["jellyfin-apiclient-python==1.16.0"]
}
16 changes: 11 additions & 5 deletions homeassistant/components/midea/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from midealocal.device import MideaDevice

from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
from homeassistant.helpers.entity import Entity, EntityDescription

from .const import DOMAIN, LOGGER
Expand Down Expand Up @@ -43,15 +43,21 @@ async def async_will_remove_from_hass(self) -> None:
@override
def device_info(self) -> DeviceInfo:
"""Return device info."""
return DeviceInfo(
device_info = DeviceInfo(
manufacturer="Midea",
# Map the device type (numeric ID) to a human-readable model name.
model=MIDEA_DEVICE_NAMES.get(self._device.device_type, "Unknown"),
model=MIDEA_DEVICE_NAMES.get(self._device.device_type),
identifiers={(DOMAIN, str(self._device.device_id))},
name=self._device_name,
model_id=str(self._device.device_type),
hw_version=str(self._device.subtype),
model_id=self._device.device_type.name,
hw_version=str(self._device.model),
)
if mac := self._device.mac:
device_info["connections"] = {(CONNECTION_NETWORK_MAC, mac)}
if serial_number := self._device.serial_number:
device_info["serial_number"] = serial_number

return device_info

@property
@override
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/mikrotik/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ async def _set_state(self, action: str) -> None:
f"/interface/{action}",
{".id": self._interface[".id"]},
)
self._interface["disabled"] = action == "disable"
self.async_write_ha_state()
await self.coordinator.async_request_refresh()

@override
async def async_turn_on(self, **kwargs: Any) -> None:
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/tesla_fleet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
)
from homeassistant.helpers.device_registry import DeviceInfo

from .const import DOMAIN, LOGGER, MODELS
from .const import DOMAIN, LOGGER
from .coordinator import (
TeslaFleetEnergySiteHistoryCoordinator,
TeslaFleetEnergySiteInfoCoordinator,
Expand Down Expand Up @@ -183,7 +183,7 @@ async def _get_access_token() -> str:
identifiers={(DOMAIN, vin)},
manufacturer="Tesla",
name=product["display_name"],
model=MODELS.get(vin[3]),
model=api_vehicle.model,
serial_number=vin,
)

Expand Down
9 changes: 0 additions & 9 deletions homeassistant/components/tesla_fleet/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,6 @@
Scope.ENERGY_CMDS,
]

MODELS = {
"S": "Model S",
"3": "Model 3",
"X": "Model X",
"Y": "Model Y",
"C": "Cybertruck",
"T": "Tesla Semi",
}

ENERGY_HISTORY_FIELDS = [
"solar_energy_exported",
"generator_energy_exported",
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion tests/components/midea/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
BASE_DATA,
TEST_DEVICE_ID,
TEST_KEY,
TEST_MAC_ADDRESS,
TEST_MODEL,
TEST_NAME,
TEST_SERIAL_NUMBER,
TEST_SUBTYPE,
TEST_TOKEN,
)
Expand All @@ -30,7 +32,7 @@ class DummyDevice:

def __init__(
self,
device_type: int,
device_type: DeviceType,
*,
attributes: dict | None = None,
) -> None:
Expand All @@ -57,6 +59,8 @@ def __init__(
"Fast-heating",
"Standby",
]
self.mac = TEST_MAC_ADDRESS
self.serial_number = TEST_SERIAL_NUMBER

def register_update(self, callback: Callable) -> None:
"""Record update callback registration."""
Expand Down
2 changes: 2 additions & 0 deletions tests/components/midea/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
TEST_DEVICE_ID = 12345678
TEST_IP_ADDRESS = "1.1.1.1"
TEST_KEY = "bb" * 16
TEST_MAC_ADDRESS = "00:11:22:33:44:55"
TEST_MODEL = "MSAGBU-09HRFN8"
TEST_NAME = "Bedroom AC"
TEST_PORT = 6444
TEST_PROTOCOL = ProtocolVersion.V3
TEST_SUBTYPE = 0
TEST_SERIAL_NUMBER = "1234567890"
TEST_TOKEN = "aa" * 16
TEST_TYPE = next(iter(MIDEA_DEVICE_NAMES))

Expand Down
51 changes: 50 additions & 1 deletion tests/components/midea/test_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
from midealocal.devices.ac import DeviceAttributes as ACAttributes
import pytest

from homeassistant.components.midea.const import DOMAIN
from homeassistant.core import CoreState, HomeAssistant
from homeassistant.helpers import device_registry as dr

from . import setup_integration
from .conftest import DummyDevice, default_ac_device, entity_entries
from .const import TEST_DEVICE_ID
from .const import TEST_DEVICE_ID, TEST_MAC_ADDRESS, TEST_MODEL, TEST_SERIAL_NUMBER

from tests.common import MockConfigEntry

Expand Down Expand Up @@ -79,6 +81,53 @@ async def test_entity_updates_from_device_callback(
assert (state.state == "unavailable") is expected_unavailable


@pytest.mark.parametrize(
("mac", "serial_number", "expected_connections", "expected_serial_number"),
[
pytest.param(
TEST_MAC_ADDRESS,
TEST_SERIAL_NUMBER,
{(dr.CONNECTION_NETWORK_MAC, TEST_MAC_ADDRESS)},
TEST_SERIAL_NUMBER,
id="populated",
),
pytest.param(
"",
"",
set(),
None,
id="absent",
),
],
)
async def test_device_info_optional_metadata(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
mock_config_entry: Callable[[DummyDevice], MockConfigEntry],
mac: str,
serial_number: str,
expected_connections: set[tuple[str, str]],
expected_serial_number: str | None,
) -> None:
"""Test device registry entry reflects optional mac and serial number."""
device = default_ac_device()
device.mac = mac
device.serial_number = serial_number
config_entry = mock_config_entry(device)
await setup_integration(hass, config_entry, device)

assert (
device_entry := device_registry.async_get_device(
identifiers={(DOMAIN, str(TEST_DEVICE_ID))}
)
) is not None

assert device_entry.model_id == device.device_type.name
assert device_entry.hw_version == TEST_MODEL
assert device_entry.connections == expected_connections
assert device_entry.serial_number == expected_serial_number


async def test_entity_callback_ignored_while_hass_stopping(
hass: HomeAssistant,
mock_config_entry: Callable[[DummyDevice], MockConfigEntry],
Expand Down
79 changes: 50 additions & 29 deletions tests/components/mikrotik/test_switch.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""Tests for the Mikrotik switch platform."""

from typing import Any
from unittest.mock import MagicMock, patch

import pytest
from syrupy.assertion import SnapshotAssertion

from homeassistant.components.switch import (
Expand Down Expand Up @@ -39,45 +41,64 @@ async def test_switch_no_matching_interfaces(hass: HomeAssistant) -> None:
assert hass.states.async_entity_ids(SWITCH_DOMAIN) == []


async def test_switch_turn_on(hass: HomeAssistant, mock_api: MagicMock) -> None:
"""Test turning on a Mikrotik switch enables the interface."""
with patch("homeassistant.components.mikrotik.PLATFORMS", [Platform.SWITCH]):
await setup_mikrotik_entry(hass, interface_data=[dict(WLAN1_INTERFACE)])

entity_id = "switch.wlan1_wlan"
assert (state := hass.states.get(entity_id))
assert state.state == STATE_OFF

await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)

mock_api.assert_called_with("/interface/enable", **{".id": "*2"})

assert (state := hass.states.get(entity_id))
assert state.state == STATE_ON

@pytest.mark.parametrize(
(
"interface",
"entity_id",
"initial_state",
"service",
"command",
"final_state",
),
[
pytest.param(
ETHER1_INTERFACE,
"switch.ether1_ethernet",
STATE_ON,
SERVICE_TURN_OFF,
"/interface/disable",
STATE_OFF,
id="turn_off",
),
pytest.param(
WLAN1_INTERFACE,
"switch.wlan1_wlan",
STATE_OFF,
SERVICE_TURN_ON,
"/interface/enable",
STATE_ON,
id="turn_on",
),
],
)
async def test_switch_turn_on_off(
hass: HomeAssistant,
mock_api: MagicMock,
interface: dict[str, Any],
entity_id: str,
initial_state: str,
service: str,
command: str,
final_state: str,
) -> None:
"""Test turning a Mikrotik switch on/off updates state via the coordinator."""

async def test_switch_turn_off(hass: HomeAssistant, mock_api: MagicMock) -> None:
"""Test turning off a Mikrotik switch disables the interface."""
with patch("homeassistant.components.mikrotik.PLATFORMS", [Platform.SWITCH]):
await setup_mikrotik_entry(hass, interface_data=[dict(ETHER1_INTERFACE)])
await setup_mikrotik_entry(hass, interface_data=[interface])

entity_id = "switch.ether1_ethernet"
assert (state := hass.states.get(entity_id))
assert state.state == STATE_ON
assert state.state == initial_state

mock_api.return_value = [{**interface, "disabled": final_state == STATE_OFF}]

await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_OFF,
service,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)

mock_api.assert_called_with("/interface/disable", **{".id": "*1"})
mock_api.assert_any_call(command, **{".id": interface[".id"]})

assert (state := hass.states.get(entity_id))
assert state.state == STATE_OFF
assert state.state == final_state
22 changes: 21 additions & 1 deletion tests/components/tesla_fleet/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from copy import deepcopy
from datetime import timedelta
from unittest.mock import AsyncMock, Mock, patch
from unittest.mock import AsyncMock, Mock, PropertyMock, patch

from freezegun.api import FrozenDateTimeFactory
import pytest
Expand Down Expand Up @@ -233,6 +233,26 @@ async def test_devices(
assert device == snapshot(name=f"{device.identifiers}")


async def test_vehicle_device_model_from_library(
hass: HomeAssistant,
normal_config_entry: MockConfigEntry,
device_registry: dr.DeviceRegistry,
) -> None:
"""Test the vehicle device model is provided by the library."""
with patch(
"tesla_fleet_api.tesla.VehicleFleet.model",
new_callable=PropertyMock,
return_value="Cybercab",
):
await setup_platform(hass, normal_config_entry)

device = device_registry.async_get_device(
identifiers={(DOMAIN, "LRWXF7EK4KC700000")}
)
assert device is not None
assert device.model == "Cybercab"


# Vehicle Coordinator
async def test_vehicle_refresh_offline(
hass: HomeAssistant,
Expand Down
Loading