Skip to content
Merged
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
2 changes: 1 addition & 1 deletion homeassistant/components/esphome/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"mqtt": ["esphome/discover/#"],
"quality_scale": "platinum",
"requirements": [
"aioesphomeapi==37.2.0",
"aioesphomeapi==37.2.2",
"esphome-dashboard-api==1.3.0",
"bleak-esphome==3.1.0"
],
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/homekit/accessories.py
Original file line number Diff line number Diff line change
Expand Up @@ -628,12 +628,12 @@ def async_call_service(
self,
domain: str,
service: str,
service_data: dict[str, Any] | None,
service_data: dict[str, Any],
value: Any | None = None,
) -> None:
"""Fire event and call service for changes from HomeKit."""
event_data = {
ATTR_ENTITY_ID: self.entity_id,
ATTR_ENTITY_ID: service_data.get(ATTR_ENTITY_ID, self.entity_id),
ATTR_DISPLAY_NAME: self.display_name,
ATTR_SERVICE: service,
ATTR_VALUE: value,
Expand Down
4 changes: 4 additions & 0 deletions homeassistant/components/homekit/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
CONF_LINKED_OBSTRUCTION_SENSOR = "linked_obstruction_sensor"
CONF_LINKED_PM25_SENSOR = "linked_pm25_sensor"
CONF_LINKED_TEMPERATURE_SENSOR = "linked_temperature_sensor"
CONF_LINKED_VALVE_DURATION = "linked_valve_duration"
CONF_LINKED_VALVE_END_TIME = "linked_valve_end_time"
CONF_LOW_BATTERY_THRESHOLD = "low_battery_threshold"
CONF_MAX_FPS = "max_fps"
CONF_MAX_HEIGHT = "max_height"
Expand Down Expand Up @@ -229,10 +231,12 @@
CHAR_OUTLET_IN_USE = "OutletInUse"
CHAR_POSITION_STATE = "PositionState"
CHAR_PROGRAMMABLE_SWITCH_EVENT = "ProgrammableSwitchEvent"
CHAR_REMAINING_DURATION = "RemainingDuration"
CHAR_REMOTE_KEY = "RemoteKey"
CHAR_ROTATION_DIRECTION = "RotationDirection"
CHAR_ROTATION_SPEED = "RotationSpeed"
CHAR_SATURATION = "Saturation"
CHAR_SET_DURATION = "SetDuration"
CHAR_SERIAL_NUMBER = "SerialNumber"
CHAR_SERVICE_LABEL_INDEX = "ServiceLabelIndex"
CHAR_SERVICE_LABEL_NAMESPACE = "ServiceLabelNamespace"
Expand Down
108 changes: 107 additions & 1 deletion homeassistant/components/homekit/type_switches.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
)

from homeassistant.components import button, input_button
from homeassistant.components.input_number import (
ATTR_VALUE as INPUT_NUMBER_ATTR_VALUE,
DOMAIN as INPUT_NUMBER_DOMAIN,
SERVICE_SET_VALUE as INPUT_NUMBER_SERVICE_SET_VALUE,
)
from homeassistant.components.input_select import ATTR_OPTIONS, SERVICE_SELECT_OPTION
from homeassistant.components.lawn_mower import (
DOMAIN as LAWN_MOWER_DOMAIN,
Expand Down Expand Up @@ -45,6 +50,7 @@
)
from homeassistant.core import HomeAssistant, State, callback, split_entity_id
from homeassistant.helpers.event import async_call_later
from homeassistant.util import dt as dt_util

from .accessories import TYPES, HomeAccessory, HomeDriver
from .const import (
Expand All @@ -54,7 +60,11 @@
CHAR_NAME,
CHAR_ON,
CHAR_OUTLET_IN_USE,
CHAR_REMAINING_DURATION,
CHAR_SET_DURATION,
CHAR_VALVE_TYPE,
CONF_LINKED_VALVE_DURATION,
CONF_LINKED_VALVE_END_TIME,
SERV_OUTLET,
SERV_SWITCH,
SERV_VALVE,
Expand Down Expand Up @@ -271,14 +281,47 @@ def __init__(
self.on_service = on_service
self.off_service = off_service

serv_valve = self.add_preload_service(SERV_VALVE)
self.chars = []

self.linked_duration_entity: str | None = self.config.get(
CONF_LINKED_VALVE_DURATION
)
self.linked_end_time_entity: str | None = self.config.get(
CONF_LINKED_VALVE_END_TIME
)

if self.linked_duration_entity:
self.chars.append(CHAR_SET_DURATION)
if self.linked_end_time_entity:
self.chars.append(CHAR_REMAINING_DURATION)

serv_valve = self.add_preload_service(SERV_VALVE, self.chars)
self.char_active = serv_valve.configure_char(
CHAR_ACTIVE, value=False, setter_callback=self.set_state
)
self.char_in_use = serv_valve.configure_char(CHAR_IN_USE, value=False)
self.char_valve_type = serv_valve.configure_char(
CHAR_VALVE_TYPE, value=VALVE_TYPE[valve_type].valve_type
)

if CHAR_SET_DURATION in self.chars:
_LOGGER.debug(
"%s: Add characteristic %s", self.entity_id, CHAR_SET_DURATION
)
self.char_set_duration = serv_valve.configure_char(
CHAR_SET_DURATION,
value=self.get_duration(),
setter_callback=self.set_duration,
)

if CHAR_REMAINING_DURATION in self.chars:
_LOGGER.debug(
"%s: Add characteristic %s", self.entity_id, CHAR_REMAINING_DURATION
)
self.char_remaining_duration = serv_valve.configure_char(
CHAR_REMAINING_DURATION, getter_callback=self.get_remaining_duration
)

# Set the state so it is in sync on initial
# GET to avoid an event storm after homekit startup
self.async_update_state(state)
Expand All @@ -294,12 +337,75 @@ def set_state(self, value: bool) -> None:
@callback
def async_update_state(self, new_state: State) -> None:
"""Update switch state after state changed."""
self._update_duration_chars()
current_state = 1 if new_state.state in self.open_states else 0
_LOGGER.debug("%s: Set active state to %s", self.entity_id, current_state)
self.char_active.set_value(current_state)
_LOGGER.debug("%s: Set in_use state to %s", self.entity_id, current_state)
self.char_in_use.set_value(current_state)

def _update_duration_chars(self) -> None:
"""Update valve duration related properties if characteristics are available."""
if CHAR_SET_DURATION in self.chars:
self.char_set_duration.set_value(self.get_duration())
if CHAR_REMAINING_DURATION in self.chars:
self.char_remaining_duration.set_value(self.get_remaining_duration())

def set_duration(self, value: int) -> None:
"""Set default duration for how long the valve should remain open."""
_LOGGER.debug("%s: Set default run time to %s", self.entity_id, value)
self.async_call_service(
INPUT_NUMBER_DOMAIN,
INPUT_NUMBER_SERVICE_SET_VALUE,
{
ATTR_ENTITY_ID: self.linked_duration_entity,
INPUT_NUMBER_ATTR_VALUE: value,
},
value,
)

def get_duration(self) -> int:
"""Get the default duration from Home Assistant."""
duration_state = self._get_entity_state(self.linked_duration_entity)
if duration_state is None:
_LOGGER.debug(
"%s: No linked duration entity state available", self.entity_id
)
return 0

try:
duration = float(duration_state)
return max(int(duration), 0)
except ValueError:
_LOGGER.debug("%s: Cannot parse linked duration entity", self.entity_id)
return 0

def get_remaining_duration(self) -> int:
"""Calculate the remaining duration based on end time in Home Assistant."""
end_time_state = self._get_entity_state(self.linked_end_time_entity)
if end_time_state is None:
_LOGGER.debug(
"%s: No linked end time entity state available", self.entity_id
)
return self.get_duration()

end_time = dt_util.parse_datetime(end_time_state)
if end_time is None:
_LOGGER.debug("%s: Cannot parse linked end time entity", self.entity_id)
return self.get_duration()

remaining_time = (end_time - dt_util.utcnow()).total_seconds()
return max(int(remaining_time), 0)

def _get_entity_state(self, entity_id: str | None) -> str | None:
"""Fetch the state of a linked entity."""
if entity_id is None:
return None
state = self.hass.states.get(entity_id)
if state is None:
return None
return state.state


@TYPES.register("ValveSwitch")
class ValveSwitch(ValveBase):
Expand Down
16 changes: 15 additions & 1 deletion homeassistant/components/homekit/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from homeassistant.components import (
binary_sensor,
input_number,
media_player,
persistent_notification,
sensor,
Expand Down Expand Up @@ -69,6 +70,8 @@
CONF_LINKED_OBSTRUCTION_SENSOR,
CONF_LINKED_PM25_SENSOR,
CONF_LINKED_TEMPERATURE_SENSOR,
CONF_LINKED_VALVE_DURATION,
CONF_LINKED_VALVE_END_TIME,
CONF_LOW_BATTERY_THRESHOLD,
CONF_MAX_FPS,
CONF_MAX_HEIGHT,
Expand Down Expand Up @@ -266,7 +269,9 @@
TYPE_VALVE,
)
),
)
),
vol.Optional(CONF_LINKED_VALVE_DURATION): cv.entity_domain(input_number.DOMAIN),
vol.Optional(CONF_LINKED_VALVE_END_TIME): cv.entity_domain(sensor.DOMAIN),
}
)

Expand All @@ -277,6 +282,12 @@
}
)

VALVE_SCHEMA = BASIC_INFO_SCHEMA.extend(
{
vol.Optional(CONF_LINKED_VALVE_DURATION): cv.entity_domain(input_number.DOMAIN),
vol.Optional(CONF_LINKED_VALVE_END_TIME): cv.entity_domain(sensor.DOMAIN),
}
)

HOMEKIT_CHAR_TRANSLATIONS = {
0: " ", # nul
Expand Down Expand Up @@ -360,6 +371,9 @@ def validate_entity_config(values: dict) -> dict[str, dict]:
elif domain == "sensor":
config = SENSOR_SCHEMA(config)

elif domain == "valve":
config = VALVE_SCHEMA(config)

else:
config = BASIC_INFO_SCHEMA(config)

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/motion_blinds/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@
"documentation": "https://www.home-assistant.io/integrations/motion_blinds",
"iot_class": "local_push",
"loggers": ["motionblinds"],
"requirements": ["motionblinds==0.6.29"]
"requirements": ["motionblinds==0.6.30"]
}
6 changes: 5 additions & 1 deletion homeassistant/components/uptime_kuma/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,11 @@ def __init__(
name=coordinator.data[monitor].monitor_name,
identifiers={(DOMAIN, f"{coordinator.config_entry.entry_id}_{monitor!s}")},
manufacturer="Uptime Kuma",
configuration_url=coordinator.config_entry.data[CONF_URL],
configuration_url=(
None
if "127.0.0.1" in (url := coordinator.config_entry.data[CONF_URL])
else url
),
sw_version=coordinator.api.version.version,
)

Expand Down
4 changes: 2 additions & 2 deletions requirements_all.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions requirements_test_all.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion script/bootstrap
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Stop on errors
set -e

cd "$(dirname "$0")/.."
cd "$(realpath "$(dirname "$0")/..")"

echo "Installing development dependencies..."
uv pip install wheel --constraint homeassistant/package_constraints.txt --upgrade
Expand Down
Loading
Loading