mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
parent
05d7d31dfd
commit
658ce9d4f2
@ -1122,12 +1122,6 @@ omit =
|
||||
homeassistant/components/soma/cover.py
|
||||
homeassistant/components/soma/sensor.py
|
||||
homeassistant/components/soma/utils.py
|
||||
homeassistant/components/somfy/__init__.py
|
||||
homeassistant/components/somfy/api.py
|
||||
homeassistant/components/somfy/climate.py
|
||||
homeassistant/components/somfy/cover.py
|
||||
homeassistant/components/somfy/sensor.py
|
||||
homeassistant/components/somfy/switch.py
|
||||
homeassistant/components/somfy_mylink/__init__.py
|
||||
homeassistant/components/somfy_mylink/cover.py
|
||||
homeassistant/components/sonos/__init__.py
|
||||
|
@ -966,8 +966,6 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/solax/ @squishykid
|
||||
/homeassistant/components/soma/ @ratsept @sebfortier2288
|
||||
/tests/components/soma/ @ratsept @sebfortier2288
|
||||
/homeassistant/components/somfy/ @tetienne
|
||||
/tests/components/somfy/ @tetienne
|
||||
/homeassistant/components/sonarr/ @ctalkington
|
||||
/tests/components/sonarr/ @ctalkington
|
||||
/homeassistant/components/songpal/ @rytilahti @shenxn
|
||||
|
@ -1,143 +0,0 @@
|
||||
"""Support for Somfy hubs."""
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from pymfy.api.devices.category import Category
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_CLIENT_ID,
|
||||
CONF_CLIENT_SECRET,
|
||||
CONF_OPTIMISTIC,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import (
|
||||
config_entry_oauth2_flow,
|
||||
config_validation as cv,
|
||||
device_registry as dr,
|
||||
)
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import api, config_flow
|
||||
from .const import COORDINATOR, DOMAIN
|
||||
from .coordinator import SomfyDataUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SCAN_INTERVAL = timedelta(minutes=1)
|
||||
SCAN_INTERVAL_ALL_ASSUMED_STATE = timedelta(minutes=60)
|
||||
|
||||
SOMFY_AUTH_CALLBACK_PATH = "/auth/somfy/callback"
|
||||
SOMFY_AUTH_START = "/auth/somfy"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: vol.Schema(
|
||||
{
|
||||
vol.Inclusive(CONF_CLIENT_ID, "oauth"): cv.string,
|
||||
vol.Inclusive(CONF_CLIENT_SECRET, "oauth"): cv.string,
|
||||
vol.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
PLATFORMS = [
|
||||
Platform.CLIMATE,
|
||||
Platform.COVER,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
]
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the Somfy component."""
|
||||
hass.data[DOMAIN] = {}
|
||||
domain_config = config.get(DOMAIN, {})
|
||||
hass.data[DOMAIN][CONF_OPTIMISTIC] = domain_config.get(CONF_OPTIMISTIC, False)
|
||||
|
||||
if CONF_CLIENT_ID in domain_config:
|
||||
config_flow.SomfyFlowHandler.async_register_implementation(
|
||||
hass,
|
||||
config_entry_oauth2_flow.LocalOAuth2Implementation(
|
||||
hass,
|
||||
DOMAIN,
|
||||
config[DOMAIN][CONF_CLIENT_ID],
|
||||
config[DOMAIN][CONF_CLIENT_SECRET],
|
||||
"https://accounts.somfy.com/oauth/oauth/v2/auth",
|
||||
"https://accounts.somfy.com/oauth/oauth/v2/token",
|
||||
),
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Somfy from a config entry."""
|
||||
|
||||
_LOGGER.warning(
|
||||
"The Somfy integration is deprecated and will be removed "
|
||||
"in Home Assistant Core 2022.7; due to the Somfy Open API deprecation."
|
||||
"The Somfy Open API will shutdown June 21st 2022, migrate to the "
|
||||
"Overkiz integration to control your Somfy devices"
|
||||
)
|
||||
|
||||
# Backwards compat
|
||||
if "auth_implementation" not in entry.data:
|
||||
hass.config_entries.async_update_entry(
|
||||
entry, data={**entry.data, "auth_implementation": DOMAIN}
|
||||
)
|
||||
|
||||
implementation = (
|
||||
await config_entry_oauth2_flow.async_get_config_entry_implementation(
|
||||
hass, entry
|
||||
)
|
||||
)
|
||||
|
||||
data = hass.data[DOMAIN]
|
||||
coordinator = SomfyDataUpdateCoordinator(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name="somfy device update",
|
||||
client=api.ConfigEntrySomfyApi(hass, entry, implementation),
|
||||
update_interval=SCAN_INTERVAL,
|
||||
)
|
||||
data[COORDINATOR] = coordinator
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
if all(not bool(device.states) for device in coordinator.data.values()):
|
||||
_LOGGER.debug(
|
||||
"All devices have assumed state. Update interval has been reduced to: %s",
|
||||
SCAN_INTERVAL_ALL_ASSUMED_STATE,
|
||||
)
|
||||
coordinator.update_interval = SCAN_INTERVAL_ALL_ASSUMED_STATE
|
||||
|
||||
device_registry = dr.async_get(hass)
|
||||
|
||||
hubs = [
|
||||
device
|
||||
for device in coordinator.data.values()
|
||||
if Category.HUB.value in device.categories
|
||||
]
|
||||
|
||||
for hub in hubs:
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={(DOMAIN, hub.id)},
|
||||
manufacturer="Somfy",
|
||||
name=hub.name,
|
||||
model=hub.type,
|
||||
)
|
||||
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
@ -1,37 +0,0 @@
|
||||
"""API for Somfy bound to Home Assistant OAuth."""
|
||||
from __future__ import annotations
|
||||
|
||||
from asyncio import run_coroutine_threadsafe
|
||||
|
||||
from pymfy.api import somfy_api
|
||||
|
||||
from homeassistant import config_entries, core
|
||||
from homeassistant.helpers import config_entry_oauth2_flow
|
||||
|
||||
|
||||
class ConfigEntrySomfyApi(somfy_api.SomfyApi):
|
||||
"""Provide a Somfy API tied into an OAuth2 based config entry."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: core.HomeAssistant,
|
||||
config_entry: config_entries.ConfigEntry,
|
||||
implementation: config_entry_oauth2_flow.AbstractOAuth2Implementation,
|
||||
) -> None:
|
||||
"""Initialize the Config Entry Somfy API."""
|
||||
self.hass = hass
|
||||
self.config_entry = config_entry
|
||||
self.session = config_entry_oauth2_flow.OAuth2Session(
|
||||
hass, config_entry, implementation
|
||||
)
|
||||
super().__init__(None, None, token=self.session.token)
|
||||
|
||||
def refresh_tokens(
|
||||
self,
|
||||
) -> dict[str, str | int]:
|
||||
"""Refresh and return new Somfy tokens using Home Assistant OAuth2 session."""
|
||||
run_coroutine_threadsafe(
|
||||
self.session.async_ensure_token_valid(), self.hass.loop
|
||||
).result()
|
||||
|
||||
return self.session.token
|
@ -1,177 +0,0 @@
|
||||
"""Support for Somfy Thermostat."""
|
||||
from __future__ import annotations
|
||||
|
||||
from pymfy.api.devices.category import Category
|
||||
from pymfy.api.devices.thermostat import (
|
||||
DurationType,
|
||||
HvacState,
|
||||
RegulationState,
|
||||
TargetMode,
|
||||
Thermostat,
|
||||
)
|
||||
|
||||
from homeassistant.components.climate import ClimateEntity
|
||||
from homeassistant.components.climate.const import (
|
||||
PRESET_AWAY,
|
||||
PRESET_HOME,
|
||||
PRESET_SLEEP,
|
||||
ClimateEntityFeature,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import COORDINATOR, DOMAIN
|
||||
from .entity import SomfyEntity
|
||||
|
||||
SUPPORTED_CATEGORIES = {Category.HVAC.value}
|
||||
|
||||
PRESET_FROST_GUARD = "Frost Guard"
|
||||
PRESET_GEOFENCING = "Geofencing"
|
||||
PRESET_MANUAL = "Manual"
|
||||
|
||||
PRESETS_MAPPING = {
|
||||
TargetMode.AT_HOME: PRESET_HOME,
|
||||
TargetMode.AWAY: PRESET_AWAY,
|
||||
TargetMode.SLEEP: PRESET_SLEEP,
|
||||
TargetMode.MANUAL: PRESET_MANUAL,
|
||||
TargetMode.GEOFENCING: PRESET_GEOFENCING,
|
||||
TargetMode.FROST_PROTECTION: PRESET_FROST_GUARD,
|
||||
}
|
||||
REVERSE_PRESET_MAPPING = {v: k for k, v in PRESETS_MAPPING.items()}
|
||||
|
||||
HVAC_MODES_MAPPING = {HvacState.COOL: HVACMode.COOL, HvacState.HEAT: HVACMode.HEAT}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Somfy climate platform."""
|
||||
domain_data = hass.data[DOMAIN]
|
||||
coordinator = domain_data[COORDINATOR]
|
||||
|
||||
climates = [
|
||||
SomfyClimate(coordinator, device_id)
|
||||
for device_id, device in coordinator.data.items()
|
||||
if SUPPORTED_CATEGORIES & set(device.categories)
|
||||
]
|
||||
|
||||
async_add_entities(climates)
|
||||
|
||||
|
||||
class SomfyClimate(SomfyEntity, ClimateEntity):
|
||||
"""Representation of a Somfy thermostat device."""
|
||||
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
|
||||
)
|
||||
|
||||
def __init__(self, coordinator, device_id):
|
||||
"""Initialize the Somfy device."""
|
||||
super().__init__(coordinator, device_id)
|
||||
self._climate = None
|
||||
self._create_device()
|
||||
|
||||
def _create_device(self):
|
||||
"""Update the device with the latest data."""
|
||||
self._climate = Thermostat(self.device, self.coordinator.client)
|
||||
|
||||
@property
|
||||
def temperature_unit(self):
|
||||
"""Return the unit of measurement used by the platform."""
|
||||
return TEMP_CELSIUS
|
||||
|
||||
@property
|
||||
def current_temperature(self):
|
||||
"""Return the current temperature."""
|
||||
return self._climate.get_ambient_temperature()
|
||||
|
||||
@property
|
||||
def target_temperature(self):
|
||||
"""Return the temperature we try to reach."""
|
||||
return self._climate.get_target_temperature()
|
||||
|
||||
def set_temperature(self, **kwargs) -> None:
|
||||
"""Set new target temperature."""
|
||||
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
|
||||
return
|
||||
|
||||
self._climate.set_target(TargetMode.MANUAL, temperature, DurationType.NEXT_MODE)
|
||||
|
||||
@property
|
||||
def max_temp(self) -> float:
|
||||
"""Return the maximum temperature."""
|
||||
return 26.0
|
||||
|
||||
@property
|
||||
def min_temp(self) -> float:
|
||||
"""Return the minimum temperature."""
|
||||
return 15.0
|
||||
|
||||
@property
|
||||
def current_humidity(self):
|
||||
"""Return the current humidity."""
|
||||
return self._climate.get_humidity()
|
||||
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode:
|
||||
"""Return hvac operation ie. heat, cool mode."""
|
||||
if self._climate.get_regulation_state() == RegulationState.TIMETABLE:
|
||||
return HVACMode.AUTO
|
||||
return HVAC_MODES_MAPPING[self._climate.get_hvac_state()]
|
||||
|
||||
@property
|
||||
def hvac_modes(self) -> list[HVACMode]:
|
||||
"""Return the list of available hvac operation modes.
|
||||
|
||||
HEAT and COOL mode are exclusive. End user has to enable a mode manually within the Somfy application.
|
||||
So only one mode can be displayed. Auto mode is a scheduler.
|
||||
"""
|
||||
hvac_state = HVAC_MODES_MAPPING[self._climate.get_hvac_state()]
|
||||
return [HVACMode.AUTO, hvac_state]
|
||||
|
||||
def set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
"""Set new target hvac mode."""
|
||||
if hvac_mode == HVACMode.AUTO:
|
||||
self._climate.cancel_target()
|
||||
else:
|
||||
self._climate.set_target(
|
||||
TargetMode.MANUAL, self.target_temperature, DurationType.FURTHER_NOTICE
|
||||
)
|
||||
|
||||
@property
|
||||
def preset_mode(self) -> str | None:
|
||||
"""Return the current preset mode."""
|
||||
mode = self._climate.get_target_mode()
|
||||
return PRESETS_MAPPING.get(mode)
|
||||
|
||||
@property
|
||||
def preset_modes(self) -> list[str] | None:
|
||||
"""Return a list of available preset modes."""
|
||||
return list(PRESETS_MAPPING.values())
|
||||
|
||||
def set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Set new preset mode."""
|
||||
if self.preset_mode == preset_mode:
|
||||
return
|
||||
|
||||
if preset_mode == PRESET_HOME:
|
||||
temperature = self._climate.get_at_home_temperature()
|
||||
elif preset_mode == PRESET_AWAY:
|
||||
temperature = self._climate.get_away_temperature()
|
||||
elif preset_mode == PRESET_SLEEP:
|
||||
temperature = self._climate.get_night_temperature()
|
||||
elif preset_mode == PRESET_FROST_GUARD:
|
||||
temperature = self._climate.get_frost_protection_temperature()
|
||||
elif preset_mode in (PRESET_MANUAL, PRESET_GEOFENCING):
|
||||
temperature = self.target_temperature
|
||||
else:
|
||||
raise ValueError(f"Preset mode not supported: {preset_mode}")
|
||||
|
||||
self._climate.set_target(
|
||||
REVERSE_PRESET_MAPPING[preset_mode], temperature, DurationType.NEXT_MODE
|
||||
)
|
@ -1,26 +0,0 @@
|
||||
"""Config flow for Somfy."""
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers import config_entry_oauth2_flow
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
class SomfyFlowHandler(
|
||||
config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN
|
||||
):
|
||||
"""Config flow to handle Somfy OAuth2 authentication."""
|
||||
|
||||
DOMAIN = DOMAIN
|
||||
|
||||
@property
|
||||
def logger(self) -> logging.Logger:
|
||||
"""Return logger."""
|
||||
return logging.getLogger(__name__)
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle a flow start."""
|
||||
if self._async_current_entries():
|
||||
return self.async_abort(reason="single_instance_allowed")
|
||||
|
||||
return await super().async_step_user(user_input)
|
@ -1,4 +0,0 @@
|
||||
"""Define constants for the Somfy component."""
|
||||
|
||||
DOMAIN = "somfy"
|
||||
COORDINATOR = "coordinator"
|
@ -1,71 +0,0 @@
|
||||
"""Helpers to help coordinate updated."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from pymfy.api.error import QuotaViolationException, SetupNotFoundException
|
||||
from pymfy.api.model import Device
|
||||
from pymfy.api.somfy_api import SomfyApi
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
|
||||
class SomfyDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
"""Class to manage fetching Somfy data."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
logger: logging.Logger,
|
||||
*,
|
||||
name: str,
|
||||
client: SomfyApi,
|
||||
update_interval: timedelta | None = None,
|
||||
) -> None:
|
||||
"""Initialize global data updater."""
|
||||
super().__init__(
|
||||
hass,
|
||||
logger,
|
||||
name=name,
|
||||
update_interval=update_interval,
|
||||
)
|
||||
self.data = {}
|
||||
self.client = client
|
||||
self.site_device: dict[str, list] = {}
|
||||
self.last_site_index = -1
|
||||
|
||||
async def _async_update_data(self) -> dict[str, Device]:
|
||||
"""Fetch Somfy data.
|
||||
|
||||
Somfy only allow one call per minute to /site. There is one exception: 2 calls are allowed after site retrieval.
|
||||
"""
|
||||
if not self.site_device:
|
||||
sites = await self.hass.async_add_executor_job(self.client.get_sites)
|
||||
if not sites:
|
||||
return {}
|
||||
self.site_device = {site.id: [] for site in sites}
|
||||
|
||||
site_id = self._site_id
|
||||
try:
|
||||
devices = await self.hass.async_add_executor_job(
|
||||
self.client.get_devices, site_id
|
||||
)
|
||||
self.site_device[site_id] = devices
|
||||
except SetupNotFoundException:
|
||||
del self.site_device[site_id]
|
||||
return await self._async_update_data()
|
||||
except QuotaViolationException:
|
||||
self.logger.warning("Quota violation")
|
||||
|
||||
return {dev.id: dev for devices in self.site_device.values() for dev in devices}
|
||||
|
||||
@property
|
||||
def _site_id(self):
|
||||
"""Return the next site id to retrieve.
|
||||
|
||||
This tweak is required as Somfy does not allow to call the /site entrypoint more than once per minute.
|
||||
"""
|
||||
self.last_site_index = (self.last_site_index + 1) % len(self.site_device)
|
||||
return list(self.site_device.keys())[self.last_site_index]
|
@ -1,205 +0,0 @@
|
||||
"""Support for Somfy Covers."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import cast
|
||||
|
||||
from pymfy.api.devices.blind import Blind
|
||||
from pymfy.api.devices.category import Category
|
||||
|
||||
from homeassistant.components.cover import (
|
||||
ATTR_POSITION,
|
||||
ATTR_TILT_POSITION,
|
||||
CoverDeviceClass,
|
||||
CoverEntity,
|
||||
CoverEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_OPTIMISTIC, STATE_CLOSED, STATE_OPEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
from .const import COORDINATOR, DOMAIN
|
||||
from .coordinator import SomfyDataUpdateCoordinator
|
||||
from .entity import SomfyEntity
|
||||
|
||||
BLIND_DEVICE_CATEGORIES = {Category.INTERIOR_BLIND.value, Category.EXTERIOR_BLIND.value}
|
||||
SHUTTER_DEVICE_CATEGORIES = {Category.EXTERIOR_BLIND.value}
|
||||
SUPPORTED_CATEGORIES = {
|
||||
Category.ROLLER_SHUTTER.value,
|
||||
Category.INTERIOR_BLIND.value,
|
||||
Category.EXTERIOR_BLIND.value,
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Somfy cover platform."""
|
||||
domain_data = hass.data[DOMAIN]
|
||||
coordinator = domain_data[COORDINATOR]
|
||||
|
||||
covers = [
|
||||
SomfyCover(coordinator, device_id, domain_data[CONF_OPTIMISTIC])
|
||||
for device_id, device in coordinator.data.items()
|
||||
if SUPPORTED_CATEGORIES & set(device.categories)
|
||||
]
|
||||
|
||||
async_add_entities(covers)
|
||||
|
||||
|
||||
class SomfyCover(SomfyEntity, RestoreEntity, CoverEntity):
|
||||
"""Representation of a Somfy cover device."""
|
||||
|
||||
def __init__(self, coordinator, device_id, optimistic):
|
||||
"""Initialize the Somfy device."""
|
||||
super().__init__(coordinator, device_id)
|
||||
self.categories = set(self.device.categories)
|
||||
self.optimistic = optimistic
|
||||
self._closed = None
|
||||
self._is_opening = None
|
||||
self._is_closing = None
|
||||
self._cover = None
|
||||
self._create_device()
|
||||
|
||||
def _create_device(self) -> Blind:
|
||||
"""Update the device with the latest data."""
|
||||
self._cover = Blind(
|
||||
self.device, cast(SomfyDataUpdateCoordinator, self.coordinator).client
|
||||
)
|
||||
|
||||
@property
|
||||
def supported_features(self) -> int:
|
||||
"""Flag supported features."""
|
||||
supported_features = 0
|
||||
if self.has_capability("open"):
|
||||
supported_features |= CoverEntityFeature.OPEN
|
||||
if self.has_capability("close"):
|
||||
supported_features |= CoverEntityFeature.CLOSE
|
||||
if self.has_capability("stop"):
|
||||
supported_features |= CoverEntityFeature.STOP
|
||||
if self.has_capability("position"):
|
||||
supported_features |= CoverEntityFeature.SET_POSITION
|
||||
if self.has_capability("rotation"):
|
||||
supported_features |= (
|
||||
CoverEntityFeature.OPEN_TILT
|
||||
| CoverEntityFeature.CLOSE_TILT
|
||||
| CoverEntityFeature.STOP_TILT
|
||||
| CoverEntityFeature.SET_TILT_POSITION
|
||||
)
|
||||
|
||||
return supported_features
|
||||
|
||||
async def async_close_cover(self, **kwargs):
|
||||
"""Close the cover."""
|
||||
self._is_closing = True
|
||||
self.async_write_ha_state()
|
||||
try:
|
||||
# Blocks until the close command is sent
|
||||
await self.hass.async_add_executor_job(self._cover.close)
|
||||
self._closed = True
|
||||
finally:
|
||||
self._is_closing = None
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_open_cover(self, **kwargs):
|
||||
"""Open the cover."""
|
||||
self._is_opening = True
|
||||
self.async_write_ha_state()
|
||||
try:
|
||||
# Blocks until the open command is sent
|
||||
await self.hass.async_add_executor_job(self._cover.open)
|
||||
self._closed = False
|
||||
finally:
|
||||
self._is_opening = None
|
||||
self.async_write_ha_state()
|
||||
|
||||
def stop_cover(self, **kwargs):
|
||||
"""Stop the cover."""
|
||||
self._cover.stop()
|
||||
|
||||
def set_cover_position(self, **kwargs):
|
||||
"""Move the cover shutter to a specific position."""
|
||||
self._cover.set_position(100 - kwargs[ATTR_POSITION])
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the device class."""
|
||||
if self.categories & BLIND_DEVICE_CATEGORIES:
|
||||
return CoverDeviceClass.BLIND
|
||||
if self.categories & SHUTTER_DEVICE_CATEGORIES:
|
||||
return CoverDeviceClass.SHUTTER
|
||||
return None
|
||||
|
||||
@property
|
||||
def current_cover_position(self):
|
||||
"""Return the current position of cover shutter."""
|
||||
if not self.has_state("position"):
|
||||
return None
|
||||
return 100 - self._cover.get_position()
|
||||
|
||||
@property
|
||||
def is_opening(self):
|
||||
"""Return if the cover is opening."""
|
||||
if not self.optimistic:
|
||||
return None
|
||||
return self._is_opening
|
||||
|
||||
@property
|
||||
def is_closing(self):
|
||||
"""Return if the cover is closing."""
|
||||
if not self.optimistic:
|
||||
return None
|
||||
return self._is_closing
|
||||
|
||||
@property
|
||||
def is_closed(self) -> bool | None:
|
||||
"""Return if the cover is closed."""
|
||||
is_closed = None
|
||||
if self.has_state("position"):
|
||||
is_closed = self._cover.is_closed()
|
||||
elif self.optimistic:
|
||||
is_closed = self._closed
|
||||
return is_closed
|
||||
|
||||
@property
|
||||
def current_cover_tilt_position(self) -> int | None:
|
||||
"""Return current position of cover tilt.
|
||||
|
||||
None is unknown, 0 is closed, 100 is fully open.
|
||||
"""
|
||||
if not self.has_state("orientation"):
|
||||
return None
|
||||
return 100 - self._cover.orientation
|
||||
|
||||
def set_cover_tilt_position(self, **kwargs):
|
||||
"""Move the cover tilt to a specific position."""
|
||||
self._cover.orientation = 100 - kwargs[ATTR_TILT_POSITION]
|
||||
|
||||
def open_cover_tilt(self, **kwargs):
|
||||
"""Open the cover tilt."""
|
||||
self._cover.orientation = 0
|
||||
|
||||
def close_cover_tilt(self, **kwargs):
|
||||
"""Close the cover tilt."""
|
||||
self._cover.orientation = 100
|
||||
|
||||
def stop_cover_tilt(self, **kwargs):
|
||||
"""Stop the cover."""
|
||||
self._cover.stop()
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Complete the initialization."""
|
||||
await super().async_added_to_hass()
|
||||
if not self.optimistic:
|
||||
return
|
||||
# Restore the last state if we use optimistic
|
||||
last_state = await self.async_get_last_state()
|
||||
|
||||
if last_state is not None and last_state.state in (
|
||||
STATE_OPEN,
|
||||
STATE_CLOSED,
|
||||
):
|
||||
self._closed = last_state.state == STATE_CLOSED
|
@ -1,73 +0,0 @@
|
||||
"""Entity representing a Somfy device."""
|
||||
|
||||
from abc import abstractmethod
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
class SomfyEntity(CoordinatorEntity, Entity):
|
||||
"""Representation of a generic Somfy device."""
|
||||
|
||||
def __init__(self, coordinator, device_id):
|
||||
"""Initialize the Somfy device."""
|
||||
super().__init__(coordinator)
|
||||
self._id = device_id
|
||||
|
||||
@property
|
||||
def device(self):
|
||||
"""Return data for the device id."""
|
||||
return self.coordinator.data[self._id]
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return the unique id base on the id returned by Somfy."""
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name of the device."""
|
||||
return self.device.name
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return device specific attributes.
|
||||
|
||||
Implemented by platform classes.
|
||||
"""
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, self.unique_id)},
|
||||
name=self.name,
|
||||
model=self.device.type,
|
||||
via_device=(DOMAIN, self.device.parent_id),
|
||||
# For the moment, Somfy only returns their own device.
|
||||
manufacturer="Somfy",
|
||||
)
|
||||
|
||||
def has_capability(self, capability: str) -> bool:
|
||||
"""Test if device has a capability."""
|
||||
capabilities = self.device.capabilities
|
||||
return bool([c for c in capabilities if c.name == capability])
|
||||
|
||||
def has_state(self, state: str) -> bool:
|
||||
"""Test if device has a state."""
|
||||
states = self.device.states
|
||||
return bool([c for c in states if c.name == state])
|
||||
|
||||
@property
|
||||
def assumed_state(self) -> bool:
|
||||
"""Return if the device has an assumed state."""
|
||||
return not bool(self.device.states)
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self):
|
||||
"""Process an update from the coordinator."""
|
||||
self._create_device()
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@abstractmethod
|
||||
def _create_device(self):
|
||||
"""Update the device with the latest data."""
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"domain": "somfy",
|
||||
"name": "Somfy",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/somfy",
|
||||
"dependencies": ["auth"],
|
||||
"codeowners": ["@tetienne"],
|
||||
"requirements": ["pymfy==0.11.0"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_kizbox._tcp.local.",
|
||||
"name": "gateway*"
|
||||
}
|
||||
],
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pymfy"]
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
"""Support for Somfy Thermostat Battery."""
|
||||
from pymfy.api.devices.category import Category
|
||||
from pymfy.api.devices.thermostat import Thermostat
|
||||
|
||||
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import PERCENTAGE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import COORDINATOR, DOMAIN
|
||||
from .entity import SomfyEntity
|
||||
|
||||
SUPPORTED_CATEGORIES = {Category.HVAC.value}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Somfy sensor platform."""
|
||||
domain_data = hass.data[DOMAIN]
|
||||
coordinator = domain_data[COORDINATOR]
|
||||
|
||||
sensors = [
|
||||
SomfyThermostatBatterySensor(coordinator, device_id)
|
||||
for device_id, device in coordinator.data.items()
|
||||
if SUPPORTED_CATEGORIES & set(device.categories)
|
||||
]
|
||||
|
||||
async_add_entities(sensors)
|
||||
|
||||
|
||||
class SomfyThermostatBatterySensor(SomfyEntity, SensorEntity):
|
||||
"""Representation of a Somfy thermostat battery."""
|
||||
|
||||
_attr_device_class = SensorDeviceClass.BATTERY
|
||||
_attr_native_unit_of_measurement = PERCENTAGE
|
||||
|
||||
def __init__(self, coordinator, device_id):
|
||||
"""Initialize the Somfy device."""
|
||||
super().__init__(coordinator, device_id)
|
||||
self._climate = None
|
||||
self._create_device()
|
||||
|
||||
def _create_device(self):
|
||||
"""Update the device with the latest data."""
|
||||
self._climate = Thermostat(self.device, self.coordinator.client)
|
||||
|
||||
@property
|
||||
def native_value(self) -> int:
|
||||
"""Return the state of the sensor."""
|
||||
return self._climate.get_battery()
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "[%key:common::config_flow::title::oauth2_pick_implementation%]"
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
||||
"authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
|
||||
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
|
||||
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "[%key:common::config_flow::create_entry::authenticated%]"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
"""Support for Somfy Camera Shutter."""
|
||||
from pymfy.api.devices.camera_protect import CameraProtect
|
||||
from pymfy.api.devices.category import Category
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import COORDINATOR, DOMAIN
|
||||
from .entity import SomfyEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Somfy switch platform."""
|
||||
domain_data = hass.data[DOMAIN]
|
||||
coordinator = domain_data[COORDINATOR]
|
||||
|
||||
switches = [
|
||||
SomfyCameraShutter(coordinator, device_id)
|
||||
for device_id, device in coordinator.data.items()
|
||||
if Category.CAMERA.value in device.categories
|
||||
]
|
||||
|
||||
async_add_entities(switches)
|
||||
|
||||
|
||||
class SomfyCameraShutter(SomfyEntity, SwitchEntity):
|
||||
"""Representation of a Somfy Camera Shutter device."""
|
||||
|
||||
def __init__(self, coordinator, device_id):
|
||||
"""Initialize the Somfy device."""
|
||||
super().__init__(coordinator, device_id)
|
||||
self._create_device()
|
||||
|
||||
def _create_device(self):
|
||||
"""Update the device with the latest data."""
|
||||
self.shutter = CameraProtect(self.device, self.coordinator.client)
|
||||
|
||||
def turn_on(self, **kwargs) -> None:
|
||||
"""Turn the entity on."""
|
||||
self.shutter.open_shutter()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn the entity off."""
|
||||
self.shutter.close_shutter()
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return True if entity is on."""
|
||||
return self.shutter.get_shutter_position() == "opened"
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0430\u0434\u0440\u0435\u0441 \u0437\u0430 \u043e\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0432 \u0441\u0440\u043e\u043a.",
|
||||
"missing_configuration": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044a\u0442 Somfy \u043d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d. \u041c\u043e\u043b\u044f, \u0441\u043b\u0435\u0434\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430.",
|
||||
"single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0438\u0440\u0430\u043d\u0435 \u0441\u044a\u0441 Somfy."
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "\u0418\u0437\u0431\u043e\u0440 \u043d\u0430 \u043c\u0435\u0442\u043e\u0434 \u0437\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Temps d'espera esgotat durant la generaci\u00f3 de l'URL d'autoritzaci\u00f3.",
|
||||
"missing_configuration": "El component no est\u00e0 configurat. Mira'n la documentaci\u00f3.",
|
||||
"no_url_available": "No hi ha cap URL disponible. Per a m\u00e9s informaci\u00f3 sobre aquest error, [consulta la secci\u00f3 d'ajuda]({docs_url})",
|
||||
"single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Autenticaci\u00f3 exitosa"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Selecciona el m\u00e8tode d'autenticaci\u00f3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "\u010casov\u00fd limit autoriza\u010dn\u00edho URL vypr\u0161el",
|
||||
"missing_configuration": "Komponenta nen\u00ed nastavena. Postupujte podle dokumentace.",
|
||||
"no_url_available": "Nen\u00ed k dispozici \u017e\u00e1dn\u00e1 adresa URL. Informace o t\u00e9to chyb\u011b naleznete [v sekci n\u00e1pov\u011bdy]({docs_url})",
|
||||
"single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "\u00dasp\u011b\u0161n\u011b ov\u011b\u0159eno"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Vyberte metodu ov\u011b\u0159en\u00ed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Timeout ved generering af autoriseret url.",
|
||||
"missing_configuration": "Komponenten Somfy er ikke konfigureret. F\u00f8lg venligst dokumentationen."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Godkendt med Somfy."
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "V\u00e6lg godkendelsesmetode"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Zeit\u00fcberschreitung beim Erstellen der Authorisierungs-URL.",
|
||||
"missing_configuration": "Die Komponente ist nicht konfiguriert. Bitte der Dokumentation folgen.",
|
||||
"no_url_available": "Keine URL verf\u00fcgbar. Informationen zu diesem Fehler findest du [im Hilfebereich]({docs_url}).",
|
||||
"single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Erfolgreich authentifiziert"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "W\u00e4hle die Authentifizierungsmethode"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "\u039b\u03ae\u03be\u03b7 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03bf\u03cd \u03bf\u03c1\u03af\u03bf\u03c5 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7\u03c2 URL \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2.",
|
||||
"missing_configuration": "\u03a4\u03bf \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03bf \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7.",
|
||||
"no_url_available": "\u0394\u03b5\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL. \u0393\u03b9\u03b1 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ac \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1, [\u03b5\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b2\u03bf\u03ae\u03b8\u03b5\u03b9\u03b1\u03c2] ( {docs_url} )",
|
||||
"single_instance_allowed": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b7\u03ba\u03b5 \u03ae\u03b4\u03b7. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03c0\u03b1\u03c1\u03b1\u03bc\u03b5\u03c4\u03c1\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "\u0395\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03bc\u03b5\u03b8\u03cc\u03b4\u03bf\u03c5 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Timeout generating authorize URL.",
|
||||
"missing_configuration": "The component is not configured. Please follow the documentation.",
|
||||
"no_url_available": "No URL available. For information about this error, [check the help section]({docs_url})",
|
||||
"single_instance_allowed": "Already configured. Only a single configuration possible."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Successfully authenticated"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Pick Authentication Method"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Timeout generating authorise URL."
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Tiempo de espera agotado para generar la URL de autorizaci\u00f3n.",
|
||||
"missing_configuration": "El componente Somfy no est\u00e1 configurado. Por favor, siga la documentaci\u00f3n."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Autenticado con \u00e9xito con Somfy."
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Seleccione el m\u00e9todo de autenticaci\u00f3n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.",
|
||||
"missing_configuration": "El componente no est\u00e1 configurado. Consulta la documentaci\u00f3n.",
|
||||
"no_url_available": "No hay URL disponible. Para obtener informaci\u00f3n sobre este error, [consulta la secci\u00f3n de ayuda]({docs_url})",
|
||||
"single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Autenticado correctamente"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Seleccione el m\u00e9todo de autenticaci\u00f3n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Kinnitus-URLi loomise ajal\u00f5pp.",
|
||||
"missing_configuration": "Komponent pole seadistatud. Palun loe dokumentatsiooni.",
|
||||
"no_url_available": "URL pole saadaval. Rohkem teavet [check the help section]({docs_url})",
|
||||
"single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Edukalt tuvastatud"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Vali tuvastusmeetod"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "D\u00e9lai de g\u00e9n\u00e9ration de l'URL d'authentification expir\u00e9.",
|
||||
"missing_configuration": "Le composant n'est pas configur\u00e9. Veuillez suivre la documentation.",
|
||||
"no_url_available": "Aucune URL disponible. Pour plus d'informations sur cette erreur, [consultez la section d'aide]({docs_url})",
|
||||
"single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Authentification r\u00e9ussie"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "S\u00e9lectionner une m\u00e9thode d'authentification"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "\u05e4\u05e1\u05e7 \u05d6\u05de\u05df \u05dc\u05d9\u05e6\u05d9\u05e8\u05ea \u05db\u05ea\u05d5\u05d1\u05ea URL \u05dc\u05d0\u05d9\u05e9\u05d5\u05e8.",
|
||||
"missing_configuration": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05e8\u05db\u05d9\u05d1 \u05dc\u05d0 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e0\u05d0 \u05e2\u05e7\u05d5\u05d1 \u05d0\u05d7\u05e8 \u05d4\u05ea\u05d9\u05e2\u05d5\u05d3.",
|
||||
"no_url_available": "\u05d0\u05d9\u05df \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d6\u05de\u05d9\u05e0\u05d4. \u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e2\u05dc \u05e9\u05d2\u05d9\u05d0\u05d4 \u05d6\u05d5, [\u05e2\u05d9\u05d9\u05df \u05d1\u05e1\u05e2\u05d9\u05e3 \u05d4\u05e2\u05d6\u05e8\u05d4] ({docs_url})",
|
||||
"single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "\u05d0\u05d5\u05de\u05ea \u05d1\u05d4\u05e6\u05dc\u05d7\u05d4"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "\u05d1\u05d7\u05e8 \u05e9\u05d9\u05d8\u05ea \u05d0\u05d9\u05de\u05d5\u05ea"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"create_entry": {
|
||||
"default": "Uspje\u0161no autentificirano sa Somfy."
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a hiteles\u00edt\u00e9si URL gener\u00e1l\u00e1sa sor\u00e1n.",
|
||||
"missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.",
|
||||
"no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3.",
|
||||
"single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Sikeres hiteles\u00edt\u00e9s"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Tenggang waktu pembuatan URL otorisasi habis.",
|
||||
"missing_configuration": "Komponen tidak dikonfigurasi. Ikuti petunjuk dalam dokumentasi.",
|
||||
"no_url_available": "Tidak ada URL yang tersedia. Untuk informasi tentang kesalahan ini, [lihat bagian bantuan]({docs_url})",
|
||||
"single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Berhasil diautentikasi"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Pilih Metode Autentikasi"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.",
|
||||
"missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.",
|
||||
"no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})",
|
||||
"single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Autenticazione riuscita"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Scegli il metodo di autenticazione"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "\u8a8d\u8a3cURL\u306e\u751f\u6210\u304c\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3057\u307e\u3057\u305f\u3002",
|
||||
"missing_configuration": "\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306b\u5f93\u3063\u3066\u304f\u3060\u3055\u3044\u3002",
|
||||
"no_url_available": "\u4f7f\u7528\u53ef\u80fd\u306aURL\u304c\u3042\u308a\u307e\u305b\u3093\u3002\u3053\u306e\u30a8\u30e9\u30fc\u306e\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001[\u30d8\u30eb\u30d7\u30bb\u30af\u30b7\u30e7\u30f3\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044]({docs_url})",
|
||||
"single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "\u6b63\u5e38\u306b\u8a8d\u8a3c\u3055\u308c\u307e\u3057\u305f"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "\u8a8d\u8a3c\u65b9\u6cd5\u306e\u9078\u629e"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "\uc778\uc99d URL \uc0dd\uc131 \uc2dc\uac04\uc774 \ucd08\uacfc\ub418\uc5c8\uc2b5\ub2c8\ub2e4.",
|
||||
"missing_configuration": "\uad6c\uc131\uc694\uc18c\uac00 \uad6c\uc131\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uc124\uba85\uc11c\ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694.",
|
||||
"no_url_available": "\uc0ac\uc6a9 \uac00\ub2a5\ud55c URL\uc774 \uc5c6\uc2b5\ub2c8\ub2e4. \uc774 \uc624\ub958\uc5d0 \ub300\ud55c \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 [\ub3c4\uc6c0\ub9d0 \uc139\uc158]({docs_url}) \uc744(\ub97c) \ucc38\uc870\ud574\uc8fc\uc138\uc694.",
|
||||
"single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "\uc131\uacf5\uc801\uc73c\ub85c \uc778\uc99d\ub418\uc5c8\uc2b5\ub2c8\ub2e4"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "\uc778\uc99d \ubc29\ubc95 \uc120\ud0dd\ud558\uae30"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Z\u00e4it Iwwerschreidung beim gener\u00e9ieren vun der Autorisatiouns URL.",
|
||||
"missing_configuration": "Komponent ass nach net konfigur\u00e9iert. Folleg w.e.g der Dokumentatioun.",
|
||||
"no_url_available": "Keng URL disponibel. Fir Informatiounen iwwert d\u00ebse Feeler, [kuck H\u00ebllef Sektioun]({docs_url})",
|
||||
"single_instance_allowed": "Scho konfigur\u00e9iert. N\u00ebmmen eng eenzeg Konfiguratioun ass m\u00e9iglech."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Erfollegr\u00e4ich authentifiz\u00e9iert."
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Wiel Authentifikatiouns Method aus"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.",
|
||||
"missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.",
|
||||
"no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})",
|
||||
"single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Authenticatie geslaagd"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Kies een authenticatie methode"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Tidsavbrudd ved oppretting av godkjenningsadresse",
|
||||
"missing_configuration": "Komponenten er ikke konfigurert, vennligst f\u00f8lg dokumentasjonen",
|
||||
"no_url_available": "Ingen URL tilgjengelig. For informasjon om denne feilen, [sjekk hjelpseksjonen]({docs_url})",
|
||||
"single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Vellykket godkjenning"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Velg godkjenningsmetode"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Przekroczono limit czasu generowania URL autoryzacji",
|
||||
"missing_configuration": "Komponent nie jest skonfigurowany. Post\u0119puj zgodnie z dokumentacj\u0105.",
|
||||
"no_url_available": "Brak dost\u0119pnego adresu URL. Aby uzyska\u0107 informacje na temat tego b\u0142\u0119du, [sprawd\u017a sekcj\u0119 pomocy] ({docs_url})",
|
||||
"single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Pomy\u015blnie uwierzytelniono"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Wybierz metod\u0119 uwierzytelniania"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Tempo limite gerando URL de autoriza\u00e7\u00e3o.",
|
||||
"missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.",
|
||||
"no_url_available": "N\u00e3o h\u00e1 URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a se\u00e7\u00e3o de ajuda]({docs_url})",
|
||||
"single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Autenticado com sucesso"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Escolha o m\u00e9todo de autentica\u00e7\u00e3o"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o",
|
||||
"missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.",
|
||||
"no_url_available": "Nenhum URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a sec\u00e7\u00e3o de ajuda]({docs_url})",
|
||||
"single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Autenticado com sucesso"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Escolha o m\u00e9todo de autentica\u00e7\u00e3o"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.",
|
||||
"missing_configuration": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439.",
|
||||
"no_url_available": "URL-\u0430\u0434\u0440\u0435\u0441 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439]({docs_url}) \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0431 \u044d\u0442\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0435.",
|
||||
"single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0439\u0434\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e."
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"create_entry": {
|
||||
"default": "\u00daspe\u0161ne overen\u00e9"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "\u010casovna omejitev za generiranje potrditvenega URL-ja je potekla.",
|
||||
"missing_configuration": "Komponenta Somfy ni konfigurirana. Upo\u0161tevajte dokumentacijo."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Uspe\u0161no overjen s Somfy-jem."
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Izberite na\u010din preverjanja pristnosti"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Timeout vid skapandet av en auktoriseringsadress.",
|
||||
"missing_configuration": "Somfy-komponenten \u00e4r inte konfigurerad. V\u00e4nligen f\u00f6lj dokumentationen."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Lyckad autentisering med Somfy."
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "V\u00e4lj autentiseringsmetod"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "Yetkilendirme URL'si olu\u015ftururken zaman a\u015f\u0131m\u0131.",
|
||||
"missing_configuration": "Bile\u015fen yap\u0131land\u0131r\u0131lmam\u0131\u015f. L\u00fctfen belgeleri takip edin.",
|
||||
"no_url_available": "Kullan\u0131labilir URL yok. Bu hata hakk\u0131nda bilgi i\u00e7in [yard\u0131m b\u00f6l\u00fcm\u00fcne bak\u0131n]({docs_url})",
|
||||
"single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Ba\u015far\u0131yla do\u011fruland\u0131"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Kimlik Do\u011frulama Y\u00f6ntemini Se\u00e7"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "\u041c\u0438\u043d\u0443\u0432 \u0447\u0430\u0441 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0456\u0457 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0456\u0457.",
|
||||
"missing_configuration": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0438 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f. \u0411\u0443\u0434\u044c \u043b\u0430\u0441\u043a\u0430, \u043e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u044f\u043c\u0438.",
|
||||
"no_url_available": "URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430. \u041e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0456\u0454\u044e] ({docs_url}) \u0434\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u0456\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u0457 \u043f\u0440\u043e \u0446\u044e \u043f\u043e\u043c\u0438\u043b\u043a\u0443.",
|
||||
"single_instance_allowed": "\u0412\u0436\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u043e\u0432\u0430\u043d\u043e. \u041c\u043e\u0436\u043b\u0438\u0432\u0430 \u043b\u0438\u0448\u0435 \u043e\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u044f."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "\u0410\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044e \u0443\u0441\u043f\u0456\u0448\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043e."
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0441\u043f\u043e\u0441\u0456\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u0457"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"authorize_url_timeout": "\u7522\u751f\u8a8d\u8b49 URL \u6642\u903e\u6642\u3002",
|
||||
"missing_configuration": "\u5143\u4ef6\u5c1a\u672a\u8a2d\u7f6e\uff0c\u8acb\u53c3\u95b1\u6587\u4ef6\u8aaa\u660e\u3002",
|
||||
"no_url_available": "\u6c92\u6709\u53ef\u7528\u7684\u7db2\u5740\u3002\u95dc\u65bc\u6b64\u932f\u8aa4\u66f4\u8a73\u7d30\u8a0a\u606f\uff0c[\u9ede\u9078\u5354\u52a9\u7ae0\u7bc0]({docs_url})",
|
||||
"single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "\u5df2\u6210\u529f\u8a8d\u8b49"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "\u9078\u64c7\u9a57\u8b49\u6a21\u5f0f"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -327,7 +327,6 @@ FLOWS = {
|
||||
"solarlog",
|
||||
"solax",
|
||||
"soma",
|
||||
"somfy",
|
||||
"somfy_mylink",
|
||||
"sonarr",
|
||||
"songpal",
|
||||
|
@ -233,10 +233,6 @@ ZEROCONF = {
|
||||
{
|
||||
"domain": "overkiz",
|
||||
"name": "gateway*"
|
||||
},
|
||||
{
|
||||
"domain": "somfy",
|
||||
"name": "gateway*"
|
||||
}
|
||||
],
|
||||
"_leap._tcp.local.": [
|
||||
|
@ -1648,9 +1648,6 @@ pymelcloud==2.5.6
|
||||
# homeassistant.components.meteoclimatic
|
||||
pymeteoclimatic==0.0.6
|
||||
|
||||
# homeassistant.components.somfy
|
||||
pymfy==0.11.0
|
||||
|
||||
# homeassistant.components.xiaomi_tv
|
||||
pymitv==1.4.3
|
||||
|
||||
|
@ -1115,9 +1115,6 @@ pymelcloud==2.5.6
|
||||
# homeassistant.components.meteoclimatic
|
||||
pymeteoclimatic==0.0.6
|
||||
|
||||
# homeassistant.components.somfy
|
||||
pymfy==0.11.0
|
||||
|
||||
# homeassistant.components.mochad
|
||||
pymochad==0.2.0
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
"""Tests for the Somfy component."""
|
@ -1,127 +0,0 @@
|
||||
"""Tests for the Somfy config flow."""
|
||||
import asyncio
|
||||
from http import HTTPStatus
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow, setup
|
||||
from homeassistant.components.somfy import DOMAIN
|
||||
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
|
||||
from homeassistant.helpers import config_entry_oauth2_flow
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
CLIENT_ID_VALUE = "1234"
|
||||
CLIENT_SECRET_VALUE = "5678"
|
||||
|
||||
|
||||
async def test_abort_if_no_configuration(hass):
|
||||
"""Check flow abort when no configuration."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "missing_configuration"
|
||||
|
||||
|
||||
async def test_abort_if_existing_entry(hass):
|
||||
"""Check flow abort when an entry already exist."""
|
||||
MockConfigEntry(domain=DOMAIN).add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "single_instance_allowed"
|
||||
|
||||
|
||||
async def test_full_flow(
|
||||
hass, hass_client_no_auth, aioclient_mock, current_request_with_host
|
||||
):
|
||||
"""Check full flow."""
|
||||
assert await setup.async_setup_component(
|
||||
hass,
|
||||
DOMAIN,
|
||||
{
|
||||
DOMAIN: {
|
||||
CONF_CLIENT_ID: CLIENT_ID_VALUE,
|
||||
CONF_CLIENT_SECRET: CLIENT_SECRET_VALUE,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
state = config_entry_oauth2_flow._encode_jwt(
|
||||
hass,
|
||||
{
|
||||
"flow_id": result["flow_id"],
|
||||
"redirect_uri": "https://example.com/auth/external/callback",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP
|
||||
assert result["url"] == (
|
||||
"https://accounts.somfy.com/oauth/oauth/v2/auth"
|
||||
f"?response_type=code&client_id={CLIENT_ID_VALUE}"
|
||||
"&redirect_uri=https://example.com/auth/external/callback"
|
||||
f"&state={state}"
|
||||
)
|
||||
|
||||
client = await hass_client_no_auth()
|
||||
resp = await client.get(f"/auth/external/callback?code=abcd&state={state}")
|
||||
assert resp.status == HTTPStatus.OK
|
||||
assert resp.headers["content-type"] == "text/html; charset=utf-8"
|
||||
|
||||
aioclient_mock.post(
|
||||
"https://accounts.somfy.com/oauth/oauth/v2/token",
|
||||
json={
|
||||
"refresh_token": "mock-refresh-token",
|
||||
"access_token": "mock-access-token",
|
||||
"type": "Bearer",
|
||||
"expires_in": 60,
|
||||
},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.somfy.async_setup_entry", return_value=True
|
||||
) as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["data"]["auth_implementation"] == DOMAIN
|
||||
|
||||
result["data"]["token"].pop("expires_at")
|
||||
assert result["data"]["token"] == {
|
||||
"refresh_token": "mock-refresh-token",
|
||||
"access_token": "mock-access-token",
|
||||
"type": "Bearer",
|
||||
"expires_in": 60,
|
||||
}
|
||||
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_abort_if_authorization_timeout(hass, current_request_with_host):
|
||||
"""Check Somfy authorization timeout."""
|
||||
assert await setup.async_setup_component(
|
||||
hass,
|
||||
DOMAIN,
|
||||
{
|
||||
DOMAIN: {
|
||||
CONF_CLIENT_ID: CLIENT_ID_VALUE,
|
||||
CONF_CLIENT_SECRET: CLIENT_SECRET_VALUE,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.somfy.config_entry_oauth2_flow."
|
||||
"LocalOAuth2Implementation.async_generate_authorize_url",
|
||||
side_effect=asyncio.TimeoutError,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "authorize_url_timeout"
|
Loading…
x
Reference in New Issue
Block a user