Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2024.12.4 #133422

Merged
merged 17 commits into from
Dec 17, 2024
Merged

2024.12.4 #133422

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion homeassistant/block_async_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ def _check_sleep_call_allowed(mapped_args: dict[str, Any]) -> bool:
return False


def _check_load_verify_locations_call_allowed(mapped_args: dict[str, Any]) -> bool:
# If only cadata is passed, we can ignore it
kwargs = mapped_args.get("kwargs")
return bool(kwargs and len(kwargs) == 1 and "cadata" in kwargs)


@dataclass(slots=True, frozen=True)
class BlockingCall:
"""Class to hold information about a blocking call."""
Expand Down Expand Up @@ -158,7 +164,7 @@ class BlockingCall:
original_func=SSLContext.load_verify_locations,
object=SSLContext,
function="load_verify_locations",
check_allowed=None,
check_allowed=_check_load_verify_locations_call_allowed,
strict=False,
strict_core=False,
skip_for_tests=True,
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/august/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@
"documentation": "https://www.home-assistant.io/integrations/august",
"iot_class": "cloud_push",
"loggers": ["pubnub", "yalexs"],
"requirements": ["yalexs==8.10.0", "yalexs-ble==2.5.2"]
"requirements": ["yalexs==8.10.0", "yalexs-ble==2.5.5"]
}
6 changes: 3 additions & 3 deletions homeassistant/components/fibaro/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,16 +274,16 @@ def hvac_mode(self) -> HVACMode | None:
if isinstance(fibaro_operation_mode, str):
with suppress(ValueError):
return HVACMode(fibaro_operation_mode.lower())
elif fibaro_operation_mode in OPMODES_HVAC:
# when the mode cannot be instantiated a preset_mode is selected
return HVACMode.AUTO
if fibaro_operation_mode in OPMODES_HVAC:
return OPMODES_HVAC[fibaro_operation_mode]
return None

def set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target operation mode."""
if not self._op_mode_device:
return
if self.preset_mode:
return

if "setOperatingMode" in self._op_mode_device.fibaro_device.actions:
self._op_mode_device.action("setOperatingMode", HA_OPMODES_HVAC[hvac_mode])
Expand Down
52 changes: 32 additions & 20 deletions homeassistant/components/flexit_bacnet/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class FlexitNumberEntityDescription(NumberEntityDescription):
"""Describes a Flexit number entity."""

native_value_fn: Callable[[FlexitBACnet], float]
native_max_value_fn: Callable[[FlexitBACnet], int]
native_min_value_fn: Callable[[FlexitBACnet], int]
set_native_value_fn: Callable[[FlexitBACnet], Callable[[int], Awaitable[None]]]


Expand All @@ -37,121 +39,121 @@ class FlexitNumberEntityDescription(NumberEntityDescription):
key="away_extract_fan_setpoint",
translation_key="away_extract_fan_setpoint",
device_class=NumberDeviceClass.POWER_FACTOR,
native_min_value=0,
native_max_value=100,
native_step=1,
mode=NumberMode.SLIDER,
native_value_fn=lambda device: device.fan_setpoint_extract_air_away,
set_native_value_fn=lambda device: device.set_fan_setpoint_extract_air_away,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda device: int(device.fan_setpoint_extract_air_home),
native_min_value_fn=lambda _: 30,
),
FlexitNumberEntityDescription(
key="away_supply_fan_setpoint",
translation_key="away_supply_fan_setpoint",
device_class=NumberDeviceClass.POWER_FACTOR,
native_min_value=0,
native_max_value=100,
native_step=1,
mode=NumberMode.SLIDER,
native_value_fn=lambda device: device.fan_setpoint_supply_air_away,
set_native_value_fn=lambda device: device.set_fan_setpoint_supply_air_away,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda device: int(device.fan_setpoint_supply_air_home),
native_min_value_fn=lambda _: 30,
),
FlexitNumberEntityDescription(
key="cooker_hood_extract_fan_setpoint",
translation_key="cooker_hood_extract_fan_setpoint",
device_class=NumberDeviceClass.POWER_FACTOR,
native_min_value=0,
native_max_value=100,
native_step=1,
mode=NumberMode.SLIDER,
native_value_fn=lambda device: device.fan_setpoint_extract_air_cooker,
set_native_value_fn=lambda device: device.set_fan_setpoint_extract_air_cooker,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_min_value_fn=lambda _: 30,
),
FlexitNumberEntityDescription(
key="cooker_hood_supply_fan_setpoint",
translation_key="cooker_hood_supply_fan_setpoint",
device_class=NumberDeviceClass.POWER_FACTOR,
native_min_value=0,
native_max_value=100,
native_step=1,
mode=NumberMode.SLIDER,
native_value_fn=lambda device: device.fan_setpoint_supply_air_cooker,
set_native_value_fn=lambda device: device.set_fan_setpoint_supply_air_cooker,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_min_value_fn=lambda _: 30,
),
FlexitNumberEntityDescription(
key="fireplace_extract_fan_setpoint",
translation_key="fireplace_extract_fan_setpoint",
device_class=NumberDeviceClass.POWER_FACTOR,
native_min_value=0,
native_max_value=100,
native_step=1,
mode=NumberMode.SLIDER,
native_value_fn=lambda device: device.fan_setpoint_extract_air_fire,
set_native_value_fn=lambda device: device.set_fan_setpoint_extract_air_fire,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_min_value_fn=lambda _: 30,
),
FlexitNumberEntityDescription(
key="fireplace_supply_fan_setpoint",
translation_key="fireplace_supply_fan_setpoint",
device_class=NumberDeviceClass.POWER_FACTOR,
native_min_value=0,
native_max_value=100,
native_step=1,
mode=NumberMode.SLIDER,
native_value_fn=lambda device: device.fan_setpoint_supply_air_fire,
set_native_value_fn=lambda device: device.set_fan_setpoint_supply_air_fire,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_min_value_fn=lambda _: 30,
),
FlexitNumberEntityDescription(
key="high_extract_fan_setpoint",
translation_key="high_extract_fan_setpoint",
device_class=NumberDeviceClass.POWER_FACTOR,
native_min_value=0,
native_max_value=100,
native_step=1,
mode=NumberMode.SLIDER,
native_value_fn=lambda device: device.fan_setpoint_extract_air_high,
set_native_value_fn=lambda device: device.set_fan_setpoint_extract_air_high,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_min_value_fn=lambda device: int(device.fan_setpoint_extract_air_home),
),
FlexitNumberEntityDescription(
key="high_supply_fan_setpoint",
translation_key="high_supply_fan_setpoint",
device_class=NumberDeviceClass.POWER_FACTOR,
native_min_value=0,
native_max_value=100,
native_step=1,
mode=NumberMode.SLIDER,
native_value_fn=lambda device: device.fan_setpoint_supply_air_high,
set_native_value_fn=lambda device: device.set_fan_setpoint_supply_air_high,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_min_value_fn=lambda device: int(device.fan_setpoint_supply_air_home),
),
FlexitNumberEntityDescription(
key="home_extract_fan_setpoint",
translation_key="home_extract_fan_setpoint",
device_class=NumberDeviceClass.POWER_FACTOR,
native_min_value=0,
native_max_value=100,
native_step=1,
mode=NumberMode.SLIDER,
native_value_fn=lambda device: device.fan_setpoint_extract_air_home,
set_native_value_fn=lambda device: device.set_fan_setpoint_extract_air_home,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_min_value_fn=lambda device: int(device.fan_setpoint_extract_air_away),
),
FlexitNumberEntityDescription(
key="home_supply_fan_setpoint",
translation_key="home_supply_fan_setpoint",
device_class=NumberDeviceClass.POWER_FACTOR,
native_min_value=0,
native_max_value=100,
native_step=1,
mode=NumberMode.SLIDER,
native_value_fn=lambda device: device.fan_setpoint_supply_air_home,
set_native_value_fn=lambda device: device.set_fan_setpoint_supply_air_home,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_min_value_fn=lambda device: int(device.fan_setpoint_supply_air_away),
),
)

Expand Down Expand Up @@ -192,6 +194,16 @@ def native_value(self) -> float:
"""Return the state of the number."""
return self.entity_description.native_value_fn(self.coordinator.device)

@property
def native_max_value(self) -> float:
"""Return the native max value of the number."""
return self.entity_description.native_max_value_fn(self.coordinator.device)

@property
def native_min_value(self) -> float:
"""Return the native min value of the number."""
return self.entity_description.native_min_value_fn(self.coordinator.device)

async def async_set_native_value(self, value: float) -> None:
"""Update the current value."""
set_native_value_fn = self.entity_description.set_native_value_fn(
Expand Down
7 changes: 2 additions & 5 deletions homeassistant/components/history/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

from . import websocket_api
from .const import DOMAIN
from .helpers import entities_may_have_state_changes_after, has_states_before
from .helpers import entities_may_have_state_changes_after, has_recorder_run_after

CONF_ORDER = "use_include_order"

Expand Down Expand Up @@ -107,10 +107,7 @@ async def get(
no_attributes = "no_attributes" in request.query

if (
# has_states_before will return True if there are states older than
# end_time. If it's false, we know there are no states in the
# database up until end_time.
(end_time and not has_states_before(hass, end_time))
(end_time and not has_recorder_run_after(hass, end_time))
or not include_start_time_state
and entity_ids
and not entities_may_have_state_changes_after(
Expand Down
13 changes: 6 additions & 7 deletions homeassistant/components/history/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from datetime import datetime as dt

from homeassistant.components.recorder import get_instance
from homeassistant.components.recorder.models import process_timestamp
from homeassistant.core import HomeAssistant


Expand All @@ -25,10 +26,8 @@ def entities_may_have_state_changes_after(
return False


def has_states_before(hass: HomeAssistant, run_time: dt) -> bool:
"""Check if the recorder has states as old or older than run_time.

Returns True if there may be such states.
"""
oldest_ts = get_instance(hass).states_manager.oldest_ts
return oldest_ts is not None and run_time.timestamp() >= oldest_ts
def has_recorder_run_after(hass: HomeAssistant, run_time: dt) -> bool:
"""Check if the recorder has any runs after a specific time."""
return run_time >= process_timestamp(
get_instance(hass).recorder_runs_manager.first.start
)
7 changes: 2 additions & 5 deletions homeassistant/components/history/websocket_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
import homeassistant.util.dt as dt_util

from .const import EVENT_COALESCE_TIME, MAX_PENDING_HISTORY_STATES
from .helpers import entities_may_have_state_changes_after, has_states_before
from .helpers import entities_may_have_state_changes_after, has_recorder_run_after

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -142,10 +142,7 @@ async def ws_get_history_during_period(
no_attributes = msg["no_attributes"]

if (
# has_states_before will return True if there are states older than
# end_time. If it's false, we know there are no states in the
# database up until end_time.
(end_time and not has_states_before(hass, end_time))
(end_time and not has_recorder_run_after(hass, end_time))
or not include_start_time_state
and entity_ids
and not entities_may_have_state_changes_after(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/holiday/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/holiday",
"iot_class": "local_polling",
"requirements": ["holidays==0.62", "babel==2.15.0"]
"requirements": ["holidays==0.63", "babel==2.15.0"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class HomeKitAlarmControlPanelEntity(HomeKitEntity, AlarmControlPanelEntity):
| AlarmControlPanelEntityFeature.ARM_AWAY
| AlarmControlPanelEntityFeature.ARM_NIGHT
)
_attr_code_arm_required = False

def get_characteristic_types(self) -> list[str]:
"""Define the homekit characteristics the entity cares about."""
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/imgw_pib/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/imgw_pib",
"iot_class": "cloud_polling",
"requirements": ["imgw_pib==1.0.6"]
"requirements": ["imgw_pib==1.0.7"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/incomfort/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/incomfort",
"iot_class": "local_polling",
"loggers": ["incomfortclient"],
"requirements": ["incomfort-client==0.6.3-1"]
"requirements": ["incomfort-client==0.6.4"]
}
5 changes: 4 additions & 1 deletion homeassistant/components/lifx/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"LIFX Ceiling",
"LIFX Clean",
"LIFX Color",
"LIFX Colour",
"LIFX DLCOL",
"LIFX Dlight",
"LIFX DLWW",
Expand All @@ -35,20 +36,22 @@
"LIFX Neon",
"LIFX Nightvision",
"LIFX PAR38",
"LIFX Permanent Outdoor",
"LIFX Pls",
"LIFX Plus",
"LIFX Round",
"LIFX Square",
"LIFX String",
"LIFX Tile",
"LIFX Tube",
"LIFX White",
"LIFX Z"
]
},
"iot_class": "local_polling",
"loggers": ["aiolifx", "aiolifx_effects", "bitstring"],
"requirements": [
"aiolifx==1.1.1",
"aiolifx==1.1.2",
"aiolifx-effects==0.3.2",
"aiolifx-themes==0.5.5"
]
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/media_extractor/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"iot_class": "calculated",
"loggers": ["yt_dlp"],
"quality_scale": "internal",
"requirements": ["yt-dlp[default]==2024.12.06"],
"requirements": ["yt-dlp[default]==2024.12.13"],
"single_config_entry": true
}
17 changes: 17 additions & 0 deletions homeassistant/components/python_script/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
"""Component to allow running Python scripts."""

from collections.abc import Mapping, Sequence
import datetime
import glob
import logging
from numbers import Number
import operator
import os
import time
import types
from typing import Any

from RestrictedPython import (
Expand Down Expand Up @@ -167,6 +169,20 @@ def python_script_service_handler(call: ServiceCall) -> ServiceResponse:
}


def guarded_import(
name: str,
globals: Mapping[str, object] | None = None,
locals: Mapping[str, object] | None = None,
fromlist: Sequence[str] = (),
level: int = 0,
) -> types.ModuleType:
"""Guard imports."""
# Allow import of _strptime needed by datetime.datetime.strptime
if name == "_strptime":
return __import__(name, globals, locals, fromlist, level)
raise ScriptError(f"Not allowed to import {name}")


def guarded_inplacevar(op: str, target: Any, operand: Any) -> Any:
"""Implement augmented-assign (+=, -=, etc.) operators for restricted code.

Expand Down Expand Up @@ -232,6 +248,7 @@ def protected_getattr(obj, name, default=None):
return getattr(obj, name, default)

extra_builtins = {
"__import__": guarded_import,
"datetime": datetime,
"sorted": sorted,
"time": TimeWrapper(),
Expand Down
Loading
Loading