This commit is contained in:
Paulus Schoutsen 2023-06-08 14:57:37 -04:00 committed by GitHub
commit a5f86bff45
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 288 additions and 113 deletions

View File

@ -391,6 +391,7 @@ def async_enable_logging(
logging.getLogger("requests").setLevel(logging.WARNING) logging.getLogger("requests").setLevel(logging.WARNING)
logging.getLogger("urllib3").setLevel(logging.WARNING) logging.getLogger("urllib3").setLevel(logging.WARNING)
logging.getLogger("aiohttp.access").setLevel(logging.WARNING) logging.getLogger("aiohttp.access").setLevel(logging.WARNING)
logging.getLogger("httpx").setLevel(logging.WARNING)
sys.excepthook = lambda *args: logging.getLogger(None).exception( sys.excepthook = lambda *args: logging.getLogger(None).exception(
"Uncaught exception", exc_info=args # type: ignore[arg-type] "Uncaught exception", exc_info=args # type: ignore[arg-type]

View File

@ -12,6 +12,7 @@ from homeassistant.components.sensor import (
SensorEntityDescription, SensorEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import LIGHT_LUX
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -71,7 +72,7 @@ class AbodeSensor(AbodeDevice, SensorEntity):
elif description.key == CONST.HUMI_STATUS_KEY: elif description.key == CONST.HUMI_STATUS_KEY:
self._attr_native_unit_of_measurement = device.humidity_unit self._attr_native_unit_of_measurement = device.humidity_unit
elif description.key == CONST.LUX_STATUS_KEY: elif description.key == CONST.LUX_STATUS_KEY:
self._attr_native_unit_of_measurement = device.lux_unit self._attr_native_unit_of_measurement = LIGHT_LUX
@property @property
def native_value(self) -> float | None: def native_value(self) -> float | None:

View File

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/airzone_cloud", "documentation": "https://www.home-assistant.io/integrations/airzone_cloud",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["aioairzone_cloud"], "loggers": ["aioairzone_cloud"],
"requirements": ["aioairzone-cloud==0.1.7"] "requirements": ["aioairzone-cloud==0.1.8"]
} }

View File

@ -98,7 +98,7 @@ async def async_setup_entry(
tasks = [] tasks = []
for heater in data_connection.get_devices(): for heater in data_connection.get_devices():
tasks.append(heater.update_device_info()) tasks.append(asyncio.create_task(heater.update_device_info()))
await asyncio.wait(tasks) await asyncio.wait(tasks)
devs = [] devs = []

View File

@ -192,7 +192,7 @@ class Flexit(ClimateEntity):
result = float( result = float(
await self._async_read_int16_from_register(register_type, register) await self._async_read_int16_from_register(register_type, register)
) )
if result == -1: if not result:
return -1 return -1
return result / 10.0 return result / 10.0
@ -200,6 +200,6 @@ class Flexit(ClimateEntity):
result = await self._hub.async_pymodbus_call( result = await self._hub.async_pymodbus_call(
self._slave, register, value, CALL_TYPE_WRITE_REGISTER self._slave, register, value, CALL_TYPE_WRITE_REGISTER
) )
if result == -1: if not result:
return False return False
return True return True

View File

@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend", "documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system", "integration_type": "system",
"quality_scale": "internal", "quality_scale": "internal",
"requirements": ["home-assistant-frontend==20230607.0"] "requirements": ["home-assistant-frontend==20230608.0"]
} }

View File

@ -14,6 +14,6 @@
"documentation": "https://www.home-assistant.io/integrations/homekit_controller", "documentation": "https://www.home-assistant.io/integrations/homekit_controller",
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["aiohomekit", "commentjson"], "loggers": ["aiohomekit", "commentjson"],
"requirements": ["aiohomekit==2.6.4"], "requirements": ["aiohomekit==2.6.5"],
"zeroconf": ["_hap._tcp.local.", "_hap._udp.local."] "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."]
} }

View File

@ -120,7 +120,7 @@ class ImapMessage:
@property @property
def subject(self) -> str: def subject(self) -> str:
"""Decode the message subject.""" """Decode the message subject."""
decoded_header = decode_header(self.email_message["Subject"]) decoded_header = decode_header(self.email_message["Subject"] or "")
subject_header = make_header(decoded_header) subject_header = make_header(decoded_header)
return str(subject_header) return str(subject_header)

View File

@ -302,12 +302,9 @@ class InputSelect(collection.CollectionEntity, SelectEntity, RestoreEntity):
async def async_select_option(self, option: str) -> None: async def async_select_option(self, option: str) -> None:
"""Select new option.""" """Select new option."""
if option not in self.options: if option not in self.options:
_LOGGER.warning( raise HomeAssistantError(
"Invalid option: %s (possible options: %s)", f"Invalid option: {option} (possible options: {', '.join(self.options)})"
option,
", ".join(self.options),
) )
return
self._attr_current_option = option self._attr_current_option = option
self.async_write_ha_state() self.async_write_ha_state()

View File

@ -25,7 +25,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import SIGNAL_ADD_ENTITIES from .const import SIGNAL_ADD_ENTITIES
from .insteon_entity import InsteonEntity from .insteon_entity import InsteonEntity
from .utils import async_add_insteon_entities from .utils import async_add_insteon_devices, async_add_insteon_entities
SENSOR_TYPES = { SENSOR_TYPES = {
OPEN_CLOSE_SENSOR: BinarySensorDeviceClass.OPENING, OPEN_CLOSE_SENSOR: BinarySensorDeviceClass.OPENING,
@ -62,7 +62,12 @@ async def async_setup_entry(
signal = f"{SIGNAL_ADD_ENTITIES}_{Platform.BINARY_SENSOR}" signal = f"{SIGNAL_ADD_ENTITIES}_{Platform.BINARY_SENSOR}"
async_dispatcher_connect(hass, signal, async_add_insteon_binary_sensor_entities) async_dispatcher_connect(hass, signal, async_add_insteon_binary_sensor_entities)
async_add_insteon_binary_sensor_entities() async_add_insteon_devices(
hass,
Platform.BINARY_SENSOR,
InsteonBinarySensorEntity,
async_add_entities,
)
class InsteonBinarySensorEntity(InsteonEntity, BinarySensorEntity): class InsteonBinarySensorEntity(InsteonEntity, BinarySensorEntity):

View File

@ -23,7 +23,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import SIGNAL_ADD_ENTITIES from .const import SIGNAL_ADD_ENTITIES
from .insteon_entity import InsteonEntity from .insteon_entity import InsteonEntity
from .utils import async_add_insteon_entities from .utils import async_add_insteon_devices, async_add_insteon_entities
FAN_ONLY = "fan_only" FAN_ONLY = "fan_only"
@ -71,7 +71,12 @@ async def async_setup_entry(
signal = f"{SIGNAL_ADD_ENTITIES}_{Platform.CLIMATE}" signal = f"{SIGNAL_ADD_ENTITIES}_{Platform.CLIMATE}"
async_dispatcher_connect(hass, signal, async_add_insteon_climate_entities) async_dispatcher_connect(hass, signal, async_add_insteon_climate_entities)
async_add_insteon_climate_entities() async_add_insteon_devices(
hass,
Platform.CLIMATE,
InsteonClimateEntity,
async_add_entities,
)
class InsteonClimateEntity(InsteonEntity, ClimateEntity): class InsteonClimateEntity(InsteonEntity, ClimateEntity):

View File

@ -15,7 +15,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import SIGNAL_ADD_ENTITIES from .const import SIGNAL_ADD_ENTITIES
from .insteon_entity import InsteonEntity from .insteon_entity import InsteonEntity
from .utils import async_add_insteon_entities from .utils import async_add_insteon_devices, async_add_insteon_entities
async def async_setup_entry( async def async_setup_entry(
@ -34,7 +34,12 @@ async def async_setup_entry(
signal = f"{SIGNAL_ADD_ENTITIES}_{Platform.COVER}" signal = f"{SIGNAL_ADD_ENTITIES}_{Platform.COVER}"
async_dispatcher_connect(hass, signal, async_add_insteon_cover_entities) async_dispatcher_connect(hass, signal, async_add_insteon_cover_entities)
async_add_insteon_cover_entities() async_add_insteon_devices(
hass,
Platform.COVER,
InsteonCoverEntity,
async_add_entities,
)
class InsteonCoverEntity(InsteonEntity, CoverEntity): class InsteonCoverEntity(InsteonEntity, CoverEntity):

View File

@ -17,7 +17,7 @@ from homeassistant.util.percentage import (
from .const import SIGNAL_ADD_ENTITIES from .const import SIGNAL_ADD_ENTITIES
from .insteon_entity import InsteonEntity from .insteon_entity import InsteonEntity
from .utils import async_add_insteon_entities from .utils import async_add_insteon_devices, async_add_insteon_entities
SPEED_RANGE = (1, 255) # off is not included SPEED_RANGE = (1, 255) # off is not included
@ -38,7 +38,12 @@ async def async_setup_entry(
signal = f"{SIGNAL_ADD_ENTITIES}_{Platform.FAN}" signal = f"{SIGNAL_ADD_ENTITIES}_{Platform.FAN}"
async_dispatcher_connect(hass, signal, async_add_insteon_fan_entities) async_dispatcher_connect(hass, signal, async_add_insteon_fan_entities)
async_add_insteon_fan_entities() async_add_insteon_devices(
hass,
Platform.FAN,
InsteonFanEntity,
async_add_entities,
)
class InsteonFanEntity(InsteonEntity, FanEntity): class InsteonFanEntity(InsteonEntity, FanEntity):

View File

@ -1,4 +1,7 @@
"""Utility methods for the Insteon platform.""" """Utility methods for the Insteon platform."""
from collections.abc import Iterable
from pyinsteon.device_types.device_base import Device
from pyinsteon.device_types.ipdb import ( from pyinsteon.device_types.ipdb import (
AccessControl_Morningstar, AccessControl_Morningstar,
ClimateControl_Thermostat, ClimateControl_Thermostat,
@ -44,7 +47,7 @@ from pyinsteon.device_types.ipdb import (
from homeassistant.const import Platform from homeassistant.const import Platform
DEVICE_PLATFORM = { DEVICE_PLATFORM: dict[Device, dict[Platform, Iterable[int]]] = {
AccessControl_Morningstar: {Platform.LOCK: [1]}, AccessControl_Morningstar: {Platform.LOCK: [1]},
DimmableLightingControl: {Platform.LIGHT: [1]}, DimmableLightingControl: {Platform.LIGHT: [1]},
DimmableLightingControl_Dial: {Platform.LIGHT: [1]}, DimmableLightingControl_Dial: {Platform.LIGHT: [1]},
@ -101,11 +104,11 @@ DEVICE_PLATFORM = {
} }
def get_device_platforms(device): def get_device_platforms(device) -> dict[Platform, Iterable[int]]:
"""Return the HA platforms for a device type.""" """Return the HA platforms for a device type."""
return DEVICE_PLATFORM.get(type(device), {}).keys() return DEVICE_PLATFORM.get(type(device), {})
def get_platform_groups(device, domain) -> dict: def get_device_platform_groups(device: Device, platform: Platform) -> Iterable[int]:
"""Return the platforms that a device belongs in.""" """Return the list of device groups for a platform."""
return DEVICE_PLATFORM.get(type(device), {}).get(domain, {}) # type: ignore[attr-defined] return get_device_platforms(device).get(platform, [])

View File

@ -12,7 +12,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import SIGNAL_ADD_ENTITIES from .const import SIGNAL_ADD_ENTITIES
from .insteon_entity import InsteonEntity from .insteon_entity import InsteonEntity
from .utils import async_add_insteon_entities from .utils import async_add_insteon_devices, async_add_insteon_entities
MAX_BRIGHTNESS = 255 MAX_BRIGHTNESS = 255
@ -37,7 +37,12 @@ async def async_setup_entry(
signal = f"{SIGNAL_ADD_ENTITIES}_{Platform.LIGHT}" signal = f"{SIGNAL_ADD_ENTITIES}_{Platform.LIGHT}"
async_dispatcher_connect(hass, signal, async_add_insteon_light_entities) async_dispatcher_connect(hass, signal, async_add_insteon_light_entities)
async_add_insteon_light_entities() async_add_insteon_devices(
hass,
Platform.LIGHT,
InsteonDimmerEntity,
async_add_entities,
)
class InsteonDimmerEntity(InsteonEntity, LightEntity): class InsteonDimmerEntity(InsteonEntity, LightEntity):

View File

@ -11,7 +11,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import SIGNAL_ADD_ENTITIES from .const import SIGNAL_ADD_ENTITIES
from .insteon_entity import InsteonEntity from .insteon_entity import InsteonEntity
from .utils import async_add_insteon_entities from .utils import async_add_insteon_devices, async_add_insteon_entities
async def async_setup_entry( async def async_setup_entry(
@ -30,7 +30,12 @@ async def async_setup_entry(
signal = f"{SIGNAL_ADD_ENTITIES}_{Platform.LOCK}" signal = f"{SIGNAL_ADD_ENTITIES}_{Platform.LOCK}"
async_dispatcher_connect(hass, signal, async_add_insteon_lock_entities) async_dispatcher_connect(hass, signal, async_add_insteon_lock_entities)
async_add_insteon_lock_entities() async_add_insteon_devices(
hass,
Platform.LOCK,
InsteonLockEntity,
async_add_entities,
)
class InsteonLockEntity(InsteonEntity, LockEntity): class InsteonLockEntity(InsteonEntity, LockEntity):

View File

@ -10,7 +10,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import SIGNAL_ADD_ENTITIES from .const import SIGNAL_ADD_ENTITIES
from .insteon_entity import InsteonEntity from .insteon_entity import InsteonEntity
from .utils import async_add_insteon_entities from .utils import async_add_insteon_devices, async_add_insteon_entities
async def async_setup_entry( async def async_setup_entry(
@ -33,7 +33,12 @@ async def async_setup_entry(
signal = f"{SIGNAL_ADD_ENTITIES}_{Platform.SWITCH}" signal = f"{SIGNAL_ADD_ENTITIES}_{Platform.SWITCH}"
async_dispatcher_connect(hass, signal, async_add_insteon_switch_entities) async_dispatcher_connect(hass, signal, async_add_insteon_switch_entities)
async_add_insteon_switch_entities() async_add_insteon_devices(
hass,
Platform.SWITCH,
InsteonSwitchEntity,
async_add_entities,
)
class InsteonSwitchEntity(InsteonEntity, SwitchEntity): class InsteonSwitchEntity(InsteonEntity, SwitchEntity):

View File

@ -1,7 +1,10 @@
"""Utilities used by insteon component.""" """Utilities used by insteon component."""
from __future__ import annotations
import asyncio import asyncio
from collections.abc import Callable from collections.abc import Callable
import logging import logging
from typing import TYPE_CHECKING, Any
from pyinsteon import devices from pyinsteon import devices
from pyinsteon.address import Address from pyinsteon.address import Address
@ -30,6 +33,7 @@ from homeassistant.const import (
CONF_ENTITY_ID, CONF_ENTITY_ID,
CONF_PLATFORM, CONF_PLATFORM,
ENTITY_MATCH_ALL, ENTITY_MATCH_ALL,
Platform,
) )
from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import device_registry as dr
@ -38,6 +42,7 @@ from homeassistant.helpers.dispatcher import (
async_dispatcher_send, async_dispatcher_send,
dispatcher_send, dispatcher_send,
) )
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ( from .const import (
CONF_CAT, CONF_CAT,
@ -78,7 +83,7 @@ from .const import (
SRV_X10_ALL_LIGHTS_ON, SRV_X10_ALL_LIGHTS_ON,
SRV_X10_ALL_UNITS_OFF, SRV_X10_ALL_UNITS_OFF,
) )
from .ipdb import get_device_platforms, get_platform_groups from .ipdb import get_device_platform_groups, get_device_platforms
from .schemas import ( from .schemas import (
ADD_ALL_LINK_SCHEMA, ADD_ALL_LINK_SCHEMA,
ADD_DEFAULT_LINKS_SCHEMA, ADD_DEFAULT_LINKS_SCHEMA,
@ -89,6 +94,9 @@ from .schemas import (
X10_HOUSECODE_SCHEMA, X10_HOUSECODE_SCHEMA,
) )
if TYPE_CHECKING:
from .insteon_entity import InsteonEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -132,6 +140,9 @@ def add_insteon_events(hass: HomeAssistant, device: Device) -> None:
_LOGGER.debug("Firing event %s with %s", event, schema) _LOGGER.debug("Firing event %s with %s", event, schema)
hass.bus.async_fire(event, schema) hass.bus.async_fire(event, schema)
if str(device.address).startswith("X10"):
return
for name_or_group, event in device.events.items(): for name_or_group, event in device.events.items():
if isinstance(name_or_group, int): if isinstance(name_or_group, int):
for _, event in device.events[name_or_group].items(): for _, event in device.events[name_or_group].items():
@ -158,8 +169,10 @@ def register_new_device_callback(hass):
await device.async_status() await device.async_status()
platforms = get_device_platforms(device) platforms = get_device_platforms(device)
for platform in platforms: for platform in platforms:
groups = get_device_platform_groups(device, platform)
signal = f"{SIGNAL_ADD_ENTITIES}_{platform}" signal = f"{SIGNAL_ADD_ENTITIES}_{platform}"
dispatcher_send(hass, signal, {"address": device.address}) dispatcher_send(hass, signal, {"address": device.address, "groups": groups})
add_insteon_events(hass, device)
devices.subscribe(async_new_insteon_device, force_strong_ref=True) devices.subscribe(async_new_insteon_device, force_strong_ref=True)
@ -383,20 +396,38 @@ def print_aldb_to_log(aldb):
@callback @callback
def async_add_insteon_entities( def async_add_insteon_entities(
hass, platform, entity_type, async_add_entities, discovery_info hass: HomeAssistant,
): platform: Platform,
"""Add Insteon devices to a platform.""" entity_type: type[InsteonEntity],
new_entities = [] async_add_entities: AddEntitiesCallback,
device_list = [discovery_info.get("address")] if discovery_info else devices discovery_info: dict[str, Any],
) -> None:
for address in device_list: """Add an Insteon group to a platform."""
device = devices[address] address = discovery_info["address"]
groups = get_platform_groups(device, platform) device = devices[address]
for group in groups: new_entities = [
new_entities.append(entity_type(device, group)) entity_type(device=device, group=group) for group in discovery_info["groups"]
]
async_add_entities(new_entities) async_add_entities(new_entities)
@callback
def async_add_insteon_devices(
hass: HomeAssistant,
platform: Platform,
entity_type: type[InsteonEntity],
async_add_entities: AddEntitiesCallback,
) -> None:
"""Add all entities to a platform."""
for address in devices:
device = devices[address]
groups = get_device_platform_groups(device, platform)
discovery_info = {"address": address, "groups": groups}
async_add_insteon_entities(
hass, platform, entity_type, async_add_entities, discovery_info
)
def get_usb_ports() -> dict[str, str]: def get_usb_ports() -> dict[str, str]:
"""Return a dict of USB ports and their friendly names.""" """Return a dict of USB ports and their friendly names."""
ports = list_ports.comports() ports = list_ports.comports()

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from typing import Any from typing import Any
from pylast import LastFMNetwork, User, WSError from pylast import LastFMNetwork, PyLastError, User, WSError
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import ( from homeassistant.config_entries import (
@ -128,11 +128,14 @@ class LastFmConfigFlowHandler(ConfigFlow, domain=DOMAIN):
main_user, _ = get_lastfm_user( main_user, _ = get_lastfm_user(
self.data[CONF_API_KEY], self.data[CONF_MAIN_USER] self.data[CONF_API_KEY], self.data[CONF_MAIN_USER]
) )
friends_response = await self.hass.async_add_executor_job(
main_user.get_friends
)
friends = [ friends = [
SelectOptionDict(value=friend.name, label=friend.get_name(True)) SelectOptionDict(value=friend.name, label=friend.get_name(True))
for friend in main_user.get_friends() for friend in friends_response
] ]
except WSError: except PyLastError:
friends = [] friends = []
return self.async_show_form( return self.async_show_form(
step_id="friends", step_id="friends",
@ -197,11 +200,14 @@ class LastFmOptionsFlowHandler(OptionsFlowWithConfigEntry):
self.options[CONF_API_KEY], self.options[CONF_API_KEY],
self.options[CONF_MAIN_USER], self.options[CONF_MAIN_USER],
) )
friends_response = await self.hass.async_add_executor_job(
main_user.get_friends
)
friends = [ friends = [
SelectOptionDict(value=friend.name, label=friend.get_name(True)) SelectOptionDict(value=friend.name, label=friend.get_name(True))
for friend in main_user.get_friends() for friend in friends_response
] ]
except WSError: except PyLastError:
friends = [] friends = []
else: else:
friends = [] friends = []

View File

@ -13,7 +13,6 @@ from homeassistant.helpers.typing import ConfigType
from . import websocket_api from . import websocket_api
from .const import ( from .const import (
ATTR_LEVEL, ATTR_LEVEL,
DEFAULT_LOGSEVERITY,
DOMAIN, DOMAIN,
LOGGER_DEFAULT, LOGGER_DEFAULT,
LOGGER_FILTERS, LOGGER_FILTERS,
@ -39,9 +38,7 @@ CONFIG_SCHEMA = vol.Schema(
{ {
DOMAIN: vol.Schema( DOMAIN: vol.Schema(
{ {
vol.Optional( vol.Optional(LOGGER_DEFAULT): _VALID_LOG_LEVEL,
LOGGER_DEFAULT, default=DEFAULT_LOGSEVERITY
): _VALID_LOG_LEVEL,
vol.Optional(LOGGER_LOGS): vol.Schema({cv.string: _VALID_LOG_LEVEL}), vol.Optional(LOGGER_LOGS): vol.Schema({cv.string: _VALID_LOG_LEVEL}),
vol.Optional(LOGGER_FILTERS): vol.Schema({cv.string: [cv.is_regex]}), vol.Optional(LOGGER_FILTERS): vol.Schema({cv.string: [cv.is_regex]}),
} }

View File

@ -119,7 +119,7 @@ class LoggerSettings:
self._yaml_config = yaml_config self._yaml_config = yaml_config
self._default_level = logging.INFO self._default_level = logging.INFO
if DOMAIN in yaml_config: if DOMAIN in yaml_config and LOGGER_DEFAULT in yaml_config[DOMAIN]:
self._default_level = yaml_config[DOMAIN][LOGGER_DEFAULT] self._default_level = yaml_config[DOMAIN][LOGGER_DEFAULT]
self._store: Store[dict[str, dict[str, dict[str, Any]]]] = Store( self._store: Store[dict[str, dict[str, dict[str, Any]]]] = Store(
hass, STORAGE_VERSION, STORAGE_KEY hass, STORAGE_VERSION, STORAGE_KEY

View File

@ -38,7 +38,7 @@ def async_setup(hass: HomeAssistant) -> None:
class LocalSource(MediaSource): class LocalSource(MediaSource):
"""Provide local directories as media sources.""" """Provide local directories as media sources."""
name: str = "Local Media" name: str = "My media"
def __init__(self, hass: HomeAssistant) -> None: def __init__(self, hass: HomeAssistant) -> None:
"""Initialize local source.""" """Initialize local source."""

View File

@ -12,5 +12,5 @@
"dependencies": ["bluetooth_adapters"], "dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/melnor", "documentation": "https://www.home-assistant.io/integrations/melnor",
"iot_class": "local_polling", "iot_class": "local_polling",
"requirements": ["melnor-bluetooth==0.0.24"] "requirements": ["melnor-bluetooth==0.0.25"]
} }

View File

@ -4,5 +4,5 @@
"codeowners": ["@joostlek"], "codeowners": ["@joostlek"],
"documentation": "https://www.home-assistant.io/integrations/opensky", "documentation": "https://www.home-assistant.io/integrations/opensky",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"requirements": ["python-opensky==0.0.7"] "requirements": ["python-opensky==0.0.9"]
} }

View File

@ -78,7 +78,7 @@ def setup_platform(
latitude = config.get(CONF_LATITUDE, hass.config.latitude) latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
radius = config.get(CONF_RADIUS, 0) radius = config.get(CONF_RADIUS, 0)
bounding_box = OpenSky.get_bounding_box(latitude, longitude, radius) bounding_box = OpenSky.get_bounding_box(latitude, longitude, radius * 1000)
session = async_get_clientsession(hass) session = async_get_clientsession(hass)
opensky = OpenSky(session=session) opensky = OpenSky(session=session)
add_entities( add_entities(

View File

@ -8,5 +8,5 @@
"documentation": "https://www.home-assistant.io/integrations/otbr", "documentation": "https://www.home-assistant.io/integrations/otbr",
"integration_type": "service", "integration_type": "service",
"iot_class": "local_polling", "iot_class": "local_polling",
"requirements": ["python-otbr-api==2.1.0"] "requirements": ["python-otbr-api==2.2.0"]
} }

View File

@ -95,6 +95,11 @@ class OTBRData:
"""Create an active operational dataset.""" """Create an active operational dataset."""
return await self.api.create_active_dataset(dataset) return await self.api.create_active_dataset(dataset)
@_handle_otbr_error
async def delete_active_dataset(self) -> None:
"""Delete the active operational dataset."""
return await self.api.delete_active_dataset()
@_handle_otbr_error @_handle_otbr_error
async def set_active_dataset_tlvs(self, dataset: bytes) -> None: async def set_active_dataset_tlvs(self, dataset: bytes) -> None:
"""Set current active operational dataset in TLVS format.""" """Set current active operational dataset in TLVS format."""

View File

@ -81,6 +81,12 @@ async def websocket_create_network(
connection.send_error(msg["id"], "set_enabled_failed", str(exc)) connection.send_error(msg["id"], "set_enabled_failed", str(exc))
return return
try:
await data.delete_active_dataset()
except HomeAssistantError as exc:
connection.send_error(msg["id"], "delete_active_dataset_failed", str(exc))
return
try: try:
await data.create_active_dataset( await data.create_active_dataset(
python_otbr_api.ActiveDataSet( python_otbr_api.ActiveDataSet(

View File

@ -13,7 +13,7 @@
"integration_type": "hub", "integration_type": "hub",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["boto3", "botocore", "pyhumps", "pyoverkiz", "s3transfer"], "loggers": ["boto3", "botocore", "pyhumps", "pyoverkiz", "s3transfer"],
"requirements": ["pyoverkiz==1.7.9"], "requirements": ["pyoverkiz==1.8.0"],
"zeroconf": [ "zeroconf": [
{ {
"type": "_kizbox._tcp.local.", "type": "_kizbox._tcp.local.",

View File

@ -4,5 +4,5 @@
"codeowners": [], "codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/pulseaudio_loopback", "documentation": "https://www.home-assistant.io/integrations/pulseaudio_loopback",
"iot_class": "local_polling", "iot_class": "local_polling",
"requirements": ["pulsectl==20.2.4"] "requirements": ["pulsectl==23.5.2"]
} }

View File

@ -7,6 +7,6 @@
"documentation": "https://www.home-assistant.io/integrations/thread", "documentation": "https://www.home-assistant.io/integrations/thread",
"integration_type": "service", "integration_type": "service",
"iot_class": "local_polling", "iot_class": "local_polling",
"requirements": ["python-otbr-api==2.1.0", "pyroute2==0.7.5"], "requirements": ["python-otbr-api==2.2.0", "pyroute2==0.7.5"],
"zeroconf": ["_meshcop._udp.local."] "zeroconf": ["_meshcop._udp.local."]
} }

View File

@ -41,7 +41,7 @@
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["pyunifiprotect", "unifi_discovery"], "loggers": ["pyunifiprotect", "unifi_discovery"],
"quality_scale": "platinum", "quality_scale": "platinum",
"requirements": ["pyunifiprotect==4.10.1", "unifi-discovery==1.1.7"], "requirements": ["pyunifiprotect==4.10.2", "unifi-discovery==1.1.7"],
"ssdp": [ "ssdp": [
{ {
"manufacturer": "Ubiquiti Networks", "manufacturer": "Ubiquiti Networks",

View File

@ -8,7 +8,7 @@ from .backports.enum import StrEnum
APPLICATION_NAME: Final = "HomeAssistant" APPLICATION_NAME: Final = "HomeAssistant"
MAJOR_VERSION: Final = 2023 MAJOR_VERSION: Final = 2023
MINOR_VERSION: Final = 6 MINOR_VERSION: Final = 6
PATCH_VERSION: Final = "0" PATCH_VERSION: Final = "1"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 10, 0) REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 10, 0)

View File

@ -25,7 +25,7 @@ ha-av==10.1.0
hass-nabucasa==0.67.1 hass-nabucasa==0.67.1
hassil==1.0.6 hassil==1.0.6
home-assistant-bluetooth==1.10.0 home-assistant-bluetooth==1.10.0
home-assistant-frontend==20230607.0 home-assistant-frontend==20230608.0
home-assistant-intents==2023.6.5 home-assistant-intents==2023.6.5
httpx==0.24.1 httpx==0.24.1
ifaddr==0.2.0 ifaddr==0.2.0
@ -128,9 +128,8 @@ authlib<1.0
# Version 2.0 added typing, prevent accidental fallbacks # Version 2.0 added typing, prevent accidental fallbacks
backoff>=2.0 backoff>=2.0
# Breaking change in version # Require to avoid issues with decorators (#93904). v2 has breaking changes.
# https://github.com/samuelcolvin/pydantic/issues/4092 pydantic>=1.10.8,<2.0
pydantic!=1.9.1
# Breaks asyncio # Breaks asyncio
# https://github.com/pubnub/python/issues/130 # https://github.com/pubnub/python/issues/130

View File

@ -245,7 +245,10 @@ async def _async_setup_component(
severity=IssueSeverity.ERROR, severity=IssueSeverity.ERROR,
issue_domain=domain, issue_domain=domain,
translation_key="integration_key_no_support", translation_key="integration_key_no_support",
translation_placeholders={"domain": domain}, translation_placeholders={
"domain": domain,
"add_integration": f"/config/integrations/dashboard/add?domain={domain}",
},
) )
start = timer() start = timer()

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "homeassistant" name = "homeassistant"
version = "2023.6.0" version = "2023.6.1"
license = {text = "Apache-2.0"} license = {text = "Apache-2.0"}
description = "Open-source home automation platform running on Python 3." description = "Open-source home automation platform running on Python 3."
readme = "README.rst" readme = "README.rst"

View File

@ -116,7 +116,7 @@ aio_georss_gdacs==0.8
aioairq==0.2.4 aioairq==0.2.4
# homeassistant.components.airzone_cloud # homeassistant.components.airzone_cloud
aioairzone-cloud==0.1.7 aioairzone-cloud==0.1.8
# homeassistant.components.airzone # homeassistant.components.airzone
aioairzone==0.6.3 aioairzone==0.6.3
@ -177,7 +177,7 @@ aioguardian==2022.07.0
aioharmony==0.2.10 aioharmony==0.2.10
# homeassistant.components.homekit_controller # homeassistant.components.homekit_controller
aiohomekit==2.6.4 aiohomekit==2.6.5
# homeassistant.components.emulated_hue # homeassistant.components.emulated_hue
# homeassistant.components.http # homeassistant.components.http
@ -924,7 +924,7 @@ hole==0.8.0
holidays==0.21.13 holidays==0.21.13
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20230607.0 home-assistant-frontend==20230608.0
# homeassistant.components.conversation # homeassistant.components.conversation
home-assistant-intents==2023.6.5 home-assistant-intents==2023.6.5
@ -1122,7 +1122,7 @@ mcstatus==6.0.0
meater-python==0.0.8 meater-python==0.0.8
# homeassistant.components.melnor # homeassistant.components.melnor
melnor-bluetooth==0.0.24 melnor-bluetooth==0.0.25
# homeassistant.components.message_bird # homeassistant.components.message_bird
messagebird==1.2.0 messagebird==1.2.0
@ -1418,7 +1418,7 @@ psutil-home-assistant==0.0.1
psutil==5.9.5 psutil==5.9.5
# homeassistant.components.pulseaudio_loopback # homeassistant.components.pulseaudio_loopback
pulsectl==20.2.4 pulsectl==23.5.2
# homeassistant.components.androidtv # homeassistant.components.androidtv
pure-python-adb[async]==0.3.0.dev0 pure-python-adb[async]==0.3.0.dev0
@ -1872,7 +1872,7 @@ pyotgw==2.1.3
pyotp==2.8.0 pyotp==2.8.0
# homeassistant.components.overkiz # homeassistant.components.overkiz
pyoverkiz==1.7.9 pyoverkiz==1.8.0
# homeassistant.components.openweathermap # homeassistant.components.openweathermap
pyowm==3.2.0 pyowm==3.2.0
@ -2102,11 +2102,11 @@ python-mystrom==2.2.0
python-nest==4.2.0 python-nest==4.2.0
# homeassistant.components.opensky # homeassistant.components.opensky
python-opensky==0.0.7 python-opensky==0.0.9
# homeassistant.components.otbr # homeassistant.components.otbr
# homeassistant.components.thread # homeassistant.components.thread
python-otbr-api==2.1.0 python-otbr-api==2.2.0
# homeassistant.components.picnic # homeassistant.components.picnic
python-picnic-api==1.1.0 python-picnic-api==1.1.0
@ -2168,7 +2168,7 @@ pytrafikverket==0.3.3
pyudev==0.23.2 pyudev==0.23.2
# homeassistant.components.unifiprotect # homeassistant.components.unifiprotect
pyunifiprotect==4.10.1 pyunifiprotect==4.10.2
# homeassistant.components.uptimerobot # homeassistant.components.uptimerobot
pyuptimerobot==22.2.0 pyuptimerobot==22.2.0

View File

@ -106,7 +106,7 @@ aio_georss_gdacs==0.8
aioairq==0.2.4 aioairq==0.2.4
# homeassistant.components.airzone_cloud # homeassistant.components.airzone_cloud
aioairzone-cloud==0.1.7 aioairzone-cloud==0.1.8
# homeassistant.components.airzone # homeassistant.components.airzone
aioairzone==0.6.3 aioairzone==0.6.3
@ -164,7 +164,7 @@ aioguardian==2022.07.0
aioharmony==0.2.10 aioharmony==0.2.10
# homeassistant.components.homekit_controller # homeassistant.components.homekit_controller
aiohomekit==2.6.4 aiohomekit==2.6.5
# homeassistant.components.emulated_hue # homeassistant.components.emulated_hue
# homeassistant.components.http # homeassistant.components.http
@ -716,7 +716,7 @@ hole==0.8.0
holidays==0.21.13 holidays==0.21.13
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20230607.0 home-assistant-frontend==20230608.0
# homeassistant.components.conversation # homeassistant.components.conversation
home-assistant-intents==2023.6.5 home-assistant-intents==2023.6.5
@ -848,7 +848,7 @@ mcstatus==6.0.0
meater-python==0.0.8 meater-python==0.0.8
# homeassistant.components.melnor # homeassistant.components.melnor
melnor-bluetooth==0.0.24 melnor-bluetooth==0.0.25
# homeassistant.components.meteo_france # homeassistant.components.meteo_france
meteofrance-api==1.2.0 meteofrance-api==1.2.0
@ -1382,7 +1382,7 @@ pyotgw==2.1.3
pyotp==2.8.0 pyotp==2.8.0
# homeassistant.components.overkiz # homeassistant.components.overkiz
pyoverkiz==1.7.9 pyoverkiz==1.8.0
# homeassistant.components.openweathermap # homeassistant.components.openweathermap
pyowm==3.2.0 pyowm==3.2.0
@ -1532,7 +1532,7 @@ python-nest==4.2.0
# homeassistant.components.otbr # homeassistant.components.otbr
# homeassistant.components.thread # homeassistant.components.thread
python-otbr-api==2.1.0 python-otbr-api==2.2.0
# homeassistant.components.picnic # homeassistant.components.picnic
python-picnic-api==1.1.0 python-picnic-api==1.1.0
@ -1579,7 +1579,7 @@ pytrafikverket==0.3.3
pyudev==0.23.2 pyudev==0.23.2
# homeassistant.components.unifiprotect # homeassistant.components.unifiprotect
pyunifiprotect==4.10.1 pyunifiprotect==4.10.2
# homeassistant.components.uptimerobot # homeassistant.components.uptimerobot
pyuptimerobot==22.2.0 pyuptimerobot==22.2.0

View File

@ -132,9 +132,8 @@ authlib<1.0
# Version 2.0 added typing, prevent accidental fallbacks # Version 2.0 added typing, prevent accidental fallbacks
backoff>=2.0 backoff>=2.0
# Breaking change in version # Require to avoid issues with decorators (#93904). v2 has breaking changes.
# https://github.com/samuelcolvin/pydantic/issues/4092 pydantic>=1.10.8,<2.0
pydantic!=1.9.1
# Breaks asyncio # Breaks asyncio
# https://github.com/pubnub/python/issues/130 # https://github.com/pubnub/python/issues/130

View File

@ -39,7 +39,7 @@ async def test_attributes(hass: HomeAssistant) -> None:
state = hass.states.get("sensor.environment_sensor_lux") state = hass.states.get("sensor.environment_sensor_lux")
assert state.state == "1.0" assert state.state == "1.0"
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "lux" assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "lx"
state = hass.states.get("sensor.environment_sensor_temperature") state = hass.states.get("sensor.environment_sensor_temperature")
# Abodepy device JSON reports 19.5, but Home Assistant shows 19.4 # Abodepy device JSON reports 19.5, but Home Assistant shows 19.4

View File

@ -24,7 +24,12 @@ TEST_MESSAGE_HEADERS2 = (
b"Subject: Test subject\r\n" b"Subject: Test subject\r\n"
) )
TEST_MESSAGE_HEADERS3 = b""
TEST_MESSAGE = TEST_MESSAGE_HEADERS1 + DATE_HEADER1 + TEST_MESSAGE_HEADERS2 TEST_MESSAGE = TEST_MESSAGE_HEADERS1 + DATE_HEADER1 + TEST_MESSAGE_HEADERS2
TEST_MESSAGE_NO_SUBJECT_TO_FROM = (
TEST_MESSAGE_HEADERS1 + DATE_HEADER1 + TEST_MESSAGE_HEADERS3
)
TEST_MESSAGE_ALT = TEST_MESSAGE_HEADERS1 + DATE_HEADER2 + TEST_MESSAGE_HEADERS2 TEST_MESSAGE_ALT = TEST_MESSAGE_HEADERS1 + DATE_HEADER2 + TEST_MESSAGE_HEADERS2
TEST_INVALID_DATE1 = ( TEST_INVALID_DATE1 = (
TEST_MESSAGE_HEADERS1 + DATE_HEADER_INVALID1 + TEST_MESSAGE_HEADERS2 TEST_MESSAGE_HEADERS1 + DATE_HEADER_INVALID1 + TEST_MESSAGE_HEADERS2
@ -204,4 +209,19 @@ TEST_FETCH_RESPONSE_MULTIPART = (
], ],
) )
TEST_FETCH_RESPONSE_NO_SUBJECT_TO_FROM = (
"OK",
[
b"1 FETCH (BODY[] {"
+ str(len(TEST_MESSAGE_NO_SUBJECT_TO_FROM + TEST_CONTENT_TEXT_PLAIN)).encode(
"utf-8"
)
+ b"}",
bytearray(TEST_MESSAGE_NO_SUBJECT_TO_FROM + TEST_CONTENT_TEXT_PLAIN),
b")",
b"Fetch completed (0.0001 + 0.000 secs).",
],
)
RESPONSE_BAD = ("BAD", []) RESPONSE_BAD = ("BAD", [])

View File

@ -1,6 +1,6 @@
"""Test the imap entry initialization.""" """Test the imap entry initialization."""
import asyncio import asyncio
from datetime import datetime, timedelta from datetime import datetime, timedelta, timezone
from typing import Any from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
@ -22,6 +22,7 @@ from .const import (
TEST_FETCH_RESPONSE_INVALID_DATE2, TEST_FETCH_RESPONSE_INVALID_DATE2,
TEST_FETCH_RESPONSE_INVALID_DATE3, TEST_FETCH_RESPONSE_INVALID_DATE3,
TEST_FETCH_RESPONSE_MULTIPART, TEST_FETCH_RESPONSE_MULTIPART,
TEST_FETCH_RESPONSE_NO_SUBJECT_TO_FROM,
TEST_FETCH_RESPONSE_TEXT_BARE, TEST_FETCH_RESPONSE_TEXT_BARE,
TEST_FETCH_RESPONSE_TEXT_OTHER, TEST_FETCH_RESPONSE_TEXT_OTHER,
TEST_FETCH_RESPONSE_TEXT_PLAIN, TEST_FETCH_RESPONSE_TEXT_PLAIN,
@ -153,6 +154,44 @@ async def test_receiving_message_successfully(
) )
@pytest.mark.parametrize("imap_search", [TEST_SEARCH_RESPONSE])
@pytest.mark.parametrize("imap_fetch", [TEST_FETCH_RESPONSE_NO_SUBJECT_TO_FROM])
@pytest.mark.parametrize("imap_has_capability", [True, False], ids=["push", "poll"])
async def test_receiving_message_no_subject_to_from(
hass: HomeAssistant, mock_imap_protocol: MagicMock
) -> None:
"""Test receiving a message successfully without subject, to and from in body."""
event_called = async_capture_events(hass, "imap_content")
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
# Make sure we have had one update (when polling)
async_fire_time_changed(hass, utcnow() + timedelta(seconds=5))
await hass.async_block_till_done()
state = hass.states.get("sensor.imap_email_email_com")
# we should have received one message
assert state is not None
assert state.state == "1"
# we should have received one event
assert len(event_called) == 1
data: dict[str, Any] = event_called[0].data
assert data["server"] == "imap.server.com"
assert data["username"] == "email@email.com"
assert data["search"] == "UnSeen UnDeleted"
assert data["folder"] == "INBOX"
assert data["sender"] == ""
assert data["subject"] == ""
assert data["date"] == datetime(
2023, 3, 24, 13, 52, tzinfo=timezone(timedelta(seconds=3600))
)
assert data["text"] == "Test body\r\n\r\n"
assert data["headers"]["Return-Path"] == ("<john.doe@example.com>",)
assert data["headers"]["Delivered-To"] == ("notify@example.com",)
@pytest.mark.parametrize("imap_has_capability", [True, False], ids=["push", "poll"]) @pytest.mark.parametrize("imap_has_capability", [True, False], ids=["push", "poll"])
@pytest.mark.parametrize( @pytest.mark.parametrize(
("imap_login_state", "success"), [(AUTH, True), (NONAUTH, False)] ("imap_login_state", "success"), [(AUTH, True), (NONAUTH, False)]

View File

@ -102,12 +102,13 @@ async def test_select_option(hass: HomeAssistant) -> None:
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
assert state.state == "another option" assert state.state == "another option"
await hass.services.async_call( with pytest.raises(HomeAssistantError):
DOMAIN, await hass.services.async_call(
SERVICE_SELECT_OPTION, DOMAIN,
{ATTR_ENTITY_ID: entity_id, ATTR_OPTION: "non existing option"}, SERVICE_SELECT_OPTION,
blocking=True, {ATTR_ENTITY_ID: entity_id, ATTR_OPTION: "non existing option"},
) blocking=True,
)
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
assert state.state == "another option" assert state.state == "another option"
@ -305,12 +306,13 @@ async def test_set_options_service(hass: HomeAssistant) -> None:
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
assert state.state == "test1" assert state.state == "test1"
await hass.services.async_call( with pytest.raises(HomeAssistantError):
DOMAIN, await hass.services.async_call(
SERVICE_SELECT_OPTION, DOMAIN,
{ATTR_ENTITY_ID: entity_id, ATTR_OPTION: "first option"}, SERVICE_SELECT_OPTION,
blocking=True, {ATTR_ENTITY_ID: entity_id, ATTR_OPTION: "first option"},
) blocking=True,
)
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
assert state.state == "test1" assert state.state == "test1"

View File

@ -2,6 +2,7 @@
import pytest import pytest
from homeassistant.core import HomeAssistant, State from homeassistant.core import HomeAssistant, State
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.state import async_reproduce_state from homeassistant.helpers.state import async_reproduce_state
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@ -60,7 +61,8 @@ async def test_reproducing_states(
assert hass.states.get(ENTITY).state == VALID_OPTION3 assert hass.states.get(ENTITY).state == VALID_OPTION3
# Test setting state to invalid state # Test setting state to invalid state
await async_reproduce_state(hass, [State(ENTITY, INVALID_OPTION)]) with pytest.raises(HomeAssistantError):
await async_reproduce_state(hass, [State(ENTITY, INVALID_OPTION)])
# The entity state should be unchanged # The entity state should be unchanged
assert hass.states.get(ENTITY).state == VALID_OPTION3 assert hass.states.get(ENTITY).state == VALID_OPTION3

View File

@ -1,7 +1,7 @@
"""The tests for lastfm.""" """The tests for lastfm."""
from unittest.mock import patch from unittest.mock import patch
from pylast import Track, WSError from pylast import PyLastError, Track
from homeassistant.components.lastfm.const import CONF_MAIN_USER, CONF_USERS from homeassistant.components.lastfm.const import CONF_MAIN_USER, CONF_USERS
from homeassistant.const import CONF_API_KEY from homeassistant.const import CONF_API_KEY
@ -65,7 +65,7 @@ class MockUser:
def get_friends(self): def get_friends(self):
"""Get mock friends.""" """Get mock friends."""
if self._has_friends is False: if self._has_friends is False:
raise WSError("network", "status", "Page not found") raise PyLastError("network", "status", "Page not found")
return [MockUser(None, None, True, USERNAME_2)] return [MockUser(None, None, True, USERNAME_2)]

View File

@ -95,7 +95,7 @@ async def test_async_browse_media(hass: HomeAssistant) -> None:
media = await media_source.async_browse_media(hass, const.URI_SCHEME) media = await media_source.async_browse_media(hass, const.URI_SCHEME)
assert isinstance(media, media_source.models.BrowseMediaSource) assert isinstance(media, media_source.models.BrowseMediaSource)
assert len(media.children) == 1 assert len(media.children) == 1
assert media.children[0].title == "Local Media" assert media.children[0].title == "My media"
async def test_async_resolve_media(hass: HomeAssistant) -> None: async def test_async_resolve_media(hass: HomeAssistant) -> None:

View File

@ -84,6 +84,8 @@ async def test_create_network(
with patch( with patch(
"python_otbr_api.OTBR.create_active_dataset" "python_otbr_api.OTBR.create_active_dataset"
) as create_dataset_mock, patch( ) as create_dataset_mock, patch(
"python_otbr_api.OTBR.delete_active_dataset"
) as delete_dataset_mock, patch(
"python_otbr_api.OTBR.set_enabled" "python_otbr_api.OTBR.set_enabled"
) as set_enabled_mock, patch( ) as set_enabled_mock, patch(
"python_otbr_api.OTBR.get_active_dataset_tlvs", return_value=DATASET_CH16 "python_otbr_api.OTBR.get_active_dataset_tlvs", return_value=DATASET_CH16
@ -99,6 +101,7 @@ async def test_create_network(
create_dataset_mock.assert_called_once_with( create_dataset_mock.assert_called_once_with(
python_otbr_api.models.ActiveDataSet(channel=15, network_name="home-assistant") python_otbr_api.models.ActiveDataSet(channel=15, network_name="home-assistant")
) )
delete_dataset_mock.assert_called_once_with()
assert len(set_enabled_mock.mock_calls) == 2 assert len(set_enabled_mock.mock_calls) == 2
assert set_enabled_mock.mock_calls[0][1][0] is False assert set_enabled_mock.mock_calls[0][1][0] is False
assert set_enabled_mock.mock_calls[1][1][0] is True assert set_enabled_mock.mock_calls[1][1][0] is True
@ -151,7 +154,7 @@ async def test_create_network_fails_2(
), patch( ), patch(
"python_otbr_api.OTBR.create_active_dataset", "python_otbr_api.OTBR.create_active_dataset",
side_effect=python_otbr_api.OTBRError, side_effect=python_otbr_api.OTBRError,
): ), patch("python_otbr_api.OTBR.delete_active_dataset"):
await websocket_client.send_json_auto_id({"type": "otbr/create_network"}) await websocket_client.send_json_auto_id({"type": "otbr/create_network"})
msg = await websocket_client.receive_json() msg = await websocket_client.receive_json()
@ -171,6 +174,8 @@ async def test_create_network_fails_3(
side_effect=[None, python_otbr_api.OTBRError], side_effect=[None, python_otbr_api.OTBRError],
), patch( ), patch(
"python_otbr_api.OTBR.create_active_dataset", "python_otbr_api.OTBR.create_active_dataset",
), patch(
"python_otbr_api.OTBR.delete_active_dataset"
): ):
await websocket_client.send_json_auto_id({"type": "otbr/create_network"}) await websocket_client.send_json_auto_id({"type": "otbr/create_network"})
msg = await websocket_client.receive_json() msg = await websocket_client.receive_json()
@ -191,6 +196,8 @@ async def test_create_network_fails_4(
), patch( ), patch(
"python_otbr_api.OTBR.get_active_dataset_tlvs", "python_otbr_api.OTBR.get_active_dataset_tlvs",
side_effect=python_otbr_api.OTBRError, side_effect=python_otbr_api.OTBRError,
), patch(
"python_otbr_api.OTBR.delete_active_dataset"
): ):
await websocket_client.send_json_auto_id({"type": "otbr/create_network"}) await websocket_client.send_json_auto_id({"type": "otbr/create_network"})
msg = await websocket_client.receive_json() msg = await websocket_client.receive_json()
@ -208,7 +215,9 @@ async def test_create_network_fails_5(
"""Test create network.""" """Test create network."""
with patch("python_otbr_api.OTBR.set_enabled"), patch( with patch("python_otbr_api.OTBR.set_enabled"), patch(
"python_otbr_api.OTBR.create_active_dataset" "python_otbr_api.OTBR.create_active_dataset"
), patch("python_otbr_api.OTBR.get_active_dataset_tlvs", return_value=None): ), patch("python_otbr_api.OTBR.get_active_dataset_tlvs", return_value=None), patch(
"python_otbr_api.OTBR.delete_active_dataset"
):
await websocket_client.send_json_auto_id({"type": "otbr/create_network"}) await websocket_client.send_json_auto_id({"type": "otbr/create_network"})
msg = await websocket_client.receive_json() msg = await websocket_client.receive_json()
@ -216,6 +225,26 @@ async def test_create_network_fails_5(
assert msg["error"]["code"] == "get_active_dataset_tlvs_empty" assert msg["error"]["code"] == "get_active_dataset_tlvs_empty"
async def test_create_network_fails_6(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
otbr_config_entry,
websocket_client,
) -> None:
"""Test create network."""
with patch("python_otbr_api.OTBR.set_enabled"), patch(
"python_otbr_api.OTBR.create_active_dataset"
), patch("python_otbr_api.OTBR.get_active_dataset_tlvs", return_value=None), patch(
"python_otbr_api.OTBR.delete_active_dataset",
side_effect=python_otbr_api.OTBRError,
):
await websocket_client.send_json_auto_id({"type": "otbr/create_network"})
msg = await websocket_client.receive_json()
assert not msg["success"]
assert msg["error"]["code"] == "delete_active_dataset_failed"
async def test_set_network( async def test_set_network(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,

View File

@ -864,7 +864,7 @@ async def test_media_browse_local_source(
assert msg["result"]["children"][0]["title"] == "Apps" assert msg["result"]["children"][0]["title"] == "Apps"
assert msg["result"]["children"][0]["media_content_type"] == MediaType.APPS assert msg["result"]["children"][0]["media_content_type"] == MediaType.APPS
assert msg["result"]["children"][1]["title"] == "Local Media" assert msg["result"]["children"][1]["title"] == "My media"
assert msg["result"]["children"][1]["media_class"] == MediaClass.DIRECTORY assert msg["result"]["children"][1]["media_class"] == MediaClass.DIRECTORY
assert msg["result"]["children"][1]["media_content_type"] is None assert msg["result"]["children"][1]["media_content_type"] is None
assert ( assert (
@ -892,7 +892,7 @@ async def test_media_browse_local_source(
assert msg["success"] assert msg["success"]
assert msg["result"] assert msg["result"]
assert msg["result"]["title"] == "Local Media" assert msg["result"]["title"] == "My media"
assert msg["result"]["media_class"] == MediaClass.DIRECTORY assert msg["result"]["media_class"] == MediaClass.DIRECTORY
assert msg["result"]["media_content_type"] is None assert msg["result"]["media_content_type"] is None
assert len(msg["result"]["children"]) == 2 assert len(msg["result"]["children"]) == 2