mirror of
https://github.com/home-assistant/core.git
synced 2026-06-27 17:15:23 +02:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 486385e308 | |||
| 11113ce1f4 | |||
| 31dc1f9519 | |||
| 926900d0ca | |||
| 06b429d0b7 | |||
| 9fb2d14fdb | |||
| 0e6279e273 | |||
| 02ddd4c3f4 | |||
| c147321aba | |||
| f0e75ebb83 | |||
| 7ae85d8d97 | |||
| 43802961e9 | |||
| 97844d7ee0 | |||
| e8e5eedd7e | |||
| bbf7a60a9e | |||
| fac6e8feb6 | |||
| 86d282ac2a |
Generated
-1
@@ -1986,7 +1986,6 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/waterfurnace/ @sdague @masterkoppa
|
||||
/homeassistant/components/watergate/ @adam-the-hero
|
||||
/tests/components/watergate/ @adam-the-hero
|
||||
/homeassistant/components/watson_tts/ @rutkai
|
||||
/homeassistant/components/watts/ @theobld-ww @devender-verma-ww @ssi-spyro
|
||||
/tests/components/watts/ @theobld-ww @devender-verma-ww @ssi-spyro
|
||||
/homeassistant/components/watttime/ @bachya
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
"""The BlinkStick integration."""
|
||||
@@ -1,87 +0,0 @@
|
||||
"""Support for BlinkStick lights."""
|
||||
|
||||
# mypy: ignore-errors
|
||||
from typing import Any
|
||||
|
||||
# from blinkstick import blinkstick
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
ATTR_HS_COLOR,
|
||||
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA,
|
||||
ColorMode,
|
||||
LightEntity,
|
||||
)
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.util import color as color_util
|
||||
|
||||
CONF_SERIAL = "serial"
|
||||
|
||||
DEFAULT_NAME = "Blinkstick"
|
||||
|
||||
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_SERIAL): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up BlinkStick device specified by serial number."""
|
||||
|
||||
name = config[CONF_NAME]
|
||||
serial = config[CONF_SERIAL]
|
||||
|
||||
stick = blinkstick.find_by_serial(serial)
|
||||
|
||||
add_entities([BlinkStickLight(stick, name)], True)
|
||||
|
||||
|
||||
class BlinkStickLight(LightEntity):
|
||||
"""Representation of a BlinkStick light."""
|
||||
|
||||
_attr_color_mode = ColorMode.HS
|
||||
_attr_supported_color_modes = {ColorMode.HS}
|
||||
|
||||
def __init__(self, stick, name):
|
||||
"""Initialize the light."""
|
||||
self._stick = stick
|
||||
self._attr_name = name
|
||||
|
||||
def update(self) -> None:
|
||||
"""Read back the device state."""
|
||||
rgb_color = self._stick.get_color()
|
||||
hsv = color_util.color_RGB_to_hsv(*rgb_color)
|
||||
self._attr_hs_color = hsv[:2]
|
||||
self._attr_brightness = int(hsv[2])
|
||||
self._attr_is_on = self.brightness is not None and self.brightness > 0
|
||||
|
||||
def turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the device on."""
|
||||
if ATTR_HS_COLOR in kwargs:
|
||||
self._attr_hs_color = kwargs[ATTR_HS_COLOR]
|
||||
|
||||
brightness: int = kwargs.get(ATTR_BRIGHTNESS, 255)
|
||||
self._attr_brightness = brightness
|
||||
self._attr_is_on = bool(brightness)
|
||||
|
||||
assert self.hs_color
|
||||
rgb_color = color_util.color_hsv_to_RGB(
|
||||
self.hs_color[0], self.hs_color[1], brightness / 255 * 100
|
||||
)
|
||||
self._stick.set_color(red=rgb_color[0], green=rgb_color[1], blue=rgb_color[2])
|
||||
|
||||
def turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the device off."""
|
||||
self._stick.turn_off()
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"domain": "blinksticklight",
|
||||
"name": "BlinkStick",
|
||||
"codeowners": [],
|
||||
"disabled": "This integration is disabled because it uses non-open source code to operate.",
|
||||
"documentation": "https://www.home-assistant.io/integrations/blinksticklight",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["blinkstick"],
|
||||
"quality_scale": "legacy",
|
||||
"requirements": ["BlinkStick==1.2.0"]
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
extend = "../../../pyproject.toml"
|
||||
|
||||
lint.extend-ignore = [
|
||||
"F821"
|
||||
]
|
||||
@@ -8,7 +8,7 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["bsblan"],
|
||||
"quality_scale": "silver",
|
||||
"requirements": ["python-bsblan==6.1.3"],
|
||||
"requirements": ["python-bsblan==6.1.4"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"name": "bsb-lan*",
|
||||
|
||||
@@ -12,11 +12,10 @@ from homeassistant.components.sensor import (
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
PERCENTAGE,
|
||||
EntityCategory,
|
||||
UnitOfEnergy,
|
||||
UnitOfPower,
|
||||
UnitOfRatio,
|
||||
UnitOfTemperature,
|
||||
UnitOfVolume,
|
||||
)
|
||||
@@ -52,7 +51,7 @@ async def async_setup_entry(
|
||||
12,
|
||||
SensorDeviceClass.BATTERY,
|
||||
SensorStateClass.MEASUREMENT,
|
||||
PERCENTAGE,
|
||||
UnitOfRatio.PERCENTAGE,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_name="Battery",
|
||||
),
|
||||
@@ -63,7 +62,7 @@ async def async_setup_entry(
|
||||
54,
|
||||
SensorDeviceClass.HUMIDITY,
|
||||
SensorStateClass.MEASUREMENT,
|
||||
PERCENTAGE,
|
||||
UnitOfRatio.PERCENTAGE,
|
||||
),
|
||||
DemoSensor(
|
||||
"sensor_3",
|
||||
@@ -72,7 +71,7 @@ async def async_setup_entry(
|
||||
54,
|
||||
SensorDeviceClass.CO,
|
||||
SensorStateClass.MEASUREMENT,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
UnitOfRatio.PARTS_PER_MILLION,
|
||||
),
|
||||
DemoSensor(
|
||||
"sensor_4",
|
||||
@@ -81,7 +80,7 @@ async def async_setup_entry(
|
||||
54,
|
||||
SensorDeviceClass.CO2,
|
||||
SensorStateClass.MEASUREMENT,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
UnitOfRatio.PARTS_PER_MILLION,
|
||||
),
|
||||
DemoSensor(
|
||||
"battery_4",
|
||||
@@ -90,7 +89,7 @@ async def async_setup_entry(
|
||||
99,
|
||||
SensorDeviceClass.BATTERY,
|
||||
SensorStateClass.MEASUREMENT,
|
||||
PERCENTAGE,
|
||||
UnitOfRatio.PERCENTAGE,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_name="Battery",
|
||||
),
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["aioimmich"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["aioimmich==0.15.0"]
|
||||
"requirements": ["aioimmich==0.15.1"]
|
||||
}
|
||||
|
||||
@@ -31,7 +31,9 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||
TextSelectorConfig(type=TextSelectorType.EMAIL, autocomplete="username")
|
||||
),
|
||||
vol.Required(CONF_PASSWORD): TextSelector(
|
||||
TextSelectorConfig(type=TextSelectorType.PASSWORD)
|
||||
TextSelectorConfig(
|
||||
type=TextSelectorType.PASSWORD, autocomplete="current-password"
|
||||
)
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -114,20 +114,26 @@ class MieleDataUpdateCoordinator(DataUpdateCoordinator[MieleCoordinatorData]):
|
||||
|
||||
async def callback_update_data(self, devices_json: dict[str, dict]) -> None:
|
||||
"""Handle data update from the API."""
|
||||
devices = {
|
||||
updated_devices = {
|
||||
device_id: MieleDevice(device) for device_id, device in devices_json.items()
|
||||
}
|
||||
self.async_set_updated_data(
|
||||
MieleCoordinatorData(devices=devices, actions=self.data.actions)
|
||||
MieleCoordinatorData(
|
||||
devices={**self.data.devices, **updated_devices},
|
||||
actions=self.data.actions,
|
||||
)
|
||||
)
|
||||
|
||||
async def callback_update_actions(self, actions_json: dict[str, dict]) -> None:
|
||||
"""Handle data update from the API."""
|
||||
actions = {
|
||||
updated_actions = {
|
||||
device_id: MieleAction(action) for device_id, action in actions_json.items()
|
||||
}
|
||||
self.async_set_updated_data(
|
||||
MieleCoordinatorData(devices=self.data.devices, actions=actions)
|
||||
MieleCoordinatorData(
|
||||
devices=self.data.devices,
|
||||
actions={**self.data.actions, **updated_actions},
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from dataclasses import dataclass
|
||||
import logging
|
||||
|
||||
from pymonoprice import Monoprice, get_monoprice
|
||||
from serial import SerialException
|
||||
from serialx import SerialException
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PORT, Platform
|
||||
|
||||
@@ -4,7 +4,7 @@ import logging
|
||||
from typing import Any, override
|
||||
|
||||
from pymonoprice import get_monoprice
|
||||
from serial import SerialException
|
||||
from serialx import SerialException
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import (
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["pymonoprice"],
|
||||
"requirements": ["pymonoprice==0.5"]
|
||||
"requirements": ["pymonoprice==0.6.1"]
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
from serial import SerialException
|
||||
from serialx import SerialException
|
||||
|
||||
from homeassistant import core
|
||||
from homeassistant.components.media_player import (
|
||||
|
||||
@@ -25,6 +25,8 @@ from .const import (
|
||||
DOMAIN,
|
||||
OVERRIDE_TYPE_CONSTANT,
|
||||
OVERRIDE_TYPE_NOW,
|
||||
SERIAL_LENGTH,
|
||||
SERIAL_PREFIX_LENGTH,
|
||||
)
|
||||
|
||||
DATA_NOBO_HUB_IMPL = "nobo_hub_flow_implementation"
|
||||
@@ -49,7 +51,20 @@ class NoboHubConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle the initial step."""
|
||||
if self._discovered_hubs is None:
|
||||
self._discovered_hubs = dict(await nobo.async_discover_hubs())
|
||||
# Wait 5s — real-world gaps up to ~4s have been observed.
|
||||
discovered = dict(await nobo.async_discover_hubs(autodiscover_wait=5.0))
|
||||
# Hide hubs that already have a config entry. Include matching on IP
|
||||
# as serial prefix is not unique.
|
||||
configured = {
|
||||
(entry.data[CONF_IP_ADDRESS], entry.unique_id[:SERIAL_PREFIX_LENGTH])
|
||||
for entry in self._async_current_entries(include_ignore=False)
|
||||
if entry.unique_id
|
||||
}
|
||||
self._discovered_hubs = {
|
||||
ip: prefix
|
||||
for ip, prefix in discovered.items()
|
||||
if (ip, prefix) not in configured
|
||||
}
|
||||
|
||||
if not self._discovered_hubs:
|
||||
# No hubs auto discovered
|
||||
@@ -227,7 +242,7 @@ class NoboHubConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
)
|
||||
|
||||
async def _test_connection(self, serial: str, ip_address: str) -> str:
|
||||
if not len(serial) == 12 or not serial.isdigit():
|
||||
if len(serial) != SERIAL_LENGTH or not serial.isdigit():
|
||||
raise NoboHubConnectError("invalid_serial")
|
||||
try:
|
||||
socket.inet_aton(ip_address)
|
||||
|
||||
@@ -9,6 +9,11 @@ CONF_OVERRIDE_TYPE = "override_type"
|
||||
OVERRIDE_TYPE_CONSTANT = "constant"
|
||||
OVERRIDE_TYPE_NOW = "now"
|
||||
|
||||
# Hub serial: 9-digit batch prefix + 3-digit per-hub suffix. Discovery
|
||||
# broadcasts only the prefix; the user supplies the suffix.
|
||||
SERIAL_PREFIX_LENGTH = 9
|
||||
SERIAL_LENGTH = SERIAL_PREFIX_LENGTH + 3
|
||||
|
||||
NOBO_MANUFACTURER = "Glen Dimplex Nordic AS"
|
||||
ATTR_HARDWARE_VERSION: Final = "hardware_version"
|
||||
ATTR_SOFTWARE_VERSION: Final = "software_version"
|
||||
|
||||
@@ -64,11 +64,7 @@ rules:
|
||||
docs-use-cases: todo
|
||||
dynamic-devices: todo
|
||||
entity-category: todo
|
||||
entity-device-class:
|
||||
status: todo
|
||||
comment: >
|
||||
Custom device class on global override select being dropped in
|
||||
PR #170135.
|
||||
entity-device-class: done
|
||||
entity-disabled-by-default: todo
|
||||
entity-translations: todo
|
||||
exception-translations: todo
|
||||
|
||||
@@ -53,7 +53,6 @@ class NoboGlobalSelector(NoboBaseEntity, SelectEntity):
|
||||
"""Global override selector for Nobø Ecohub."""
|
||||
|
||||
_attr_translation_key = "global_override"
|
||||
_attr_device_class = "nobo_hub__override"
|
||||
_modes = {
|
||||
nobo.API.OVERRIDE_MODE_NORMAL: "none",
|
||||
nobo.API.OVERRIDE_MODE_AWAY: "away",
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable
|
||||
import contextlib
|
||||
from datetime import timedelta
|
||||
from enum import IntEnum
|
||||
import io
|
||||
@@ -188,9 +187,7 @@ async def _async_upload_image(call: ServiceCall) -> None:
|
||||
current = asyncio.current_task()
|
||||
if (prev := entry.runtime_data.upload_task) is not None and not prev.done():
|
||||
prev.cancel()
|
||||
# pylint: disable-next=home-assistant-action-swallowed-exception
|
||||
with contextlib.suppress(asyncio.CancelledError):
|
||||
await prev
|
||||
await asyncio.wait({prev})
|
||||
entry.runtime_data.upload_task = current
|
||||
|
||||
try:
|
||||
|
||||
@@ -25,6 +25,9 @@ from homeassistant.helpers.selector import (
|
||||
SelectSelector,
|
||||
SelectSelectorConfig,
|
||||
SelectSelectorMode,
|
||||
TextSelector,
|
||||
TextSelectorConfig,
|
||||
TextSelectorType,
|
||||
)
|
||||
|
||||
from .common import sanitize_config_entry
|
||||
@@ -58,7 +61,9 @@ BASE_SCHEMA = vol.Schema(
|
||||
)
|
||||
),
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Required(CONF_USERNAME): cv.string,
|
||||
vol.Required(CONF_USERNAME): TextSelector(
|
||||
TextSelectorConfig(type=TextSelectorType.TEXT, autocomplete="username")
|
||||
),
|
||||
vol.Required(CONF_PORT, default=DEFAULT_PORT): cv.port,
|
||||
vol.Required(CONF_TOKEN, default=False): cv.boolean,
|
||||
vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean,
|
||||
@@ -67,7 +72,12 @@ BASE_SCHEMA = vol.Schema(
|
||||
|
||||
PASSWORD_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_PASSWORD): cv.string,
|
||||
vol.Required(CONF_PASSWORD): TextSelector(
|
||||
TextSelectorConfig(
|
||||
type=TextSelectorType.PASSWORD,
|
||||
autocomplete="current-password",
|
||||
)
|
||||
),
|
||||
}
|
||||
)
|
||||
TOKEN_SCHEMA = vol.Schema(
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"quality_scale": "silver",
|
||||
"requirements": ["pysmlight==0.4.0"],
|
||||
"requirements": ["pysmlight==0.5.0"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_slzb-06._tcp.local."
|
||||
|
||||
@@ -23,6 +23,7 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
PERCENTAGE,
|
||||
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
|
||||
UnitOfApparentPower,
|
||||
UnitOfElectricCurrent,
|
||||
UnitOfElectricPotential,
|
||||
UnitOfEnergy,
|
||||
@@ -348,6 +349,13 @@ SENSOR_DESCRIPTIONS = {
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
Keys.OUTPUT_CURRENT: VictronBLESensorEntityDescription(
|
||||
key=Keys.OUTPUT_CURRENT,
|
||||
translation_key=Keys.OUTPUT_CURRENT,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
Keys.REMAINING_MINUTES: VictronBLESensorEntityDescription(
|
||||
key=Keys.REMAINING_MINUTES,
|
||||
translation_key=Keys.REMAINING_MINUTES,
|
||||
@@ -412,6 +420,27 @@ SENSOR_DESCRIPTIONS = {
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
Keys.AC_APPARENT_POWER: VictronBLESensorEntityDescription(
|
||||
key=Keys.AC_APPARENT_POWER,
|
||||
translation_key=Keys.AC_APPARENT_POWER,
|
||||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
Keys.AC_VOLTAGE: VictronBLESensorEntityDescription(
|
||||
key=Keys.AC_VOLTAGE,
|
||||
translation_key=Keys.AC_VOLTAGE,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
Keys.INPUT_CURRENT: VictronBLESensorEntityDescription(
|
||||
key=Keys.INPUT_CURRENT,
|
||||
translation_key=Keys.INPUT_CURRENT,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
Keys.OUTPUT_VOLTAGE_1: VictronBLESensorEntityDescription(
|
||||
key=Keys.OUTPUT_VOLTAGE_1,
|
||||
translation_key="output_phase_voltage",
|
||||
|
||||
@@ -50,6 +50,9 @@
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"ac_apparent_power": {
|
||||
"name": "AC apparent power"
|
||||
},
|
||||
"ac_current": {
|
||||
"name": "AC current"
|
||||
},
|
||||
@@ -67,6 +70,9 @@
|
||||
"ac_out_power": {
|
||||
"name": "AC-out power"
|
||||
},
|
||||
"ac_voltage": {
|
||||
"name": "AC voltage"
|
||||
},
|
||||
"alarm": {
|
||||
"name": "Alarm",
|
||||
"state": {
|
||||
@@ -210,6 +216,9 @@
|
||||
"external_device_load": {
|
||||
"name": "External device load"
|
||||
},
|
||||
"input_current": {
|
||||
"name": "Input current"
|
||||
},
|
||||
"input_voltage": {
|
||||
"name": "Input voltage"
|
||||
},
|
||||
@@ -254,6 +263,9 @@
|
||||
"switched_off_switch": "Switched off by switch"
|
||||
}
|
||||
},
|
||||
"output_current": {
|
||||
"name": "Output current"
|
||||
},
|
||||
"output_phase_current": {
|
||||
"name": "Output phase {phase} current"
|
||||
},
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
"""Support for IBM Watson TTS integration."""
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"domain": "watson_tts",
|
||||
"name": "IBM Watson TTS",
|
||||
"codeowners": ["@rutkai"],
|
||||
"disabled": "Dependencies not compatible with the new pip resolver",
|
||||
"documentation": "https://www.home-assistant.io/integrations/watson_tts",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["ibm_cloud_sdk_core", "ibm_watson"],
|
||||
"quality_scale": "legacy",
|
||||
"requirements": ["ibm-watson==5.2.2"]
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
"""Support for IBM Watson TTS integration."""
|
||||
|
||||
import logging
|
||||
from typing import Any, override
|
||||
|
||||
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
|
||||
from ibm_watson import TextToSpeechV1
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.tts import (
|
||||
PLATFORM_SCHEMA as TTS_PLATFORM_SCHEMA,
|
||||
Provider,
|
||||
TtsAudioType,
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_URL = "watson_url"
|
||||
CONF_APIKEY = "watson_apikey"
|
||||
|
||||
DEFAULT_URL = "https://api.us-south.text-to-speech.watson.cloud.ibm.com"
|
||||
|
||||
CONF_VOICE = "voice"
|
||||
CONF_OUTPUT_FORMAT = "output_format"
|
||||
CONF_TEXT_TYPE = "text"
|
||||
|
||||
# List from https://tinyurl.com/watson-tts-docs
|
||||
SUPPORTED_VOICES = [
|
||||
"de-DE_BirgitV2Voice",
|
||||
"de-DE_BirgitV3Voice",
|
||||
"de-DE_BirgitVoice",
|
||||
"de-DE_DieterV2Voice",
|
||||
"de-DE_DieterV3Voice",
|
||||
"de-DE_DieterVoice",
|
||||
"de-DE_ErikaV3Voice",
|
||||
"en-AU_HeidiExpressive",
|
||||
"en-AU_JackExpressive",
|
||||
"en-GB_CharlotteV3Voice",
|
||||
"en-GB_JamesV3Voice",
|
||||
"en-GB_KateV3Voice",
|
||||
"en-GB_KateVoice",
|
||||
"en-US_AllisonExpressive",
|
||||
"en-US_AllisonV2Voice",
|
||||
"en-US_AllisonV3Voice",
|
||||
"en-US_AllisonVoice",
|
||||
"en-US_EmilyV3Voice",
|
||||
"en-US_EmmaExpressive",
|
||||
"en-US_HenryV3Voice",
|
||||
"en-US_KevinV3Voice",
|
||||
"en-US_LisaExpressive",
|
||||
"en-US_LisaV2Voice",
|
||||
"en-US_LisaV3Voice",
|
||||
"en-US_LisaVoice",
|
||||
"en-US_MichaelExpressive",
|
||||
"en-US_MichaelV2Voice",
|
||||
"en-US_MichaelV3Voice",
|
||||
"en-US_MichaelVoice",
|
||||
"en-US_OliviaV3Voice",
|
||||
"es-ES_EnriqueV3Voice",
|
||||
"es-ES_EnriqueVoice",
|
||||
"es-ES_LauraV3Voice",
|
||||
"es-ES_LauraVoice",
|
||||
"es-LA_SofiaV3Voice",
|
||||
"es-LA_SofiaVoice",
|
||||
"es-US_SofiaV3Voice",
|
||||
"es-US_SofiaVoice",
|
||||
"fr-CA_LouiseV3Voice",
|
||||
"fr-FR_NicolasV3Voice",
|
||||
"fr-FR_ReneeV3Voice",
|
||||
"fr-FR_ReneeVoice",
|
||||
"it-IT_FrancescaV2Voice",
|
||||
"it-IT_FrancescaV3Voice",
|
||||
"it-IT_FrancescaVoice",
|
||||
"ja-JP_EmiV3Voice",
|
||||
"ja-JP_EmiVoice",
|
||||
"ko-KR_JinV3Voice",
|
||||
"nl-NL_MerelV3Voice",
|
||||
"pt-BR_IsabelaV3Voice",
|
||||
"pt-BR_IsabelaVoice",
|
||||
]
|
||||
|
||||
DEPRECATED_VOICES = [
|
||||
"de-DE_BirgitVoice",
|
||||
"de-DE_DieterVoice",
|
||||
"en-US_AllisonVoice",
|
||||
"en-US_LisaVoice",
|
||||
"en-US_MichaelVoice",
|
||||
"es-ES_EnriqueVoice",
|
||||
"es-ES_LauraVoice",
|
||||
"es-LA_SofiaVoice",
|
||||
"es-US_SofiaVoice",
|
||||
"fr-FR_ReneeVoice",
|
||||
"it-IT_FrancescaVoice",
|
||||
"ja-JP_EmiVoice",
|
||||
"pt-BR_IsabelaVoice",
|
||||
]
|
||||
|
||||
SUPPORTED_OUTPUT_FORMATS = [
|
||||
"audio/flac",
|
||||
"audio/mp3",
|
||||
"audio/mpeg",
|
||||
"audio/ogg",
|
||||
"audio/ogg;codecs=opus",
|
||||
"audio/ogg;codecs=vorbis",
|
||||
"audio/wav",
|
||||
]
|
||||
|
||||
CONTENT_TYPE_EXTENSIONS = {
|
||||
"audio/flac": "flac",
|
||||
"audio/mp3": "mp3",
|
||||
"audio/mpeg": "mp3",
|
||||
"audio/ogg": "ogg",
|
||||
"audio/ogg;codecs=opus": "ogg",
|
||||
"audio/ogg;codecs=vorbis": "ogg",
|
||||
"audio/wav": "wav",
|
||||
}
|
||||
|
||||
DEFAULT_VOICE = "en-US_AllisonV3Voice"
|
||||
DEFAULT_OUTPUT_FORMAT = "audio/mp3"
|
||||
|
||||
PLATFORM_SCHEMA = TTS_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_URL, default=DEFAULT_URL): cv.string,
|
||||
vol.Required(CONF_APIKEY): cv.string,
|
||||
vol.Optional(CONF_VOICE, default=DEFAULT_VOICE): vol.In(SUPPORTED_VOICES),
|
||||
vol.Optional(CONF_OUTPUT_FORMAT, default=DEFAULT_OUTPUT_FORMAT): vol.In(
|
||||
SUPPORTED_OUTPUT_FORMATS
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def get_engine(hass, config, discovery_info=None):
|
||||
"""Set up IBM Watson TTS component."""
|
||||
|
||||
authenticator = IAMAuthenticator(config[CONF_APIKEY])
|
||||
service = TextToSpeechV1(authenticator)
|
||||
service.set_service_url(config[CONF_URL])
|
||||
|
||||
supported_languages = list({s[:5] for s in SUPPORTED_VOICES})
|
||||
default_voice = config[CONF_VOICE]
|
||||
output_format = config[CONF_OUTPUT_FORMAT]
|
||||
service.set_default_headers({"x-watson-learning-opt-out": "true"})
|
||||
|
||||
if default_voice in DEPRECATED_VOICES:
|
||||
_LOGGER.warning(
|
||||
"Watson TTS voice %s is deprecated, it may be removed in the future",
|
||||
default_voice,
|
||||
)
|
||||
|
||||
return WatsonTTSProvider(service, supported_languages, default_voice, output_format)
|
||||
|
||||
|
||||
class WatsonTTSProvider(Provider):
|
||||
"""IBM Watson TTS api provider."""
|
||||
|
||||
def __init__(self, service, supported_languages, default_voice, output_format):
|
||||
"""Initialize Watson TTS provider."""
|
||||
self.service = service
|
||||
self.supported_langs = supported_languages
|
||||
self.default_lang = default_voice[:5]
|
||||
self.default_voice = default_voice
|
||||
self.output_format = output_format
|
||||
self.name = "Watson TTS"
|
||||
|
||||
@property
|
||||
@override
|
||||
def supported_languages(self) -> list[str]:
|
||||
"""Return a list of supported languages."""
|
||||
return self.supported_langs
|
||||
|
||||
@property
|
||||
@override
|
||||
def default_language(self) -> str:
|
||||
"""Return the default language."""
|
||||
return self.default_lang
|
||||
|
||||
@property
|
||||
@override
|
||||
def default_options(self) -> dict[str, Any]:
|
||||
"""Return dict include default options."""
|
||||
return {CONF_VOICE: self.default_voice}
|
||||
|
||||
@property
|
||||
@override
|
||||
def supported_options(self) -> list[str]:
|
||||
"""Return a list of supported options."""
|
||||
return [CONF_VOICE]
|
||||
|
||||
@override
|
||||
def get_tts_audio(
|
||||
self, message: str, language: str, options: dict[str, Any]
|
||||
) -> TtsAudioType:
|
||||
"""Request TTS file from Watson TTS."""
|
||||
response = self.service.synthesize(
|
||||
text=message, accept=self.output_format, voice=options[CONF_VOICE]
|
||||
).get_result()
|
||||
|
||||
return (CONTENT_TYPE_EXTENSIONS[self.output_format], response.content)
|
||||
@@ -764,12 +764,6 @@
|
||||
"config_flow": true,
|
||||
"iot_class": "cloud_polling"
|
||||
},
|
||||
"blinksticklight": {
|
||||
"name": "BlinkStick",
|
||||
"integration_type": "hub",
|
||||
"config_flow": false,
|
||||
"iot_class": "local_polling"
|
||||
},
|
||||
"bliss_automation": {
|
||||
"name": "Bliss Automation",
|
||||
"integration_type": "virtual",
|
||||
@@ -7868,12 +7862,6 @@
|
||||
"config_flow": true,
|
||||
"iot_class": "local_push"
|
||||
},
|
||||
"watson_tts": {
|
||||
"name": "IBM Watson TTS",
|
||||
"integration_type": "hub",
|
||||
"config_flow": false,
|
||||
"iot_class": "cloud_push"
|
||||
},
|
||||
"watts": {
|
||||
"name": "Watts Vision +",
|
||||
"integration_type": "hub",
|
||||
|
||||
Generated
+4
-4
@@ -303,7 +303,7 @@ aiohue==4.8.1
|
||||
aioimaplib==2.0.1
|
||||
|
||||
# homeassistant.components.immich
|
||||
aioimmich==0.15.0
|
||||
aioimmich==0.15.1
|
||||
|
||||
# homeassistant.components.apache_kafka
|
||||
aiokafka==0.10.0
|
||||
@@ -2358,7 +2358,7 @@ pymochad==0.2.0
|
||||
pymodbus==3.13.1
|
||||
|
||||
# homeassistant.components.monoprice
|
||||
pymonoprice==0.5
|
||||
pymonoprice==0.6.1
|
||||
|
||||
# homeassistant.components.mysensors
|
||||
pymysensors==0.26.0
|
||||
@@ -2564,7 +2564,7 @@ pysmhi==2.0.0
|
||||
pysml==0.1.8
|
||||
|
||||
# homeassistant.components.smlight
|
||||
pysmlight==0.4.0
|
||||
pysmlight==0.5.0
|
||||
|
||||
# homeassistant.components.snmp
|
||||
pysnmp==7.1.27
|
||||
@@ -2615,7 +2615,7 @@ python-awair==0.2.5
|
||||
python-blockchain-api==0.0.2
|
||||
|
||||
# homeassistant.components.bsblan
|
||||
python-bsblan==6.1.3
|
||||
python-bsblan==6.1.4
|
||||
|
||||
# homeassistant.components.citybikes
|
||||
python-citybikes==0.3.3
|
||||
|
||||
@@ -27,7 +27,7 @@ pytest-asyncio==1.4.0
|
||||
pytest-aiohttp==1.1.1
|
||||
pytest-cov==7.1.0
|
||||
pytest-freezer==0.4.9
|
||||
pytest-github-actions-annotate-failures==0.4.1
|
||||
pytest-github-actions-annotate-failures==0.4.2
|
||||
pytest-socket==0.8.0
|
||||
pytest-sugar==1.1.1
|
||||
pytest-timeout==2.4.0
|
||||
|
||||
@@ -189,7 +189,6 @@ INTEGRATIONS_WITHOUT_QUALITY_SCALE_FILE = [
|
||||
"blackbird",
|
||||
"blebox",
|
||||
"blink",
|
||||
"blinksticklight",
|
||||
"blockchain",
|
||||
"blue_current",
|
||||
"bluemaestro",
|
||||
@@ -986,7 +985,6 @@ INTEGRATIONS_WITHOUT_QUALITY_SCALE_FILE = [
|
||||
"wake_on_lan",
|
||||
"wallbox",
|
||||
"waqi",
|
||||
"watson_tts",
|
||||
"watttime",
|
||||
"waze_travel_time",
|
||||
"weatherflow_cloud",
|
||||
@@ -1133,7 +1131,6 @@ INTEGRATIONS_WITHOUT_SCALE = [
|
||||
"blackbird",
|
||||
"blebox",
|
||||
"blink",
|
||||
"blinksticklight",
|
||||
"blockchain",
|
||||
"blue_current",
|
||||
"bluemaestro",
|
||||
@@ -1966,7 +1963,6 @@ INTEGRATIONS_WITHOUT_SCALE = [
|
||||
"wake_on_lan",
|
||||
"wallbox",
|
||||
"waqi",
|
||||
"watson_tts",
|
||||
"watttime",
|
||||
"waze_travel_time",
|
||||
"weatherflow",
|
||||
|
||||
@@ -29,12 +29,6 @@
|
||||
"system_production_phases": null,
|
||||
"ctmeters": {},
|
||||
"ctmeters_phases": {},
|
||||
"ctmeter_production": null,
|
||||
"ctmeter_consumption": null,
|
||||
"ctmeter_storage": null,
|
||||
"ctmeter_production_phases": null,
|
||||
"ctmeter_consumption_phases": null,
|
||||
"ctmeter_storage_phases": null,
|
||||
"dry_contact_status": {},
|
||||
"dry_contact_settings": {},
|
||||
"inverters": {
|
||||
|
||||
@@ -70,40 +70,6 @@
|
||||
}
|
||||
},
|
||||
"ctmeters_phases": {},
|
||||
"ctmeter_production": {
|
||||
"eid": "100000010",
|
||||
"timestamp": 1708006110,
|
||||
"energy_delivered": 11234,
|
||||
"energy_received": 12345,
|
||||
"active_power": 100,
|
||||
"power_factor": 0.11,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["production-imbalance", "power-on-unused-phase"]
|
||||
},
|
||||
"ctmeter_consumption": {
|
||||
"eid": "100000020",
|
||||
"timestamp": 1708006120,
|
||||
"energy_delivered": 21234,
|
||||
"energy_received": 22345,
|
||||
"active_power": 101,
|
||||
"power_factor": 0.21,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"ctmeter_storage": null,
|
||||
"ctmeter_production_phases": null,
|
||||
"ctmeter_consumption_phases": null,
|
||||
"ctmeter_storage_phases": null,
|
||||
"dry_contact_status": {},
|
||||
"dry_contact_settings": {},
|
||||
"inverters": {
|
||||
|
||||
@@ -215,132 +215,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ctmeter_production": {
|
||||
"eid": "100000010",
|
||||
"timestamp": 1708006110,
|
||||
"energy_delivered": 11234,
|
||||
"energy_received": 12345,
|
||||
"active_power": 100,
|
||||
"power_factor": 0.11,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["production-imbalance", "power-on-unused-phase"]
|
||||
},
|
||||
"ctmeter_consumption": {
|
||||
"eid": "100000020",
|
||||
"timestamp": 1708006120,
|
||||
"energy_delivered": 21234,
|
||||
"energy_received": 22345,
|
||||
"active_power": 101,
|
||||
"power_factor": 0.21,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"ctmeter_storage": null,
|
||||
"ctmeter_production_phases": {
|
||||
"L1": {
|
||||
"eid": "100000011",
|
||||
"timestamp": 1708006111,
|
||||
"energy_delivered": 112341,
|
||||
"energy_received": 123451,
|
||||
"active_power": 20,
|
||||
"power_factor": 0.12,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["production-imbalance"]
|
||||
},
|
||||
"L2": {
|
||||
"eid": "100000012",
|
||||
"timestamp": 1708006112,
|
||||
"energy_delivered": 112342,
|
||||
"energy_received": 123452,
|
||||
"active_power": 30,
|
||||
"power_factor": 0.13,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["power-on-unused-phase"]
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000013",
|
||||
"timestamp": 1708006113,
|
||||
"energy_delivered": 112343,
|
||||
"energy_received": 123453,
|
||||
"active_power": 50,
|
||||
"power_factor": 0.14,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"ctmeter_consumption_phases": {
|
||||
"L1": {
|
||||
"eid": "100000021",
|
||||
"timestamp": 1708006121,
|
||||
"energy_delivered": 212341,
|
||||
"energy_received": 223451,
|
||||
"active_power": 21,
|
||||
"power_factor": 0.22,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L2": {
|
||||
"eid": "100000022",
|
||||
"timestamp": 1708006122,
|
||||
"energy_delivered": 212342,
|
||||
"energy_received": 223452,
|
||||
"active_power": 31,
|
||||
"power_factor": 0.23,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000023",
|
||||
"timestamp": 1708006123,
|
||||
"energy_delivered": 212343,
|
||||
"energy_received": 223453,
|
||||
"active_power": 51,
|
||||
"power_factor": 0.24,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"ctmeter_storage_phases": null,
|
||||
"dry_contact_status": {},
|
||||
"dry_contact_settings": {},
|
||||
"inverters": {
|
||||
|
||||
@@ -203,132 +203,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ctmeter_production": {
|
||||
"eid": "100000010",
|
||||
"timestamp": 1708006110,
|
||||
"energy_delivered": 11234,
|
||||
"energy_received": 12345,
|
||||
"active_power": 100,
|
||||
"power_factor": 0.11,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["production-imbalance", "power-on-unused-phase"]
|
||||
},
|
||||
"ctmeter_consumption": {
|
||||
"eid": "100000020",
|
||||
"timestamp": 1708006120,
|
||||
"energy_delivered": 21234,
|
||||
"energy_received": 22345,
|
||||
"active_power": 101,
|
||||
"power_factor": 0.21,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"ctmeter_storage": null,
|
||||
"ctmeter_production_phases": {
|
||||
"L1": {
|
||||
"eid": "100000011",
|
||||
"timestamp": 1708006111,
|
||||
"energy_delivered": 112341,
|
||||
"energy_received": 123451,
|
||||
"active_power": 20,
|
||||
"power_factor": 0.12,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["production-imbalance"]
|
||||
},
|
||||
"L2": {
|
||||
"eid": "100000012",
|
||||
"timestamp": 1708006112,
|
||||
"energy_delivered": 112342,
|
||||
"energy_received": 123452,
|
||||
"active_power": 30,
|
||||
"power_factor": 0.13,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["power-on-unused-phase"]
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000013",
|
||||
"timestamp": 1708006113,
|
||||
"energy_delivered": 112343,
|
||||
"energy_received": 123453,
|
||||
"active_power": 50,
|
||||
"power_factor": 0.14,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"ctmeter_consumption_phases": {
|
||||
"L1": {
|
||||
"eid": "100000021",
|
||||
"timestamp": 1708006121,
|
||||
"energy_delivered": 212341,
|
||||
"energy_received": 223451,
|
||||
"active_power": 21,
|
||||
"power_factor": 0.22,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L2": {
|
||||
"eid": "100000022",
|
||||
"timestamp": 1708006122,
|
||||
"energy_delivered": 212342,
|
||||
"energy_received": 223452,
|
||||
"active_power": 31,
|
||||
"power_factor": 0.23,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000023",
|
||||
"timestamp": 1708006123,
|
||||
"energy_delivered": 212343,
|
||||
"energy_received": 223453,
|
||||
"active_power": 51,
|
||||
"power_factor": 0.24,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"ctmeter_storage_phases": null,
|
||||
"dry_contact_status": {},
|
||||
"dry_contact_settings": {},
|
||||
"inverters": {
|
||||
|
||||
@@ -589,192 +589,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ctmeter_production": {
|
||||
"eid": "100000010",
|
||||
"timestamp": 1708006110,
|
||||
"energy_delivered": 11234,
|
||||
"energy_received": 12345,
|
||||
"active_power": 100,
|
||||
"power_factor": 0.11,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["production-imbalance", "power-on-unused-phase"]
|
||||
},
|
||||
"ctmeter_consumption": {
|
||||
"eid": "100000020",
|
||||
"timestamp": 1708006120,
|
||||
"energy_delivered": 21234,
|
||||
"energy_received": 22345,
|
||||
"active_power": 101,
|
||||
"power_factor": 0.21,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"ctmeter_storage": {
|
||||
"eid": "100000030",
|
||||
"timestamp": 1708006120,
|
||||
"energy_delivered": 31234,
|
||||
"energy_received": 32345,
|
||||
"active_power": 103,
|
||||
"power_factor": 0.23,
|
||||
"voltage": 113,
|
||||
"current": 0.4,
|
||||
"frequency": 50.3,
|
||||
"state": "enabled",
|
||||
"measurement_type": "storage",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"ctmeter_production_phases": {
|
||||
"L1": {
|
||||
"eid": "100000011",
|
||||
"timestamp": 1708006111,
|
||||
"energy_delivered": 112341,
|
||||
"energy_received": 123451,
|
||||
"active_power": 20,
|
||||
"power_factor": 0.12,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["production-imbalance"]
|
||||
},
|
||||
"L2": {
|
||||
"eid": "100000012",
|
||||
"timestamp": 1708006112,
|
||||
"energy_delivered": 112342,
|
||||
"energy_received": 123452,
|
||||
"active_power": 30,
|
||||
"power_factor": 0.13,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["power-on-unused-phase"]
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000013",
|
||||
"timestamp": 1708006113,
|
||||
"energy_delivered": 112343,
|
||||
"energy_received": 123453,
|
||||
"active_power": 50,
|
||||
"power_factor": 0.14,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"ctmeter_consumption_phases": {
|
||||
"L1": {
|
||||
"eid": "100000021",
|
||||
"timestamp": 1708006121,
|
||||
"energy_delivered": 212341,
|
||||
"energy_received": 223451,
|
||||
"active_power": 21,
|
||||
"power_factor": 0.22,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L2": {
|
||||
"eid": "100000022",
|
||||
"timestamp": 1708006122,
|
||||
"energy_delivered": 212342,
|
||||
"energy_received": 223452,
|
||||
"active_power": 31,
|
||||
"power_factor": 0.23,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000023",
|
||||
"timestamp": 1708006123,
|
||||
"energy_delivered": 212343,
|
||||
"energy_received": 223453,
|
||||
"active_power": 51,
|
||||
"power_factor": 0.24,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"ctmeter_storage_phases": {
|
||||
"L1": {
|
||||
"eid": "100000031",
|
||||
"timestamp": 1708006121,
|
||||
"energy_delivered": 312341,
|
||||
"energy_received": 323451,
|
||||
"active_power": 22,
|
||||
"power_factor": 0.32,
|
||||
"voltage": 113,
|
||||
"current": 0.4,
|
||||
"frequency": 50.3,
|
||||
"state": "enabled",
|
||||
"measurement_type": "storage",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L2": {
|
||||
"eid": "100000032",
|
||||
"timestamp": 1708006122,
|
||||
"energy_delivered": 312342,
|
||||
"energy_received": 323452,
|
||||
"active_power": 33,
|
||||
"power_factor": 0.23,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "storage",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000033",
|
||||
"timestamp": 1708006123,
|
||||
"energy_delivered": 312343,
|
||||
"energy_received": 323453,
|
||||
"active_power": 53,
|
||||
"power_factor": 0.24,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "storage",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"dry_contact_status": {
|
||||
"NC1": {
|
||||
"id": "NC1",
|
||||
|
||||
@@ -222,132 +222,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ctmeter_production": {
|
||||
"eid": "100000010",
|
||||
"timestamp": 1708006110,
|
||||
"energy_delivered": 11234,
|
||||
"energy_received": 12345,
|
||||
"active_power": 100,
|
||||
"power_factor": 0.11,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["production-imbalance", "power-on-unused-phase"]
|
||||
},
|
||||
"ctmeter_consumption": {
|
||||
"eid": "100000020",
|
||||
"timestamp": 1708006120,
|
||||
"energy_delivered": 21234,
|
||||
"energy_received": 22345,
|
||||
"active_power": 101,
|
||||
"power_factor": 0.21,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"ctmeter_storage": null,
|
||||
"ctmeter_production_phases": {
|
||||
"L1": {
|
||||
"eid": "100000011",
|
||||
"timestamp": 1708006111,
|
||||
"energy_delivered": 112341,
|
||||
"energy_received": 123451,
|
||||
"active_power": 20,
|
||||
"power_factor": 0.12,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["production-imbalance"]
|
||||
},
|
||||
"L2": {
|
||||
"eid": "100000012",
|
||||
"timestamp": 1708006112,
|
||||
"energy_delivered": 112342,
|
||||
"energy_received": 123452,
|
||||
"active_power": 30,
|
||||
"power_factor": 0.13,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["power-on-unused-phase"]
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000013",
|
||||
"timestamp": 1708006113,
|
||||
"energy_delivered": 112343,
|
||||
"energy_received": 123453,
|
||||
"active_power": 50,
|
||||
"power_factor": 0.14,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"ctmeter_consumption_phases": {
|
||||
"L1": {
|
||||
"eid": "100000021",
|
||||
"timestamp": 1708006121,
|
||||
"energy_delivered": 212341,
|
||||
"energy_received": 223451,
|
||||
"active_power": 21,
|
||||
"power_factor": 0.22,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L2": {
|
||||
"eid": "100000022",
|
||||
"timestamp": 1708006122,
|
||||
"energy_delivered": 212342,
|
||||
"energy_received": 223452,
|
||||
"active_power": 31,
|
||||
"power_factor": 0.23,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"L3": {
|
||||
"eid": "100000023",
|
||||
"timestamp": 1708006123,
|
||||
"energy_delivered": 212343,
|
||||
"energy_received": 223453,
|
||||
"active_power": 51,
|
||||
"power_factor": 0.24,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "net-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
}
|
||||
},
|
||||
"ctmeter_storage_phases": null,
|
||||
"dry_contact_status": {},
|
||||
"dry_contact_settings": {},
|
||||
"inverters": {
|
||||
|
||||
@@ -65,40 +65,6 @@
|
||||
}
|
||||
},
|
||||
"ctmeters_phases": {},
|
||||
"ctmeter_production": {
|
||||
"eid": "100000010",
|
||||
"timestamp": 1708006110,
|
||||
"energy_delivered": 11234,
|
||||
"energy_received": 12345,
|
||||
"active_power": 100,
|
||||
"power_factor": 0.11,
|
||||
"voltage": 111,
|
||||
"current": 0.2,
|
||||
"frequency": 50.1,
|
||||
"state": "enabled",
|
||||
"measurement_type": "production",
|
||||
"metering_status": "normal",
|
||||
"status_flags": ["production-imbalance", "power-on-unused-phase"]
|
||||
},
|
||||
"ctmeter_consumption": {
|
||||
"eid": "100000020",
|
||||
"timestamp": 1708006120,
|
||||
"energy_delivered": 21234,
|
||||
"energy_received": 22345,
|
||||
"active_power": 101,
|
||||
"power_factor": 0.21,
|
||||
"voltage": 112,
|
||||
"current": 0.3,
|
||||
"frequency": 50.2,
|
||||
"state": "enabled",
|
||||
"measurement_type": "total-consumption",
|
||||
"metering_status": "normal",
|
||||
"status_flags": []
|
||||
},
|
||||
"ctmeter_storage": null,
|
||||
"ctmeter_production_phases": null,
|
||||
"ctmeter_consumption_phases": null,
|
||||
"ctmeter_storage_phases": null,
|
||||
"dry_contact_status": {},
|
||||
"dry_contact_settings": {},
|
||||
"inverters": {
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"Dummy_Appliance_1": {
|
||||
"ident": {
|
||||
"type": {
|
||||
"key_localized": "Device type",
|
||||
"value_raw": 20,
|
||||
"value_localized": "Freezer"
|
||||
},
|
||||
"deviceName": "",
|
||||
"protocolVersion": 201,
|
||||
"deviceIdentLabel": {
|
||||
"fabNumber": "Dummy_Appliance_1",
|
||||
"fabIndex": "21",
|
||||
"techType": "FNS 28463 E ed/",
|
||||
"matNumber": "10805070",
|
||||
"swids": ["4497"]
|
||||
},
|
||||
"xkmIdentLabel": {
|
||||
"techType": "EK042",
|
||||
"releaseVersion": "31.17"
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
"ProgramID": {
|
||||
"value_raw": 0,
|
||||
"value_localized": "",
|
||||
"key_localized": "Program name"
|
||||
},
|
||||
"status": {
|
||||
"value_raw": 5,
|
||||
"value_localized": "In use",
|
||||
"key_localized": "status"
|
||||
},
|
||||
"programType": {
|
||||
"value_raw": 0,
|
||||
"value_localized": "",
|
||||
"key_localized": "Program type"
|
||||
},
|
||||
"programPhase": {
|
||||
"value_raw": 0,
|
||||
"value_localized": "",
|
||||
"key_localized": "Program phase"
|
||||
},
|
||||
"remainingTime": [0, 0],
|
||||
"startTime": [0, 0],
|
||||
"targetTemperature": [
|
||||
{
|
||||
"value_raw": -1800,
|
||||
"value_localized": -18,
|
||||
"unit": "Celsius"
|
||||
},
|
||||
{
|
||||
"value_raw": -32768,
|
||||
"value_localized": null,
|
||||
"unit": "Celsius"
|
||||
},
|
||||
{
|
||||
"value_raw": -32768,
|
||||
"value_localized": null,
|
||||
"unit": "Celsius"
|
||||
}
|
||||
],
|
||||
"coreTargetTemperature": [],
|
||||
"temperature": [
|
||||
{
|
||||
"value_raw": -1900,
|
||||
"value_localized": -19,
|
||||
"unit": "Celsius"
|
||||
},
|
||||
{
|
||||
"value_raw": -32768,
|
||||
"value_localized": null,
|
||||
"unit": "Celsius"
|
||||
},
|
||||
{
|
||||
"value_raw": -32768,
|
||||
"value_localized": null,
|
||||
"unit": "Celsius"
|
||||
}
|
||||
],
|
||||
"coreTemperature": [],
|
||||
"signalInfo": false,
|
||||
"signalFailure": false,
|
||||
"signalDoor": false,
|
||||
"remoteEnable": {
|
||||
"fullRemoteControl": true,
|
||||
"smartGrid": false,
|
||||
"mobileStart": false
|
||||
},
|
||||
"ambientLight": null,
|
||||
"light": null,
|
||||
"elapsedTime": [],
|
||||
"spinningSpeed": {
|
||||
"unit": "rpm",
|
||||
"value_raw": null,
|
||||
"value_localized": null,
|
||||
"key_localized": "Spin speed"
|
||||
},
|
||||
"dryingStep": {
|
||||
"value_raw": null,
|
||||
"value_localized": "",
|
||||
"key_localized": "Drying level"
|
||||
},
|
||||
"ventilationStep": {
|
||||
"value_raw": null,
|
||||
"value_localized": "",
|
||||
"key_localized": "Fan level"
|
||||
},
|
||||
"plateStep": [],
|
||||
"ecoFeedback": null,
|
||||
"batteryLevel": null
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,8 @@ from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import get_data_callback
|
||||
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
async_fire_time_changed,
|
||||
@@ -55,6 +57,29 @@ async def test_sensor_states_api_push(
|
||||
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2025-05-31 12:30:00+00:00")
|
||||
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_sensor_states_api_push_one_device(
|
||||
hass: HomeAssistant,
|
||||
mock_miele_client: MagicMock,
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_registry: er.EntityRegistry,
|
||||
setup_platform: MockConfigEntry,
|
||||
push_data_and_actions: None,
|
||||
) -> None:
|
||||
"""Test sensor state when the API pushes data for one device only via SSE."""
|
||||
|
||||
dev_file = await async_load_json_object_fixture(hass, "1_device.json", DOMAIN)
|
||||
data_callback = get_data_callback(mock_miele_client)
|
||||
await data_callback(dev_file)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("sensor.refrigerator_temperature").state != "unavailable"
|
||||
assert hass.states.get("sensor.freezer_temperature").state == "-19.0"
|
||||
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("load_device_file", ["hob.json"])
|
||||
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from serial import SerialException
|
||||
from serialx import SerialException
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.monoprice.const import (
|
||||
|
||||
@@ -4,7 +4,7 @@ from collections import defaultdict
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from serial import SerialException
|
||||
from serialx import SerialException
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
ATTR_INPUT_SOURCE,
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
'object_id_base': 'Global override',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': 'nobo_hub__override',
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Global override',
|
||||
'platform': 'nobo_hub',
|
||||
@@ -103,7 +103,6 @@
|
||||
# name: test_select_entities[select.my_eco_hub_global_override-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'nobo_hub__override',
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'My Eco Hub Global override',
|
||||
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
|
||||
'none',
|
||||
|
||||
@@ -32,12 +32,13 @@ async def test_configure_with_discover(
|
||||
with patch(
|
||||
"homeassistant.components.nobo_hub.config_flow.nobo.async_discover_hubs",
|
||||
return_value=[("1.1.1.1", "123456789")],
|
||||
):
|
||||
) as mock_discover:
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
mock_discover.assert_awaited_once_with(autodiscover_wait=5.0)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
@@ -76,6 +77,106 @@ async def test_configure_with_discover(
|
||||
mock_setup_entry.assert_awaited_once()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("discovered", "expected_devices", "selected_device"),
|
||||
[
|
||||
# Same IP+prefix hidden; sibling with same prefix at a different IP shown.
|
||||
(
|
||||
[("1.1.1.1", "111111111"), ("2.2.2.2", "111111111")],
|
||||
{"2.2.2.2", "manual"},
|
||||
"2.2.2.2",
|
||||
),
|
||||
# Same IP, different prefix → different hub (e.g. replacement), shown.
|
||||
([("1.1.1.1", "222222222")], {"1.1.1.1", "manual"}, "1.1.1.1"),
|
||||
],
|
||||
ids=["sibling_different_ip", "replaced_hub"],
|
||||
)
|
||||
async def test_configure_filters_configured_hubs(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
discovered: list[tuple[str, str]],
|
||||
expected_devices: set[str],
|
||||
selected_device: str,
|
||||
) -> None:
|
||||
"""Configured (IP, prefix) pairs are hidden; the user can pick a remaining one."""
|
||||
MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="111111111012",
|
||||
data={CONF_SERIAL: "111111111012", CONF_IP_ADDRESS: "1.1.1.1"},
|
||||
).add_to_hass(hass)
|
||||
|
||||
with patch("pynobo.nobo.async_discover_hubs", return_value=discovered):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert set(result["data_schema"].schema["device"].container) == expected_devices
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{"device": selected_device},
|
||||
)
|
||||
assert result2["type"] is FlowResultType.FORM
|
||||
assert result2["step_id"] == "selected"
|
||||
|
||||
with (
|
||||
patch("pynobo.nobo.async_connect_hub", return_value=True),
|
||||
patch(
|
||||
"pynobo.nobo.hub_info",
|
||||
new_callable=PropertyMock,
|
||||
create=True,
|
||||
return_value={"name": "My Nobø Ecohub"},
|
||||
),
|
||||
):
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
{"serial_suffix": "999"},
|
||||
)
|
||||
|
||||
assert result3["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
async def test_configure_skips_user_step_when_all_configured(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
"""Flow falls through to manual when every discovered hub matches a configured pair."""
|
||||
MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="111111111012",
|
||||
data={CONF_SERIAL: "111111111012", CONF_IP_ADDRESS: "1.1.1.1"},
|
||||
).add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"pynobo.nobo.async_discover_hubs",
|
||||
return_value=[("1.1.1.1", "111111111")],
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "manual"
|
||||
|
||||
with (
|
||||
patch("pynobo.nobo.async_connect_hub", return_value=True),
|
||||
patch(
|
||||
"pynobo.nobo.hub_info",
|
||||
new_callable=PropertyMock,
|
||||
create=True,
|
||||
return_value={"name": "My Nobø Ecohub"},
|
||||
),
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{"serial": "999999999999", "ip_address": "9.9.9.9"},
|
||||
)
|
||||
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
async def test_configure_manual(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
'MAC': 'AA:BB:CC:DD:EE:FF',
|
||||
'addons': dict({
|
||||
}),
|
||||
'ble': None,
|
||||
'coord_mode': 0,
|
||||
'device_ip': '192.168.1.161',
|
||||
'fs_total': 3456,
|
||||
|
||||
@@ -72,6 +72,22 @@ VICTRON_DC_DC_CONVERTER_SERVICE_INFO = BluetoothServiceInfo(
|
||||
|
||||
VICTRON_DC_DC_CONVERTER_TOKEN = "64ba49f1a8562e45197a8e1fe50d7658"
|
||||
|
||||
# Orion XS
|
||||
|
||||
VICTRON_ORION_XS_SERVICE_INFO = BluetoothServiceInfo(
|
||||
name="Orion XS",
|
||||
address="01:02:03:04:05:15",
|
||||
rssi=-60,
|
||||
manufacturer_data={
|
||||
0x02E1: bytes.fromhex("1000f0a30f3412aa471c3ac637f436dd184693849c9c081a"),
|
||||
},
|
||||
service_data={},
|
||||
service_uuids=[],
|
||||
source="local",
|
||||
)
|
||||
|
||||
VICTRON_ORION_XS_TOKEN = "aabbccdd11223344aabbccdd11223344"
|
||||
|
||||
# DC energy meter
|
||||
|
||||
VICTRON_DC_ENERGY_METER_SERVICE_INFO = BluetoothServiceInfo(
|
||||
@@ -105,12 +121,13 @@ VICTRON_INVERTER_SERVICE_INFO = BluetoothServiceInfo(
|
||||
address="01:02:03:04:05:10",
|
||||
rssi=-60,
|
||||
manufacturer_data={
|
||||
0x02E1: bytes.fromhex("1003a2a2031252dad26f0b8eb39162074d140df410"),
|
||||
}, # not a valid advertisement, but model id mangled to match inverter
|
||||
0x02E1: bytes.fromhex("100064a2033412aa4d1c7c1e0100570c5f4d938199990f1d"),
|
||||
},
|
||||
service_data={},
|
||||
service_uuids=[],
|
||||
source="local",
|
||||
)
|
||||
VICTRON_INVERTER_TOKEN = "aabbccdd11223344aabbccdd11223344"
|
||||
|
||||
VICTRON_INVERTER_RS_SERVICE_INFO = BluetoothServiceInfo(
|
||||
name="Inverter RS",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,10 @@ from .fixtures import (
|
||||
VICTRON_DC_DC_CONVERTER_UNKNOWN_OFF_REASON_SERVICE_INFO,
|
||||
VICTRON_DC_ENERGY_METER_SERVICE_INFO,
|
||||
VICTRON_DC_ENERGY_METER_TOKEN,
|
||||
VICTRON_INVERTER_SERVICE_INFO,
|
||||
VICTRON_INVERTER_TOKEN,
|
||||
VICTRON_ORION_XS_SERVICE_INFO,
|
||||
VICTRON_ORION_XS_TOKEN,
|
||||
VICTRON_SMART_BATTERY_PROTECT_SERVICE_INFO,
|
||||
VICTRON_SMART_BATTERY_PROTECT_TOKEN,
|
||||
VICTRON_SMART_LITHIUM_SERVICE_INFO,
|
||||
@@ -110,6 +114,8 @@ def test_sensor_descriptions_are_json_serializable() -> None:
|
||||
(VICTRON_BATTERY_SENSE_SERVICE_INFO, VICTRON_BATTERY_SENSE_TOKEN),
|
||||
(VICTRON_DC_DC_CONVERTER_SERVICE_INFO, VICTRON_DC_DC_CONVERTER_TOKEN),
|
||||
(VICTRON_DC_ENERGY_METER_SERVICE_INFO, VICTRON_DC_ENERGY_METER_TOKEN),
|
||||
(VICTRON_INVERTER_SERVICE_INFO, VICTRON_INVERTER_TOKEN),
|
||||
(VICTRON_ORION_XS_SERVICE_INFO, VICTRON_ORION_XS_TOKEN),
|
||||
(
|
||||
VICTRON_SMART_BATTERY_PROTECT_SERVICE_INFO,
|
||||
VICTRON_SMART_BATTERY_PROTECT_TOKEN,
|
||||
@@ -124,6 +130,8 @@ def test_sensor_descriptions_are_json_serializable() -> None:
|
||||
"battery_sense",
|
||||
"dc_dc_converter",
|
||||
"dc_energy_meter",
|
||||
"inverter",
|
||||
"orion_xs",
|
||||
"smart_battery_protect",
|
||||
"smart_lithium",
|
||||
"solar_charger",
|
||||
|
||||
Reference in New Issue
Block a user