forked from home-assistant/core
Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9962e9b67e | |||
| 8e8a170121 | |||
| aacd8e044d | |||
| 08fb16a0c2 | |||
| 354d77d26b | |||
| 2e541e7ef6 | |||
| a2c9f92420 | |||
| e67d9988fd | |||
| c4470fc36d | |||
| ab14671dbc | |||
| be83753514 | |||
| e0a6a6cfa6 | |||
| 0d9393ca79 | |||
| d06e640889 | |||
| 9657567280 | |||
| 5bc49b1757 | |||
| 590bdc1f49 | |||
| bc01df6b52 | |||
| 7fd3f404de | |||
| f59eb6c277 | |||
| 08c23dd688 | |||
| 1a72b64024 | |||
| 8a257df59f | |||
| e30ea3e344 | |||
| 423d8f0bca | |||
| b2ccf2e87e | |||
| 4a7aee4bde | |||
| 5b0c7321b5 | |||
| 1df82f39c1 | |||
| 1c88195d32 | |||
| d82e409d8e | |||
| 3253eeca7a | |||
| f4f1b0dfc5 | |||
| 1f08fc72bb | |||
| 323ab97ff5 | |||
| 93229196d8 | |||
| 23e04ba891 | |||
| 2d5ab5a0dc | |||
| 30f63ae0ea | |||
| 2d85890789 | |||
| c791a7c7fb | |||
| fe7c7001ad | |||
| 6d2a2b1c91 | |||
| 1df7fcea09 | |||
| 681de9a516 | |||
| a5378ec9a8 | |||
| 54687d6b56 | |||
| 4c0ea397c8 | |||
| 9769a0f0ec | |||
| e78dd34a45 | |||
| 6543a1169b | |||
| 012ba55154 | |||
| e12425c229 | |||
| 5aaddf72e6 | |||
| dbd8ffc282 | |||
| 372afc5c28 | |||
| ed8a0ef0ea | |||
| 1d8f5b2e16 | |||
| 75796e1f0f | |||
| 063bbe91d1 | |||
| bfcae4e07b | |||
| be77d7daa5 | |||
| a58e4e0f88 | |||
| 56a583e6ac | |||
| 517e89ab3c | |||
| ef8029ebbf | |||
| 264b6d4f77 | |||
| b24d0a86ee | |||
| 8a7e2922c2 | |||
| 5fe3adff57 | |||
| 4641497806 | |||
| eed15bb9fa | |||
| 7028aa7dac | |||
| 6c93b28374 | |||
| 65286d0544 | |||
| a678eee31b | |||
| fe541583a8 | |||
| eabcfa419e |
@@ -20,7 +20,7 @@ repos:
|
||||
- --remove-all-unused-imports
|
||||
stages: [manual]
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.12.0
|
||||
rev: 23.1.0
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
@@ -60,7 +60,7 @@ repos:
|
||||
- --configfile=tests/bandit.yaml
|
||||
files: ^(homeassistant|script|tests)/.+\.py$
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.11.4
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
|
||||
@@ -406,7 +406,6 @@ def async_enable_logging(
|
||||
if (err_path_exists and os.access(err_log_path, os.W_OK)) or (
|
||||
not err_path_exists and os.access(err_dir, os.W_OK)
|
||||
):
|
||||
|
||||
err_handler: (
|
||||
logging.handlers.RotatingFileHandler
|
||||
| logging.handlers.TimedRotatingFileHandler
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Abode",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/abode",
|
||||
"requirements": ["jaraco.abode==3.2.1"],
|
||||
"requirements": ["jaraco.abode==3.3.0"],
|
||||
"codeowners": ["@shred86"],
|
||||
"homekit": {
|
||||
"models": ["Abode", "Iota"]
|
||||
|
||||
@@ -24,7 +24,6 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
|
||||
# convert title and unique_id to string
|
||||
if config_entry.version == 1:
|
||||
if isinstance(config_entry.unique_id, int):
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry,
|
||||
unique_id=str(config_entry.unique_id),
|
||||
|
||||
@@ -88,7 +88,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
errors["base"] = "cannot_connect"
|
||||
|
||||
else:
|
||||
|
||||
self.hass.config_entries.async_update_entry(
|
||||
self.entry,
|
||||
data={
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "aladdin_connect",
|
||||
"name": "Aladdin Connect",
|
||||
"documentation": "https://www.home-assistant.io/integrations/aladdin_connect",
|
||||
"requirements": ["AIOAladdinConnect==0.1.53"],
|
||||
"requirements": ["AIOAladdinConnect==0.1.55"],
|
||||
"codeowners": ["@mkmer"],
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aladdin_connect"],
|
||||
|
||||
@@ -270,7 +270,6 @@ class Alert(Entity):
|
||||
await self._send_notification_message(message)
|
||||
|
||||
async def _send_notification_message(self, message: Any) -> None:
|
||||
|
||||
if not self._notifiers:
|
||||
return
|
||||
|
||||
|
||||
@@ -103,7 +103,6 @@ class Auth:
|
||||
return dt.utcnow() < preemptive_expire_time
|
||||
|
||||
async def _async_request_new_token(self, lwa_params):
|
||||
|
||||
try:
|
||||
session = aiohttp_client.async_get_clientsession(self.hass)
|
||||
async with async_timeout.timeout(10):
|
||||
|
||||
@@ -193,7 +193,6 @@ def resolve_slot_synonyms(key, request):
|
||||
and "resolutionsPerAuthority" in request["resolutions"]
|
||||
and len(request["resolutions"]["resolutionsPerAuthority"]) >= 1
|
||||
):
|
||||
|
||||
# Extract all of the possible values from each authority with a
|
||||
# successful match
|
||||
possible_values = []
|
||||
|
||||
@@ -274,7 +274,6 @@ class AsusWrtRouter:
|
||||
entity_reg, self._entry.entry_id
|
||||
)
|
||||
for entry in track_entries:
|
||||
|
||||
if entry.domain != TRACKER_DOMAIN:
|
||||
continue
|
||||
device_mac = format_mac(entry.unique_id)
|
||||
|
||||
@@ -78,6 +78,7 @@ SENSOR_DESCRIPTIONS: tuple[SensorValueEntityDescription, ...] = (
|
||||
entity_registry_enabled_default=False,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
icon="mdi:phone-plus",
|
||||
value=lambda x: x.get("calls"),
|
||||
),
|
||||
SensorValueEntityDescription(
|
||||
key="sms",
|
||||
@@ -101,6 +102,7 @@ SENSOR_DESCRIPTIONS: tuple[SensorValueEntityDescription, ...] = (
|
||||
entity_registry_enabled_default=False,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
icon="mdi:phone",
|
||||
value=lambda x: x.get("calls"),
|
||||
),
|
||||
SensorValueEntityDescription(
|
||||
key="other",
|
||||
@@ -108,6 +110,7 @@ SENSOR_DESCRIPTIONS: tuple[SensorValueEntityDescription, ...] = (
|
||||
entity_registry_enabled_default=False,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
icon="mdi:phone",
|
||||
value=lambda x: x.get("calls"),
|
||||
),
|
||||
# Generic sensors
|
||||
SensorValueEntityDescription(
|
||||
|
||||
@@ -112,7 +112,6 @@ class AxisBinarySensor(AxisEventEntity, BinarySensorEntity):
|
||||
self._attr_name = self.device.api.vapix.ports[event.id].name
|
||||
|
||||
elif event.group == EventGroup.MOTION:
|
||||
|
||||
for event_topic, event_data in (
|
||||
(EventTopic.FENCE_GUARD, self.device.api.vapix.fence_guard),
|
||||
(EventTopic.LOITERING_GUARD, self.device.api.vapix.loitering_guard),
|
||||
@@ -120,7 +119,6 @@ class AxisBinarySensor(AxisEventEntity, BinarySensorEntity):
|
||||
(EventTopic.OBJECT_ANALYTICS, self.device.api.vapix.object_analytics),
|
||||
(EventTopic.MOTION_DETECTION_4, self.device.api.vapix.vmd4),
|
||||
):
|
||||
|
||||
if (
|
||||
event.topic_base == event_topic
|
||||
and event_data
|
||||
|
||||
@@ -399,7 +399,6 @@ class BayesianBinarySensor(BinarySensorEntity):
|
||||
|
||||
observations_by_entity: dict[str, list[Observation]] = {}
|
||||
for observation in self._observations:
|
||||
|
||||
if (key := observation.entity_id) is None:
|
||||
continue
|
||||
observations_by_entity.setdefault(key, []).append(observation)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"connectable": false
|
||||
}
|
||||
],
|
||||
"requirements": ["bluemaestro-ble==0.2.1"],
|
||||
"requirements": ["bluemaestro-ble==0.2.3"],
|
||||
"dependencies": ["bluetooth_adapters"],
|
||||
"codeowners": ["@bdraco"],
|
||||
"iot_class": "local_push"
|
||||
|
||||
@@ -396,7 +396,6 @@ class BluesoundPlayer(MediaPlayerEntity):
|
||||
_LOGGER.debug("Calling URL: %s", url)
|
||||
|
||||
try:
|
||||
|
||||
async with async_timeout.timeout(125):
|
||||
response = await self._polling_session.get(
|
||||
url, headers={CONNECTION: KEEP_ALIVE}
|
||||
|
||||
@@ -12,7 +12,6 @@ from home_assistant_bluetooth import BluetoothServiceInfoBleak
|
||||
from homeassistant.util.dt import monotonic_time_coarse
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
from .manager import BluetoothManager
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "bmw_connected_drive",
|
||||
"name": "BMW Connected Drive",
|
||||
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
|
||||
"requirements": ["bimmer_connected==0.12.0"],
|
||||
"requirements": ["bimmer_connected==0.12.1"],
|
||||
"codeowners": ["@gerard33", "@rikroe"],
|
||||
"config_flow": true,
|
||||
"iot_class": "cloud_polling",
|
||||
|
||||
@@ -95,7 +95,6 @@ async def async_setup_entry(
|
||||
session: SHCSession = hass.data[DOMAIN][config_entry.entry_id][DATA_SESSION]
|
||||
|
||||
for switch in session.device_helper.smart_plugs:
|
||||
|
||||
entities.append(
|
||||
SHCSwitch(
|
||||
device=switch,
|
||||
@@ -113,7 +112,6 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
for switch in session.device_helper.light_switches:
|
||||
|
||||
entities.append(
|
||||
SHCSwitch(
|
||||
device=switch,
|
||||
@@ -124,7 +122,6 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
for switch in session.device_helper.smart_plugs_compact:
|
||||
|
||||
entities.append(
|
||||
SHCSwitch(
|
||||
device=switch,
|
||||
@@ -135,7 +132,6 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
for switch in session.device_helper.camera_eyes:
|
||||
|
||||
entities.append(
|
||||
SHCSwitch(
|
||||
device=switch,
|
||||
@@ -146,7 +142,6 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
for switch in session.device_helper.camera_360:
|
||||
|
||||
entities.append(
|
||||
SHCSwitch(
|
||||
device=switch,
|
||||
|
||||
@@ -171,7 +171,7 @@ class BraviaTVCoordinator(DataUpdateCoordinator[None]):
|
||||
async def async_update_volume(self) -> None:
|
||||
"""Update volume information."""
|
||||
volume_info = await self.client.get_volume_info()
|
||||
if volume_level := volume_info.get("volume"):
|
||||
if (volume_level := volume_info.get("volume")) is not None:
|
||||
self.volume_level = volume_level / 100
|
||||
self.volume_muted = volume_info.get("mute", False)
|
||||
self.volume_target = volume_info.get("target")
|
||||
|
||||
@@ -231,7 +231,10 @@ SENSOR_DESCRIPTIONS = {
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
# UV index (-)
|
||||
(BTHomeSensorDeviceClass.UV_INDEX, None,): SensorEntityDescription(
|
||||
(
|
||||
BTHomeSensorDeviceClass.UV_INDEX,
|
||||
None,
|
||||
): SensorEntityDescription(
|
||||
key=f"{BTHomeSensorDeviceClass.UV_INDEX}",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
@@ -256,7 +259,10 @@ SENSOR_DESCRIPTIONS = {
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
# Volume (L)
|
||||
(BTHomeSensorDeviceClass.VOLUME, Units.VOLUME_LITERS,): SensorEntityDescription(
|
||||
(
|
||||
BTHomeSensorDeviceClass.VOLUME,
|
||||
Units.VOLUME_LITERS,
|
||||
): SensorEntityDescription(
|
||||
key=f"{BTHomeSensorDeviceClass.VOLUME}_{Units.VOLUME_LITERS}",
|
||||
device_class=SensorDeviceClass.VOLUME,
|
||||
native_unit_of_measurement=UnitOfVolume.LITERS,
|
||||
|
||||
@@ -735,7 +735,6 @@ class BrSensor(SensorEntity):
|
||||
or sensor_type.endswith("_4d")
|
||||
or sensor_type.endswith("_5d")
|
||||
):
|
||||
|
||||
# update forecasting sensors:
|
||||
fcday = 0
|
||||
if sensor_type.endswith("_2d"):
|
||||
|
||||
@@ -96,7 +96,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle the initial step."""
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
|
||||
hub = Control4Validator(
|
||||
user_input[CONF_HOST],
|
||||
user_input[CONF_USERNAME],
|
||||
|
||||
@@ -403,16 +403,20 @@ class DefaultAgent(AbstractConversationAgent):
|
||||
|
||||
entity = entities.async_get(state.entity_id)
|
||||
if entity is not None:
|
||||
if entity.entity_category:
|
||||
# Skip configuration/diagnostic entities
|
||||
if entity.entity_category or entity.hidden:
|
||||
# Skip configuration/diagnostic/hidden entities
|
||||
continue
|
||||
|
||||
if entity.aliases:
|
||||
for alias in entity.aliases:
|
||||
names.append((alias, state.entity_id, context))
|
||||
|
||||
# Default name
|
||||
names.append((state.name, state.entity_id, context))
|
||||
# Default name
|
||||
names.append((state.name, state.entity_id, context))
|
||||
|
||||
else:
|
||||
# Default name
|
||||
names.append((state.name, state.entity_id, context))
|
||||
|
||||
self._names_list = TextSlotList.from_tuples(names, allow_template=False)
|
||||
return self._names_list
|
||||
|
||||
@@ -188,6 +188,7 @@ class DarkSkyWeather(WeatherEntity):
|
||||
@property
|
||||
def forecast(self):
|
||||
"""Return the forecast array."""
|
||||
|
||||
# Per conversation with Joshua Reyes of Dark Sky, to get the total
|
||||
# forecasted precipitation, you have to multiple the intensity by
|
||||
# the hours for the forecast interval
|
||||
|
||||
@@ -271,7 +271,6 @@ class DeconzBinarySensor(DeconzDevice[SensorResources], BinarySensorEntity):
|
||||
attr[ATTR_TEMPERATURE] = self._device.internal_temperature
|
||||
|
||||
if isinstance(self._device, Presence):
|
||||
|
||||
if self._device.dark is not None:
|
||||
attr[ATTR_DARK] = self._device.dark
|
||||
|
||||
|
||||
@@ -87,7 +87,6 @@ class DeconzFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
If no bridge is found allow user to manually input configuration.
|
||||
"""
|
||||
if user_input is not None:
|
||||
|
||||
if CONF_MANUAL_INPUT == user_input[CONF_HOST]:
|
||||
return await self.async_step_manual_input()
|
||||
|
||||
|
||||
@@ -372,7 +372,6 @@ class DeconzSensor(DeconzDevice[SensorResources], SensorEntity):
|
||||
attr[ATTR_DAYLIGHT] = self._device.daylight
|
||||
|
||||
elif isinstance(self._device, LightLevel):
|
||||
|
||||
if self._device.dark is not None:
|
||||
attr[ATTR_DARK] = self._device.dark
|
||||
|
||||
|
||||
@@ -186,10 +186,8 @@ async def async_remove_orphaned_entries_service(gateway: DeconzGateway) -> None:
|
||||
devices_to_be_removed.remove(event.device_id)
|
||||
|
||||
for entry in entity_entries:
|
||||
|
||||
# Don't remove available entities
|
||||
if entry.unique_id in gateway.entities[entry.domain]:
|
||||
|
||||
# Don't remove devices with available entities
|
||||
if entry.device_id in devices_to_be_removed:
|
||||
devices_to_be_removed.remove(entry.device_id)
|
||||
|
||||
@@ -121,6 +121,6 @@ class DeLijnPublicTransportSensor(SensorEntity):
|
||||
self._attr_extra_state_attributes["next_passages"] = self.line.passages
|
||||
|
||||
self._attr_available = True
|
||||
except (KeyError) as error:
|
||||
except KeyError as error:
|
||||
_LOGGER.error("Invalid data received from De Lijn: %s", error)
|
||||
self._attr_available = False
|
||||
|
||||
@@ -245,7 +245,7 @@ class DerivativeSensor(RestoreEntity, SensorEntity):
|
||||
derivative = new_derivative
|
||||
else:
|
||||
derivative = Decimal(0)
|
||||
for (start, end, value) in self._state_list:
|
||||
for start, end, value in self._state_list:
|
||||
weight = calculate_weight(start, end, new_state.last_updated)
|
||||
derivative = derivative + (value * Decimal(weight))
|
||||
|
||||
|
||||
@@ -214,7 +214,6 @@ async def activate_automation( # noqa: C901
|
||||
elif start_point and start_point < now < get_astral_event_next(
|
||||
hass, SUN_EVENT_SUNSET
|
||||
):
|
||||
|
||||
# Check for every light if it would be on if someone was home
|
||||
# when the fading in started and turn it on if so
|
||||
for index, light_id in enumerate(light_ids):
|
||||
|
||||
@@ -93,7 +93,6 @@ class DnsIPConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
errors = {}
|
||||
|
||||
if user_input:
|
||||
|
||||
hostname = user_input[CONF_HOSTNAME]
|
||||
name = DEFAULT_NAME if hostname == DEFAULT_HOSTNAME else hostname
|
||||
resolver = user_input.get(CONF_RESOLVER, DEFAULT_RESOLVER)
|
||||
|
||||
@@ -255,7 +255,6 @@ class Doods(ImageProcessingEntity):
|
||||
)
|
||||
|
||||
for label, values in matches.items():
|
||||
|
||||
# Draw custom label regions/areas
|
||||
if label in self._label_areas and self._label_areas[label] != [0, 0, 1, 1]:
|
||||
box_label = f"{label.capitalize()} Detection Area"
|
||||
|
||||
@@ -66,7 +66,6 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
server_address = (conf.get(CONF_HOST), conf.get(CONF_PORT))
|
||||
|
||||
try:
|
||||
|
||||
ebusdpy.init(server_address)
|
||||
hass.data[DOMAIN] = EbusdData(server_address, circuit)
|
||||
|
||||
|
||||
@@ -160,7 +160,6 @@ class EcovacsVacuum(VacuumEntity):
|
||||
def set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None:
|
||||
"""Set fan speed."""
|
||||
if self.is_on:
|
||||
|
||||
self.device.run(sucks.Clean(mode=self.device.clean_status, speed=fan_speed))
|
||||
|
||||
def send_command(
|
||||
|
||||
@@ -87,7 +87,7 @@ class SmartPlugSwitch(SwitchEntity):
|
||||
self._state = self._pca.get_state(self._device_id)
|
||||
self._available = True
|
||||
|
||||
except (OSError) as ex:
|
||||
except OSError as ex:
|
||||
if self._available:
|
||||
_LOGGER.warning("Could not read state for %s: %s", self.name, ex)
|
||||
self._available = False
|
||||
|
||||
@@ -106,7 +106,6 @@ def setup_platform(
|
||||
sensors = []
|
||||
|
||||
for elem in data.data:
|
||||
|
||||
if exclude_feeds is not None and int(elem["id"]) in exclude_feeds:
|
||||
continue
|
||||
|
||||
|
||||
@@ -379,7 +379,7 @@ class HueOneLightChangeView(HomeAssistantView):
|
||||
else:
|
||||
parsed[STATE_ON] = entity.state != STATE_OFF
|
||||
|
||||
for (key, attr) in (
|
||||
for key, attr in (
|
||||
(HUE_API_STATE_BRI, STATE_BRIGHTNESS),
|
||||
(HUE_API_STATE_HUE, STATE_HUE),
|
||||
(HUE_API_STATE_SAT, STATE_SATURATION),
|
||||
@@ -587,7 +587,7 @@ class HueOneLightChangeView(HomeAssistantView):
|
||||
)
|
||||
]
|
||||
|
||||
for (key, val) in (
|
||||
for key, val in (
|
||||
(STATE_BRIGHTNESS, HUE_API_STATE_BRI),
|
||||
(STATE_HUE, HUE_API_STATE_HUE),
|
||||
(STATE_SATURATION, HUE_API_STATE_SAT),
|
||||
@@ -634,78 +634,88 @@ def get_entity_state_dict(config: Config, entity: State) -> dict[str, Any]:
|
||||
# Remove the now stale cached entry.
|
||||
config.cached_states.pop(entity.entity_id)
|
||||
|
||||
if cached_state is None:
|
||||
return _build_entity_state_dict(entity)
|
||||
|
||||
data: dict[str, Any] = cached_state
|
||||
# Make sure brightness is valid
|
||||
if data[STATE_BRIGHTNESS] is None:
|
||||
data[STATE_BRIGHTNESS] = HUE_API_STATE_BRI_MAX if data[STATE_ON] else 0
|
||||
|
||||
# Make sure hue/saturation are valid
|
||||
if (data[STATE_HUE] is None) or (data[STATE_SATURATION] is None):
|
||||
data[STATE_HUE] = 0
|
||||
data[STATE_SATURATION] = 0
|
||||
|
||||
# If the light is off, set the color to off
|
||||
if data[STATE_BRIGHTNESS] == 0:
|
||||
data[STATE_HUE] = 0
|
||||
data[STATE_SATURATION] = 0
|
||||
|
||||
_clamp_values(data)
|
||||
return data
|
||||
|
||||
|
||||
@lru_cache(maxsize=512)
|
||||
def _build_entity_state_dict(entity: State) -> dict[str, Any]:
|
||||
"""Build a state dict for an entity."""
|
||||
data: dict[str, Any] = {
|
||||
STATE_ON: False,
|
||||
STATE_ON: entity.state != STATE_OFF,
|
||||
STATE_BRIGHTNESS: None,
|
||||
STATE_HUE: None,
|
||||
STATE_SATURATION: None,
|
||||
STATE_COLOR_TEMP: None,
|
||||
}
|
||||
|
||||
if cached_state is None:
|
||||
data[STATE_ON] = entity.state != STATE_OFF
|
||||
|
||||
if data[STATE_ON]:
|
||||
data[STATE_BRIGHTNESS] = hass_to_hue_brightness(
|
||||
entity.attributes.get(ATTR_BRIGHTNESS, 0)
|
||||
)
|
||||
hue_sat = entity.attributes.get(ATTR_HS_COLOR)
|
||||
if hue_sat is not None:
|
||||
hue = hue_sat[0]
|
||||
sat = hue_sat[1]
|
||||
# Convert hass hs values back to hue hs values
|
||||
data[STATE_HUE] = int((hue / 360.0) * HUE_API_STATE_HUE_MAX)
|
||||
data[STATE_SATURATION] = int((sat / 100.0) * HUE_API_STATE_SAT_MAX)
|
||||
else:
|
||||
data[STATE_HUE] = HUE_API_STATE_HUE_MIN
|
||||
data[STATE_SATURATION] = HUE_API_STATE_SAT_MIN
|
||||
data[STATE_COLOR_TEMP] = entity.attributes.get(ATTR_COLOR_TEMP, 0)
|
||||
|
||||
if data[STATE_ON]:
|
||||
data[STATE_BRIGHTNESS] = hass_to_hue_brightness(
|
||||
entity.attributes.get(ATTR_BRIGHTNESS, 0)
|
||||
)
|
||||
hue_sat = entity.attributes.get(ATTR_HS_COLOR)
|
||||
if hue_sat is not None:
|
||||
hue = hue_sat[0]
|
||||
sat = hue_sat[1]
|
||||
# Convert hass hs values back to hue hs values
|
||||
data[STATE_HUE] = int((hue / 360.0) * HUE_API_STATE_HUE_MAX)
|
||||
data[STATE_SATURATION] = int((sat / 100.0) * HUE_API_STATE_SAT_MAX)
|
||||
else:
|
||||
data[STATE_BRIGHTNESS] = 0
|
||||
data[STATE_HUE] = 0
|
||||
data[STATE_SATURATION] = 0
|
||||
data[STATE_COLOR_TEMP] = 0
|
||||
data[STATE_HUE] = HUE_API_STATE_HUE_MIN
|
||||
data[STATE_SATURATION] = HUE_API_STATE_SAT_MIN
|
||||
data[STATE_COLOR_TEMP] = entity.attributes.get(ATTR_COLOR_TEMP, 0)
|
||||
|
||||
if entity.domain == climate.DOMAIN:
|
||||
temperature = entity.attributes.get(ATTR_TEMPERATURE, 0)
|
||||
# Convert 0-100 to 0-254
|
||||
data[STATE_BRIGHTNESS] = round(temperature * HUE_API_STATE_BRI_MAX / 100)
|
||||
elif entity.domain == humidifier.DOMAIN:
|
||||
humidity = entity.attributes.get(ATTR_HUMIDITY, 0)
|
||||
# Convert 0-100 to 0-254
|
||||
data[STATE_BRIGHTNESS] = round(humidity * HUE_API_STATE_BRI_MAX / 100)
|
||||
elif entity.domain == media_player.DOMAIN:
|
||||
level = entity.attributes.get(
|
||||
ATTR_MEDIA_VOLUME_LEVEL, 1.0 if data[STATE_ON] else 0.0
|
||||
)
|
||||
# Convert 0.0-1.0 to 0-254
|
||||
data[STATE_BRIGHTNESS] = round(min(1.0, level) * HUE_API_STATE_BRI_MAX)
|
||||
elif entity.domain == fan.DOMAIN:
|
||||
percentage = entity.attributes.get(ATTR_PERCENTAGE) or 0
|
||||
# Convert 0-100 to 0-254
|
||||
data[STATE_BRIGHTNESS] = round(percentage * HUE_API_STATE_BRI_MAX / 100)
|
||||
elif entity.domain == cover.DOMAIN:
|
||||
level = entity.attributes.get(ATTR_CURRENT_POSITION, 0)
|
||||
data[STATE_BRIGHTNESS] = round(level / 100 * HUE_API_STATE_BRI_MAX)
|
||||
else:
|
||||
data = cached_state
|
||||
# Make sure brightness is valid
|
||||
if data[STATE_BRIGHTNESS] is None:
|
||||
data[STATE_BRIGHTNESS] = HUE_API_STATE_BRI_MAX if data[STATE_ON] else 0
|
||||
data[STATE_BRIGHTNESS] = 0
|
||||
data[STATE_HUE] = 0
|
||||
data[STATE_SATURATION] = 0
|
||||
data[STATE_COLOR_TEMP] = 0
|
||||
|
||||
# Make sure hue/saturation are valid
|
||||
if (data[STATE_HUE] is None) or (data[STATE_SATURATION] is None):
|
||||
data[STATE_HUE] = 0
|
||||
data[STATE_SATURATION] = 0
|
||||
if entity.domain == climate.DOMAIN:
|
||||
temperature = entity.attributes.get(ATTR_TEMPERATURE, 0)
|
||||
# Convert 0-100 to 0-254
|
||||
data[STATE_BRIGHTNESS] = round(temperature * HUE_API_STATE_BRI_MAX / 100)
|
||||
elif entity.domain == humidifier.DOMAIN:
|
||||
humidity = entity.attributes.get(ATTR_HUMIDITY, 0)
|
||||
# Convert 0-100 to 0-254
|
||||
data[STATE_BRIGHTNESS] = round(humidity * HUE_API_STATE_BRI_MAX / 100)
|
||||
elif entity.domain == media_player.DOMAIN:
|
||||
level = entity.attributes.get(
|
||||
ATTR_MEDIA_VOLUME_LEVEL, 1.0 if data[STATE_ON] else 0.0
|
||||
)
|
||||
# Convert 0.0-1.0 to 0-254
|
||||
data[STATE_BRIGHTNESS] = round(min(1.0, level) * HUE_API_STATE_BRI_MAX)
|
||||
elif entity.domain == fan.DOMAIN:
|
||||
percentage = entity.attributes.get(ATTR_PERCENTAGE) or 0
|
||||
# Convert 0-100 to 0-254
|
||||
data[STATE_BRIGHTNESS] = round(percentage * HUE_API_STATE_BRI_MAX / 100)
|
||||
elif entity.domain == cover.DOMAIN:
|
||||
level = entity.attributes.get(ATTR_CURRENT_POSITION, 0)
|
||||
data[STATE_BRIGHTNESS] = round(level / 100 * HUE_API_STATE_BRI_MAX)
|
||||
_clamp_values(data)
|
||||
return data
|
||||
|
||||
# If the light is off, set the color to off
|
||||
if data[STATE_BRIGHTNESS] == 0:
|
||||
data[STATE_HUE] = 0
|
||||
data[STATE_SATURATION] = 0
|
||||
|
||||
# Clamp brightness, hue, saturation, and color temp to valid values
|
||||
for (key, v_min, v_max) in (
|
||||
def _clamp_values(data: dict[str, Any]) -> None:
|
||||
"""Clamp brightness, hue, saturation, and color temp to valid values."""
|
||||
for key, v_min, v_max in (
|
||||
(STATE_BRIGHTNESS, HUE_API_STATE_BRI_MIN, HUE_API_STATE_BRI_MAX),
|
||||
(STATE_HUE, HUE_API_STATE_HUE_MIN, HUE_API_STATE_HUE_MAX),
|
||||
(STATE_SATURATION, HUE_API_STATE_SAT_MIN, HUE_API_STATE_SAT_MAX),
|
||||
@@ -714,8 +724,6 @@ def get_entity_state_dict(config: Config, entity: State) -> dict[str, Any]:
|
||||
if data[key] is not None:
|
||||
data[key] = max(v_min, min(data[key], v_max))
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def _entity_unique_id(entity_id: str) -> str:
|
||||
@@ -831,6 +839,7 @@ def create_hue_success_response(
|
||||
def create_config_model(config: Config, request: web.Request) -> dict[str, Any]:
|
||||
"""Create a config resource."""
|
||||
return {
|
||||
"name": "HASS BRIDGE",
|
||||
"mac": "00:00:00:00:00:00",
|
||||
"swversion": "01003542",
|
||||
"apiversion": "1.17.0",
|
||||
@@ -842,10 +851,18 @@ def create_config_model(config: Config, request: web.Request) -> dict[str, Any]:
|
||||
|
||||
def create_list_of_entities(config: Config, request: web.Request) -> dict[str, Any]:
|
||||
"""Create a list of all entities."""
|
||||
json_response: dict[str, Any] = {
|
||||
config.entity_id_to_number(state.entity_id): state_to_json(config, state)
|
||||
for state in config.get_exposed_states()
|
||||
}
|
||||
hass: core.HomeAssistant = request.app["hass"]
|
||||
|
||||
json_response: dict[str, Any] = {}
|
||||
for cached_state in config.get_exposed_states():
|
||||
entity_id = cached_state.entity_id
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
|
||||
json_response[config.entity_id_to_number(entity_id)] = state_to_json(
|
||||
config, state
|
||||
)
|
||||
|
||||
return json_response
|
||||
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ SENSORS = (
|
||||
key="seven_days_production",
|
||||
name="Last Seven Days Energy Production",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
@@ -61,7 +61,7 @@ SENSORS = (
|
||||
key="seven_days_consumption",
|
||||
name="Last Seven Days Energy Consumption",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "environment_canada",
|
||||
"name": "Environment Canada",
|
||||
"documentation": "https://www.home-assistant.io/integrations/environment_canada",
|
||||
"requirements": ["env_canada==0.5.27"],
|
||||
"requirements": ["env_canada==0.5.28"],
|
||||
"codeowners": ["@gwww", "@michaeldavie"],
|
||||
"config_flow": true,
|
||||
"iot_class": "cloud_polling",
|
||||
|
||||
@@ -21,7 +21,6 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def _async_has_devices(hass: HomeAssistant) -> bool:
|
||||
|
||||
controller_ready = asyncio.Event()
|
||||
|
||||
@callback
|
||||
|
||||
@@ -66,7 +66,6 @@ async def async_setup_entry(
|
||||
camera_entities = []
|
||||
|
||||
for camera, value in coordinator.data.items():
|
||||
|
||||
camera_rtsp_entry = [
|
||||
item
|
||||
for item in hass.config_entries.async_entries(DOMAIN)
|
||||
@@ -81,7 +80,6 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
if camera_rtsp_entry:
|
||||
|
||||
ffmpeg_arguments = camera_rtsp_entry[0].options[CONF_FFMPEG_ARGUMENTS]
|
||||
camera_username = camera_rtsp_entry[0].data[CONF_USERNAME]
|
||||
camera_password = camera_rtsp_entry[0].data[CONF_PASSWORD]
|
||||
@@ -96,7 +94,6 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
else:
|
||||
|
||||
discovery_flow.async_create_flow(
|
||||
hass,
|
||||
DOMAIN,
|
||||
|
||||
@@ -182,7 +182,6 @@ class EzvizConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
errors = {}
|
||||
|
||||
if user_input is not None:
|
||||
|
||||
if user_input[CONF_URL] == CONF_CUSTOMIZE:
|
||||
self.context["data"] = {
|
||||
CONF_USERNAME: user_input[CONF_USERNAME],
|
||||
|
||||
@@ -25,7 +25,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle the initial step."""
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
|
||||
await self.async_set_unique_id(user_input[CONF_ID])
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ SENSOR_TYPES = (
|
||||
icon=ICON,
|
||||
name="Last Updated",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -507,7 +507,6 @@ class RangeFilter(Filter, SensorEntity):
|
||||
new_state_value = cast(float, new_state.state)
|
||||
|
||||
if self._upper_bound is not None and new_state_value > self._upper_bound:
|
||||
|
||||
self._stats_internal["erasures_up"] += 1
|
||||
|
||||
_LOGGER.debug(
|
||||
@@ -519,7 +518,6 @@ class RangeFilter(Filter, SensorEntity):
|
||||
new_state.state = self._upper_bound
|
||||
|
||||
elif self._lower_bound is not None and new_state_value < self._lower_bound:
|
||||
|
||||
self._stats_internal["erasures_low"] += 1
|
||||
|
||||
_LOGGER.debug(
|
||||
@@ -564,7 +562,6 @@ class OutlierFilter(Filter, SensorEntity):
|
||||
len(self.states) == self.states.maxlen
|
||||
and abs(new_state_value - median) > self._radius
|
||||
):
|
||||
|
||||
self._stats_internal["erasures"] += 1
|
||||
|
||||
_LOGGER.debug(
|
||||
|
||||
@@ -208,7 +208,6 @@ class FinTsClient:
|
||||
holdings_accounts = []
|
||||
|
||||
for account in self.client.get_sepa_accounts():
|
||||
|
||||
if self.is_balance_account(account):
|
||||
balance_accounts.append(account)
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
|
||||
_LOGGER.debug("Closing Firmata board %s", config_entry.data[CONF_NAME])
|
||||
|
||||
unload_entries = []
|
||||
for (conf, platform) in CONF_PLATFORM_MAP.items():
|
||||
for conf, platform in CONF_PLATFORM_MAP.items():
|
||||
if conf in config_entry.data:
|
||||
unload_entries.append(
|
||||
hass.config_entries.async_forward_entry_unload(config_entry, platform)
|
||||
|
||||
@@ -86,7 +86,6 @@ class FleetGoDeviceScanner:
|
||||
|
||||
for device in devices:
|
||||
if not self._include or device.license_plate in self._include:
|
||||
|
||||
if device.active or device.current_address is None:
|
||||
device.get_map_details()
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ class FliprDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
data = await self.hass.async_add_executor_job(
|
||||
self.client.get_pool_measure_latest, self.flipr_id
|
||||
)
|
||||
except (FliprError) as error:
|
||||
except FliprError as error:
|
||||
raise UpdateFailed(error) from error
|
||||
|
||||
return data
|
||||
|
||||
@@ -43,7 +43,7 @@ class FloDeviceDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
await self.send_presence_ping()
|
||||
await self._update_device()
|
||||
await self._update_consumption_data()
|
||||
except (RequestError) as error:
|
||||
except RequestError as error:
|
||||
raise UpdateFailed(error) from error
|
||||
|
||||
@property
|
||||
|
||||
@@ -97,7 +97,6 @@ async def async_setup_entry(
|
||||
]
|
||||
flume_entity_list = []
|
||||
for device in flume_devices:
|
||||
|
||||
device_id = device[KEY_DEVICE_ID]
|
||||
device_timezone = device[KEY_DEVICE_LOCATION][KEY_DEVICE_LOCATION_TIMEZONE]
|
||||
device_location_name = device[KEY_DEVICE_LOCATION][KEY_DEVICE_LOCATION_NAME]
|
||||
|
||||
@@ -102,7 +102,6 @@ async def _async_port_entities_list(
|
||||
_LOGGER.debug("IP source for %s is %s", avm_wrapper.host, local_ip)
|
||||
|
||||
for i in range(port_forwards_count):
|
||||
|
||||
portmap = await avm_wrapper.async_get_port_mapping(
|
||||
avm_wrapper.device_conn_type, i
|
||||
)
|
||||
@@ -406,7 +405,6 @@ class FritzBoxPortSwitch(FritzBoxBaseSwitch, SwitchEntity):
|
||||
self._attributes[attr] = self.port_mapping[key]
|
||||
|
||||
async def _async_switch_on_off_executor(self, turn_on: bool) -> bool:
|
||||
|
||||
if self.port_mapping is None:
|
||||
return False
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "frontend",
|
||||
"name": "Home Assistant Frontend",
|
||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||
"requirements": ["home-assistant-frontend==20230130.0"],
|
||||
"requirements": ["home-assistant-frontend==20230202.0"],
|
||||
"dependencies": [
|
||||
"api",
|
||||
"auth",
|
||||
|
||||
@@ -262,7 +262,6 @@ async def async_setup_entry(
|
||||
if entity_id := ent_reg.async_get_entity_id(
|
||||
Platform.SENSOR, DOMAIN, old_unique_id
|
||||
):
|
||||
|
||||
ent_reg.async_update_entity(
|
||||
entity_id, new_unique_id=f"{config_entry.entry_id}-{new_key}"
|
||||
)
|
||||
|
||||
@@ -289,7 +289,6 @@ class AbstractConfig(ABC):
|
||||
return
|
||||
|
||||
for user_agent_id, _ in self._store.agent_user_ids.items():
|
||||
|
||||
if (webhook_id := self.get_local_webhook_id(user_agent_id)) is None:
|
||||
setup_successful = False
|
||||
break
|
||||
|
||||
@@ -244,7 +244,6 @@ class SensorGroup(GroupEntity, SensorEntity):
|
||||
|
||||
_attr_available = False
|
||||
_attr_should_poll = False
|
||||
_attr_icon = "mdi:calculator"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -352,6 +351,16 @@ class SensorGroup(GroupEntity, SensorEntity):
|
||||
return self._attr_device_class
|
||||
return self.calc_device_class
|
||||
|
||||
@property
|
||||
def icon(self) -> str | None:
|
||||
"""Return the icon.
|
||||
|
||||
Only override the icon if the device class is not set.
|
||||
"""
|
||||
if not self.device_class:
|
||||
return "mdi:calculator"
|
||||
return None
|
||||
|
||||
@property
|
||||
def state_class(self) -> SensorStateClass | str | None:
|
||||
"""Return state class."""
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
},
|
||||
"sensor": {
|
||||
"title": "[%key:component::group::config::step::user::title%]",
|
||||
"description": "If \"ignore non-numeric\" is enabled, the group's state is calculated if at least one member has a numerical value. If \"ignore non-numeric\" is disabled, the group's state is calculated only if all group members have numerical values.",
|
||||
"data": {
|
||||
"ignore_non_numeric": "Ignore non-numeric",
|
||||
"entities": "Members",
|
||||
@@ -134,7 +133,7 @@
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"description": "[%key:component::group::config::step::sensor::description%]",
|
||||
"description": "If \"ignore non-numeric\" is enabled, the group's state is calculated if at least one member has a numerical value. If \"ignore non-numeric\" is disabled, the group's state is calculated only if all group members have numerical values.",
|
||||
"data": {
|
||||
"ignore_non_numeric": "[%key:component::group::config::step::sensor::data::ignore_non_numeric%]",
|
||||
"entities": "[%key:component::group::config::step::sensor::data::entities%]",
|
||||
|
||||
@@ -63,7 +63,6 @@
|
||||
"type": "Type",
|
||||
"unit_of_measurement": "Unit of Measurement"
|
||||
},
|
||||
"description": "If \"ignore non-numeric\" is enabled, the group's state is calculated if at least one member has a numerical value. If \"ignore non-numeric\" is disabled, the group's state is calculated only if all group members have numerical values.",
|
||||
"title": "Add Group"
|
||||
},
|
||||
"switch": {
|
||||
|
||||
@@ -64,7 +64,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle the initial step."""
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
|
||||
try:
|
||||
validated = await validate_input(user_input)
|
||||
except CannotConnect:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "HomeKit Controller",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
|
||||
"requirements": ["aiohomekit==2.4.4"],
|
||||
"requirements": ["aiohomekit==2.4.6"],
|
||||
"zeroconf": ["_hap._tcp.local.", "_hap._udp.local."],
|
||||
"bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }],
|
||||
"dependencies": ["bluetooth_adapters", "zeroconf"],
|
||||
|
||||
@@ -316,7 +316,12 @@ class HomematicipHeatingGroup(HomematicipGenericEntity, ClimateEntity):
|
||||
@property
|
||||
def _first_radiator_thermostat(
|
||||
self,
|
||||
) -> AsyncHeatingThermostat | AsyncHeatingThermostatCompact | AsyncHeatingThermostatEvo | None:
|
||||
) -> (
|
||||
AsyncHeatingThermostat
|
||||
| AsyncHeatingThermostatCompact
|
||||
| AsyncHeatingThermostatEvo
|
||||
| None
|
||||
):
|
||||
"""Return the first radiator thermostat from the hmip heating group."""
|
||||
for device in self._device.devices:
|
||||
if isinstance(
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
import asyncio
|
||||
from dataclasses import dataclass
|
||||
|
||||
import AIOSomecomfort
|
||||
import aiosomecomfort
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import (
|
||||
@@ -50,22 +50,19 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
username = config_entry.data[CONF_USERNAME]
|
||||
password = config_entry.data[CONF_PASSWORD]
|
||||
|
||||
client = AIOSomecomfort.AIOSomeComfort(
|
||||
client = aiosomecomfort.AIOSomeComfort(
|
||||
username, password, session=async_get_clientsession(hass)
|
||||
)
|
||||
try:
|
||||
await client.login()
|
||||
await client.discover()
|
||||
|
||||
except AIOSomecomfort.AuthError as ex:
|
||||
raise ConfigEntryNotReady(
|
||||
"Failed to initialize the Honeywell client: "
|
||||
"Check your configuration (username, password), "
|
||||
) from ex
|
||||
except aiosomecomfort.device.AuthError as ex:
|
||||
raise ConfigEntryAuthFailed("Incorrect Password") from ex
|
||||
|
||||
except (
|
||||
AIOSomecomfort.ConnectionError,
|
||||
AIOSomecomfort.ConnectionTimeout,
|
||||
aiosomecomfort.device.ConnectionError,
|
||||
aiosomecomfort.device.ConnectionTimeout,
|
||||
asyncio.TimeoutError,
|
||||
) as ex:
|
||||
raise ConfigEntryNotReady(
|
||||
@@ -117,5 +114,5 @@ class HoneywellData:
|
||||
"""Shared data for Honeywell."""
|
||||
|
||||
entry_id: str
|
||||
client: AIOSomecomfort.AIOSomeComfort
|
||||
devices: dict[str, AIOSomecomfort.device.Device]
|
||||
client: aiosomecomfort.AIOSomeComfort
|
||||
devices: dict[str, aiosomecomfort.device.Device]
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
import datetime
|
||||
from typing import Any
|
||||
|
||||
import AIOSomecomfort
|
||||
import aiosomecomfort
|
||||
|
||||
from homeassistant.components.climate import (
|
||||
ATTR_TARGET_TEMP_HIGH,
|
||||
@@ -100,7 +100,7 @@ class HoneywellUSThermostat(ClimateEntity):
|
||||
def __init__(
|
||||
self,
|
||||
data: HoneywellData,
|
||||
device: AIOSomecomfort.device.Device,
|
||||
device: aiosomecomfort.device.Device,
|
||||
cool_away_temp: int | None,
|
||||
heat_away_temp: int | None,
|
||||
) -> None:
|
||||
@@ -295,7 +295,7 @@ class HoneywellUSThermostat(ClimateEntity):
|
||||
if mode == "heat":
|
||||
await self._device.set_setpoint_heat(temperature)
|
||||
|
||||
except AIOSomecomfort.SomeComfortError as err:
|
||||
except aiosomecomfort.SomeComfortError as err:
|
||||
_LOGGER.error("Invalid temperature %.1f: %s", temperature, err)
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
@@ -308,7 +308,7 @@ class HoneywellUSThermostat(ClimateEntity):
|
||||
if temperature := kwargs.get(ATTR_TARGET_TEMP_LOW):
|
||||
await self._device.set_setpoint_heat(temperature)
|
||||
|
||||
except AIOSomecomfort.SomeComfortError as err:
|
||||
except aiosomecomfort.SomeComfortError as err:
|
||||
_LOGGER.error("Invalid temperature %.1f: %s", temperature, err)
|
||||
|
||||
async def async_set_fan_mode(self, fan_mode: str) -> None:
|
||||
@@ -330,11 +330,10 @@ class HoneywellUSThermostat(ClimateEntity):
|
||||
try:
|
||||
# Get current mode
|
||||
mode = self._device.system_mode
|
||||
except AIOSomecomfort.SomeComfortError:
|
||||
except aiosomecomfort.SomeComfortError:
|
||||
_LOGGER.error("Can not get system mode")
|
||||
return
|
||||
try:
|
||||
|
||||
# Set permanent hold
|
||||
# and Set temperature
|
||||
if mode in COOLING_MODES:
|
||||
@@ -344,8 +343,7 @@ class HoneywellUSThermostat(ClimateEntity):
|
||||
await self._device.set_hold_heat(True)
|
||||
await self._device.set_setpoint_heat(self._heat_away_temp)
|
||||
|
||||
except AIOSomecomfort.SomeComfortError:
|
||||
|
||||
except aiosomecomfort.SomeComfortError:
|
||||
_LOGGER.error(
|
||||
"Temperature out of range. Mode: %s, Heat Temperature: %.1f, Cool Temperature: %.1f",
|
||||
mode,
|
||||
@@ -358,7 +356,7 @@ class HoneywellUSThermostat(ClimateEntity):
|
||||
try:
|
||||
# Get current mode
|
||||
mode = self._device.system_mode
|
||||
except AIOSomecomfort.SomeComfortError:
|
||||
except aiosomecomfort.SomeComfortError:
|
||||
_LOGGER.error("Can not get system mode")
|
||||
return
|
||||
# Check that we got a valid mode back
|
||||
@@ -370,7 +368,7 @@ class HoneywellUSThermostat(ClimateEntity):
|
||||
if mode in HEATING_MODES:
|
||||
await self._device.set_hold_heat(True)
|
||||
|
||||
except AIOSomecomfort.SomeComfortError:
|
||||
except aiosomecomfort.SomeComfortError:
|
||||
_LOGGER.error("Couldn't set permanent hold")
|
||||
else:
|
||||
_LOGGER.error("Invalid system mode returned: %s", mode)
|
||||
@@ -382,7 +380,7 @@ class HoneywellUSThermostat(ClimateEntity):
|
||||
# Disabling all hold modes
|
||||
await self._device.set_hold_cool(False)
|
||||
await self._device.set_hold_heat(False)
|
||||
except AIOSomecomfort.SomeComfortError:
|
||||
except aiosomecomfort.SomeComfortError:
|
||||
_LOGGER.error("Can not stop hold mode")
|
||||
|
||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
@@ -411,13 +409,13 @@ class HoneywellUSThermostat(ClimateEntity):
|
||||
try:
|
||||
await self._device.refresh()
|
||||
except (
|
||||
AIOSomecomfort.SomeComfortError,
|
||||
aiosomecomfort.SomeComfortError,
|
||||
OSError,
|
||||
):
|
||||
try:
|
||||
await self._data.client.login()
|
||||
|
||||
except AIOSomecomfort.SomeComfortError:
|
||||
except aiosomecomfort.SomeComfortError:
|
||||
self._attr_available = False
|
||||
await self.hass.async_create_task(
|
||||
self.hass.config_entries.async_reload(self._data.entry_id)
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
|
||||
import AIOSomecomfort
|
||||
import aiosomecomfort
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
@@ -20,11 +22,66 @@ from .const import (
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
REAUTH_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): str})
|
||||
|
||||
|
||||
class HoneywellConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a honeywell config flow."""
|
||||
|
||||
VERSION = 1
|
||||
entry: config_entries.ConfigEntry | None
|
||||
|
||||
async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult:
|
||||
"""Handle re-authentication with Honeywell."""
|
||||
|
||||
self.entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
|
||||
return await self.async_step_reauth_confirm()
|
||||
|
||||
async def async_step_reauth_confirm(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Confirm re-authentication with Honeywell."""
|
||||
errors: dict[str, str] = {}
|
||||
|
||||
if user_input:
|
||||
assert self.entry is not None
|
||||
password = user_input[CONF_PASSWORD]
|
||||
data = {
|
||||
CONF_USERNAME: self.entry.data[CONF_USERNAME],
|
||||
CONF_PASSWORD: password,
|
||||
}
|
||||
|
||||
try:
|
||||
await self.is_valid(
|
||||
username=data[CONF_USERNAME], password=data[CONF_PASSWORD]
|
||||
)
|
||||
|
||||
except aiosomecomfort.AuthError:
|
||||
errors["base"] = "invalid_auth"
|
||||
|
||||
except (
|
||||
aiosomecomfort.ConnectionError,
|
||||
aiosomecomfort.ConnectionTimeout,
|
||||
asyncio.TimeoutError,
|
||||
):
|
||||
errors["base"] = "cannot_connect"
|
||||
|
||||
else:
|
||||
self.hass.config_entries.async_update_entry(
|
||||
self.entry,
|
||||
data={
|
||||
**self.entry.data,
|
||||
CONF_PASSWORD: password,
|
||||
},
|
||||
)
|
||||
await self.hass.config_entries.async_reload(self.entry.entry_id)
|
||||
return self.async_abort(reason="reauth_successful")
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="reauth_confirm",
|
||||
data_schema=REAUTH_SCHEMA,
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_user(self, user_input=None) -> FlowResult:
|
||||
"""Create config entry. Show the setup form to the user."""
|
||||
@@ -32,11 +89,11 @@ class HoneywellConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
if user_input is not None:
|
||||
try:
|
||||
await self.is_valid(**user_input)
|
||||
except AIOSomecomfort.AuthError:
|
||||
except aiosomecomfort.AuthError:
|
||||
errors["base"] = "invalid_auth"
|
||||
except (
|
||||
AIOSomecomfort.ConnectionError,
|
||||
AIOSomecomfort.ConnectionTimeout,
|
||||
aiosomecomfort.ConnectionError,
|
||||
aiosomecomfort.ConnectionTimeout,
|
||||
asyncio.TimeoutError,
|
||||
):
|
||||
errors["base"] = "cannot_connect"
|
||||
@@ -57,7 +114,7 @@ class HoneywellConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
async def is_valid(self, **kwargs) -> bool:
|
||||
"""Check if login credentials are valid."""
|
||||
client = AIOSomecomfort.AIOSomeComfort(
|
||||
client = aiosomecomfort.AIOSomeComfort(
|
||||
kwargs[CONF_USERNAME],
|
||||
kwargs[CONF_PASSWORD],
|
||||
session=async_get_clientsession(self.hass),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Honeywell Total Connect Comfort (US)",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/honeywell",
|
||||
"requirements": ["aiosomecomfort==0.0.3"],
|
||||
"requirements": ["aiosomecomfort==0.0.6"],
|
||||
"codeowners": ["@rdfurman", "@mkmer"],
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["somecomfort"]
|
||||
|
||||
@@ -5,7 +5,7 @@ from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from AIOSomecomfort.device import Device
|
||||
from aiosomecomfort.device import Device
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
|
||||
@@ -92,7 +92,6 @@ async def handle_v2_migration(hass: core.HomeAssistant, entry: ConfigEntry) -> N
|
||||
|
||||
# initialize bridge connection just for the migration
|
||||
async with HueBridgeV2(host, api_key) as api:
|
||||
|
||||
sensor_class_mapping = {
|
||||
SensorDeviceClass.BATTERY.value: ResourceTypes.DEVICE_POWER,
|
||||
BinarySensorDeviceClass.MOTION.value: ResourceTypes.MOTION,
|
||||
@@ -130,7 +129,6 @@ async def handle_v2_migration(hass: core.HomeAssistant, entry: ConfigEntry) -> N
|
||||
|
||||
# loop through all entities for device and find match
|
||||
for ent in async_entries_for_device(ent_reg, hass_dev_id, True):
|
||||
|
||||
if ent.entity_id.startswith("light"):
|
||||
# migrate light
|
||||
# should always return one lightid here
|
||||
|
||||
@@ -48,7 +48,6 @@ async def async_setup_entry(
|
||||
|
||||
for partial_station in station_information.get("partialStations", []):
|
||||
for elevator in partial_station.get("elevators", []):
|
||||
|
||||
state = elevator.get("state") != "READY"
|
||||
available = elevator.get("state") != "UNKNOWN"
|
||||
label = elevator.get("label")
|
||||
|
||||
@@ -81,7 +81,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
async def async_step_station(self, user_input=None):
|
||||
"""Handle the step where the user inputs his/her station."""
|
||||
if user_input is not None:
|
||||
|
||||
errors = {}
|
||||
|
||||
check_name = await self.hub.gti.checkName(
|
||||
@@ -145,7 +144,6 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||
"""Manage the options."""
|
||||
errors = {}
|
||||
if not self.departure_filters:
|
||||
|
||||
departure_list = {}
|
||||
hub: GTIHub = self.hass.data[DOMAIN][self.config_entry.entry_id]
|
||||
|
||||
@@ -172,7 +170,6 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||
}
|
||||
|
||||
if user_input is not None and not errors:
|
||||
|
||||
options = {
|
||||
CONF_FILTER: [
|
||||
self.departure_filters[x] for x in user_input[CONF_FILTER]
|
||||
|
||||
@@ -259,7 +259,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
for device_entry in dr.async_entries_for_config_entry(
|
||||
device_registry, entry.entry_id
|
||||
):
|
||||
for (kind, key) in device_entry.identifiers:
|
||||
for kind, key in device_entry.identifiers:
|
||||
if kind == DOMAIN and key in known_devices:
|
||||
break
|
||||
else:
|
||||
|
||||
@@ -77,7 +77,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
target_keys[target] = api_keys[target]
|
||||
|
||||
try:
|
||||
|
||||
for target, key in target_keys.items():
|
||||
res = pyfttt.send_event(key, event, value1, value2, value3)
|
||||
if res.status_code != HTTPStatus.OK:
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
{ "local_name": "xBBQ*", "connectable": false },
|
||||
{ "local_name": "tps", "connectable": false }
|
||||
],
|
||||
"requirements": ["inkbird-ble==0.5.5"],
|
||||
"requirements": ["inkbird-ble==0.5.6"],
|
||||
"dependencies": ["bluetooth_adapters"],
|
||||
"codeowners": ["@bdraco"],
|
||||
"iot_class": "local_push"
|
||||
|
||||
@@ -119,7 +119,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
)
|
||||
|
||||
if user_input is not None:
|
||||
|
||||
control_schema = vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
|
||||
@@ -33,7 +33,6 @@ class IntellifireDataUpdateCoordinator(DataUpdateCoordinator[IntellifirePollData
|
||||
self._api = api
|
||||
|
||||
async def _async_update_data(self) -> IntellifirePollData:
|
||||
|
||||
if not self._api.is_polling_in_background:
|
||||
LOGGER.info("Starting Intellifire Background Polling Loop")
|
||||
await self._api.start_background_polling()
|
||||
|
||||
@@ -7,6 +7,7 @@ from urllib.parse import urlparse
|
||||
from aiohttp import CookieJar
|
||||
import async_timeout
|
||||
from pyisy import ISY, ISYConnectionError, ISYInvalidAuthError, ISYResponseParseError
|
||||
from pyisy.constants import CONFIG_NETWORKING, CONFIG_PORTAL
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
@@ -43,7 +44,6 @@ from .const import (
|
||||
ISY_CONF_FIRMWARE,
|
||||
ISY_CONF_MODEL,
|
||||
ISY_CONF_NAME,
|
||||
ISY_CONF_NETWORKING,
|
||||
MANUFACTURER,
|
||||
PLATFORMS,
|
||||
SCHEME_HTTP,
|
||||
@@ -220,9 +220,11 @@ async def async_setup_entry(
|
||||
numbers = isy_data.variables[Platform.NUMBER]
|
||||
for vtype, _, vid in isy.variables.children:
|
||||
numbers.append(isy.variables[vtype][vid])
|
||||
if isy.conf[ISY_CONF_NETWORKING]:
|
||||
if (
|
||||
isy.conf[CONFIG_NETWORKING] or isy.conf[CONFIG_PORTAL]
|
||||
) and isy.networking.nobjs:
|
||||
isy_data.devices[CONF_NETWORK] = _create_service_device_info(
|
||||
isy, name=ISY_CONF_NETWORKING, unique_id=CONF_NETWORK
|
||||
isy, name=CONFIG_NETWORKING, unique_id=CONF_NETWORK
|
||||
)
|
||||
for resource in isy.networking.nobjs:
|
||||
isy_data.net_resources.append(resource)
|
||||
|
||||
@@ -95,7 +95,7 @@ async def async_setup_entry(
|
||||
entities_by_address[node.address] = entity
|
||||
|
||||
# Handle some special child node cases for Insteon Devices
|
||||
for (node, device_class, device_type, device_info) in child_nodes:
|
||||
for node, device_class, device_type, device_info in child_nodes:
|
||||
subnode_id = int(node.address.split(" ")[-1], 16)
|
||||
# Handle Insteon Thermostats
|
||||
if device_type is not None and device_type.startswith(TYPE_CATEGORY_CLIMATE):
|
||||
|
||||
@@ -118,7 +118,6 @@ SUPPORTED_BIN_SENS_CLASSES = ["moisture", "opening", "motion", "climate"]
|
||||
# (they can turn off, and report their state)
|
||||
ISY_GROUP_PLATFORM = Platform.SWITCH
|
||||
|
||||
ISY_CONF_NETWORKING = "Networking Module"
|
||||
ISY_CONF_UUID = "uuid"
|
||||
ISY_CONF_NAME = "name"
|
||||
ISY_CONF_MODEL = "model"
|
||||
@@ -255,7 +254,7 @@ NODE_FILTERS: dict[Platform, dict[str, list[str]]] = {
|
||||
FILTER_STATES: ["open", "closed", "closing", "opening", "stopped"],
|
||||
FILTER_NODE_DEF_ID: ["DimmerMotorSwitch_ADV"],
|
||||
FILTER_INSTEON_TYPE: [TYPE_CATEGORY_COVER],
|
||||
FILTER_ZWAVE_CAT: [],
|
||||
FILTER_ZWAVE_CAT: ["106", "107"],
|
||||
},
|
||||
Platform.LIGHT: {
|
||||
FILTER_UOM: ["51"],
|
||||
|
||||
@@ -109,7 +109,6 @@ def _check_for_insteon_type(
|
||||
device_type.startswith(t)
|
||||
for t in set(NODE_FILTERS[platform][FILTER_INSTEON_TYPE])
|
||||
):
|
||||
|
||||
# Hacky special-cases for certain devices with different platforms
|
||||
# included as subnodes. Note that special-cases are not necessary
|
||||
# on ISY 5.x firmware as it uses the superior NodeDefs method
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Universal Devices ISY/IoX",
|
||||
"integration_type": "hub",
|
||||
"documentation": "https://www.home-assistant.io/integrations/isy994",
|
||||
"requirements": ["pyisy==3.1.11"],
|
||||
"requirements": ["pyisy==3.1.13"],
|
||||
"codeowners": ["@bdraco", "@shbatm"],
|
||||
"config_flow": true,
|
||||
"ssdp": [
|
||||
|
||||
@@ -23,7 +23,7 @@ import homeassistant.helpers.entity_registry as er
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
from homeassistant.helpers.service import entity_service_call
|
||||
|
||||
from .const import _LOGGER, CONF_NETWORK, DOMAIN, ISY_CONF_NAME, ISY_CONF_NETWORKING
|
||||
from .const import _LOGGER, CONF_NETWORK, DOMAIN, ISY_CONF_NAME
|
||||
from .util import _async_cleanup_registry_entries
|
||||
|
||||
# Common Services for All Platforms:
|
||||
@@ -233,7 +233,7 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
|
||||
isy = isy_data.root
|
||||
if isy_name and isy_name != isy.conf[ISY_CONF_NAME]:
|
||||
continue
|
||||
if isy.networking is None or not isy.conf[ISY_CONF_NETWORKING]:
|
||||
if isy.networking is None:
|
||||
continue
|
||||
command = None
|
||||
if address:
|
||||
|
||||
@@ -173,6 +173,7 @@ class ControllerDevice(ClimateEntity):
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Call on adding to hass."""
|
||||
|
||||
# Register for connect/disconnect/update events
|
||||
@callback
|
||||
def controller_disconnected(ctrl: Controller, ex: Exception) -> None:
|
||||
@@ -290,7 +291,7 @@ class ControllerDevice(ClimateEntity):
|
||||
return HVACMode.OFF
|
||||
if (mode := self._controller.mode) == Controller.Mode.FREE_AIR:
|
||||
return HVACMode.FAN_ONLY
|
||||
for (key, value) in self._state_to_pizone.items():
|
||||
for key, value in self._state_to_pizone.items():
|
||||
if value == mode:
|
||||
return key
|
||||
assert False, "Should be unreachable"
|
||||
@@ -527,7 +528,7 @@ class ZoneDevice(ClimateEntity):
|
||||
def hvac_mode(self) -> HVACMode | None:
|
||||
"""Return current operation ie. heat, cool, idle."""
|
||||
mode = self._zone.mode
|
||||
for (key, value) in self._state_to_pizone.items():
|
||||
for key, value in self._state_to_pizone.items():
|
||||
if value == mode:
|
||||
return key
|
||||
return None
|
||||
|
||||
@@ -17,7 +17,6 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def _async_has_devices(hass: HomeAssistant) -> bool:
|
||||
|
||||
controller_ready = asyncio.Event()
|
||||
|
||||
@callback
|
||||
|
||||
@@ -46,7 +46,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle the initial step."""
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
|
||||
await self.async_set_unique_id(user_input[CONF_ACCESS_TOKEN])
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class DemoFixFlow(RepairsFlow):
|
||||
) -> data_entry_flow.FlowResult:
|
||||
"""Handle the first step of a fix flow."""
|
||||
|
||||
return await (self.async_step_confirm())
|
||||
return await self.async_step_confirm()
|
||||
|
||||
async def async_step_confirm(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
|
||||
@@ -62,7 +62,6 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
|
||||
|
||||
# Removing domain name and config entry id from entity unique id's, replacing it with device number
|
||||
if config_entry.version == 1:
|
||||
|
||||
config_entry.version = 2
|
||||
|
||||
device_number = config_entry.data["device_number"]
|
||||
|
||||
@@ -6,7 +6,19 @@
|
||||
"requirements": ["bluetooth-data-tools==0.3.1", "ld2410-ble==0.1.1"],
|
||||
"dependencies": ["bluetooth_adapters"],
|
||||
"codeowners": ["@930913"],
|
||||
"bluetooth": [{ "local_name": "HLK-LD2410B_*" }],
|
||||
"bluetooth": [
|
||||
{
|
||||
"local_name": "HLK-LD2410B_*"
|
||||
},
|
||||
{
|
||||
"local_name": "HLK-LD2410_*"
|
||||
},
|
||||
{
|
||||
"manufacturer_id": 256,
|
||||
"manufacturer_data_start": [7, 1],
|
||||
"service_uuid": "0000af30-0000-1000-8000-00805f9b34fb"
|
||||
}
|
||||
],
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push"
|
||||
}
|
||||
|
||||
@@ -296,7 +296,6 @@ class LIFXManager:
|
||||
)
|
||||
|
||||
elif service == SERVICE_EFFECT_MORPH:
|
||||
|
||||
theme_name = kwargs.get(ATTR_THEME, "exciting")
|
||||
palette = kwargs.get(ATTR_PALETTE, None)
|
||||
|
||||
@@ -336,7 +335,6 @@ class LIFXManager:
|
||||
)
|
||||
|
||||
elif service == SERVICE_EFFECT_PULSE:
|
||||
|
||||
effect = aiolifx_effects.EffectPulse(
|
||||
power_on=kwargs.get(ATTR_POWER_ON),
|
||||
period=kwargs.get(ATTR_PERIOD),
|
||||
@@ -347,7 +345,6 @@ class LIFXManager:
|
||||
await self.effects_conductor.start(effect, bulbs)
|
||||
|
||||
elif service == SERVICE_EFFECT_COLORLOOP:
|
||||
|
||||
brightness = None
|
||||
saturation_max = None
|
||||
saturation_min = None
|
||||
@@ -378,7 +375,6 @@ class LIFXManager:
|
||||
await self.effects_conductor.start(effect, bulbs)
|
||||
|
||||
elif service == SERVICE_EFFECT_STOP:
|
||||
|
||||
await self.effects_conductor.stop(bulbs)
|
||||
|
||||
for coordinator in coordinators:
|
||||
|
||||
@@ -218,7 +218,6 @@ def parse_api_response(response):
|
||||
for authority in AUTHORITIES:
|
||||
for entry in response["HourlyAirQualityIndex"]["LocalAuthority"]:
|
||||
if entry["@LocalAuthorityName"] == authority:
|
||||
|
||||
if isinstance(entry["Site"], dict):
|
||||
entry_sites_data = [entry["Site"]]
|
||||
else:
|
||||
|
||||
@@ -157,7 +157,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return
|
||||
|
||||
if change_type == collection.CHANGE_ADDED:
|
||||
|
||||
existing = hass.data[DOMAIN]["dashboards"].get(url_path)
|
||||
|
||||
if existing:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "lupusec",
|
||||
"name": "Lupus Electronics LUPUSEC",
|
||||
"documentation": "https://www.home-assistant.io/integrations/lupusec",
|
||||
"requirements": ["lupupy==0.2.5"],
|
||||
"requirements": ["lupupy==0.2.7"],
|
||||
"codeowners": ["@majuss"],
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["lupupy"]
|
||||
|
||||
@@ -31,7 +31,6 @@ def setup_platform(
|
||||
devices = []
|
||||
|
||||
for device in data.lupusec.get_devices(generic_type=CONST.TYPE_SWITCH):
|
||||
|
||||
devices.append(LupusecSwitch(data, device))
|
||||
|
||||
add_entities(devices)
|
||||
|
||||
@@ -24,7 +24,7 @@ def setup_platform(
|
||||
if discovery_info is None:
|
||||
return
|
||||
devs = []
|
||||
for (area_name, device) in hass.data[LUTRON_DEVICES]["binary_sensor"]:
|
||||
for area_name, device in hass.data[LUTRON_DEVICES]["binary_sensor"]:
|
||||
dev = LutronOccupancySensor(area_name, device, hass.data[LUTRON_CONTROLLER])
|
||||
devs.append(dev)
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ def setup_platform(
|
||||
) -> None:
|
||||
"""Set up the Lutron shades."""
|
||||
devs = []
|
||||
for (area_name, device) in hass.data[LUTRON_DEVICES]["cover"]:
|
||||
for area_name, device in hass.data[LUTRON_DEVICES]["cover"]:
|
||||
dev = LutronCover(area_name, device, hass.data[LUTRON_CONTROLLER])
|
||||
devs.append(dev)
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ def setup_platform(
|
||||
) -> None:
|
||||
"""Set up the Lutron lights."""
|
||||
devs = []
|
||||
for (area_name, device) in hass.data[LUTRON_DEVICES]["light"]:
|
||||
for area_name, device in hass.data[LUTRON_DEVICES]["light"]:
|
||||
dev = LutronLight(area_name, device, hass.data[LUTRON_CONTROLLER])
|
||||
devs.append(dev)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ def setup_platform(
|
||||
devs = []
|
||||
|
||||
# Add Lutron Switches
|
||||
for (area_name, device) in hass.data[LUTRON_DEVICES]["switch"]:
|
||||
for area_name, device in hass.data[LUTRON_DEVICES]["switch"]:
|
||||
dev = LutronSwitch(area_name, device, hass.data[LUTRON_CONTROLLER])
|
||||
devs.append(dev)
|
||||
|
||||
|
||||
@@ -256,7 +256,6 @@ def _async_setup_keypads(
|
||||
leap_to_keypad_button_names: dict[int, dict[int, str]] = {}
|
||||
|
||||
for bridge_button in bridge_buttons.values():
|
||||
|
||||
parent_device = cast(str, bridge_button["parent_device"])
|
||||
bridge_keypad = bridge_devices[parent_device]
|
||||
keypad_lutron_device_id = cast(int, bridge_keypad["device_id"])
|
||||
|
||||
@@ -29,7 +29,6 @@ async def async_setup_entry(
|
||||
entities: list[LutronCasetaButton] = []
|
||||
|
||||
for device in button_devices.values():
|
||||
|
||||
parent_keypad = keypads[device["parent_device"]]
|
||||
parent_device_info = parent_keypad["device_info"]
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "lutron_caseta",
|
||||
"name": "Lutron Cas\u00e9ta",
|
||||
"documentation": "https://www.home-assistant.io/integrations/lutron_caseta",
|
||||
"requirements": ["pylutron-caseta==0.18.0"],
|
||||
"requirements": ["pylutron-caseta==0.18.1"],
|
||||
"config_flow": true,
|
||||
"zeroconf": [
|
||||
{
|
||||
|
||||
@@ -32,7 +32,7 @@ from .addon import get_addon_manager
|
||||
from .api import async_register_api
|
||||
from .const import CONF_INTEGRATION_CREATED_ADDON, CONF_USE_ADDON, DOMAIN, LOGGER
|
||||
from .device_platform import DEVICE_PLATFORM
|
||||
from .helpers import MatterEntryData, get_matter
|
||||
from .helpers import MatterEntryData, get_matter, get_node_from_device_entry
|
||||
|
||||
CONNECT_TIMEOUT = 10
|
||||
LISTEN_READY_TIMEOUT = 30
|
||||
@@ -192,23 +192,13 @@ async def async_remove_config_entry_device(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry, device_entry: dr.DeviceEntry
|
||||
) -> bool:
|
||||
"""Remove a config entry from a device."""
|
||||
unique_id = None
|
||||
node = await get_node_from_device_entry(hass, device_entry)
|
||||
|
||||
for ident in device_entry.identifiers:
|
||||
if ident[0] == DOMAIN:
|
||||
unique_id = ident[1]
|
||||
break
|
||||
|
||||
if not unique_id:
|
||||
if node is None:
|
||||
return True
|
||||
|
||||
matter_entry_data: MatterEntryData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
matter_client = matter_entry_data.adapter.matter_client
|
||||
|
||||
for node in await matter_client.get_nodes():
|
||||
if node.unique_id == unique_id:
|
||||
await matter_client.remove_node(node.node_id)
|
||||
break
|
||||
matter = get_matter(hass)
|
||||
await matter.matter_client.remove_node(node.node_id)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user