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
25 changes: 18 additions & 7 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -523,22 +523,24 @@ jobs:
${{ runner.os }}-${{ runner.arch }}-${{ steps.python.outputs.python-version }}-uv-${{
env.UV_CACHE_VERSION }}-${{ steps.generate-uv-key.outputs.version }}-${{
env.HA_SHORT_VERSION }}-
- name: Restore apt cache
if: steps.cache-venv.outputs.cache-hit != 'true'
id: cache-apt
uses: actions/cache@v4.2.4
- name: Check if apt cache exists
id: cache-apt-check
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
lookup-only: ${{ steps.cache-venv.outputs.cache-hit == 'true' }}
path: |
${{ env.APT_CACHE_DIR }}
${{ env.APT_LIST_CACHE_DIR }}
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ needs.info.outputs.apt_cache_key }}
- name: Install additional OS dependencies
if: steps.cache-venv.outputs.cache-hit != 'true'
if: |
steps.cache-venv.outputs.cache-hit != 'true'
|| steps.cache-apt-check.outputs.cache-hit != 'true'
timeout-minutes: 10
run: |
sudo rm /etc/apt/sources.list.d/microsoft-prod.list
if [[ "${{ steps.cache-apt.outputs.cache-hit }}" != 'true' ]]; then
if [[ "${{ steps.cache-apt-check.outputs.cache-hit }}" != 'true' ]]; then
mkdir -p ${{ env.APT_CACHE_DIR }}
mkdir -p ${{ env.APT_LIST_CACHE_DIR }}
fi
Expand All @@ -563,9 +565,18 @@ jobs:
libswscale-dev \
libudev-dev

if [[ "${{ steps.cache-apt.outputs.cache-hit }}" != 'true' ]]; then
if [[ "${{ steps.cache-apt-check.outputs.cache-hit }}" != 'true' ]]; then
sudo chmod -R 755 ${{ env.APT_CACHE_BASE }}
fi
- name: Save apt cache
if: steps.cache-apt-check.outputs.cache-hit != 'true'
uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: |
${{ env.APT_CACHE_DIR }}
${{ env.APT_LIST_CACHE_DIR }}
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ needs.info.outputs.apt_cache_key }}
- name: Create Python virtual environment
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
Expand Down
2 changes: 2 additions & 0 deletions CODEOWNERS

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

53 changes: 50 additions & 3 deletions homeassistant/components/derivative/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,15 +227,28 @@ def calculate_weight(start: datetime, end: datetime, now: datetime) -> float:
weight = calculate_weight(start, end, current_time)
derivative = derivative + (value * Decimal(weight))

_LOGGER.debug(
"%s: Calculated new derivative as %f from %d segments",
self.entity_id,
derivative,
len(self._state_list),
)

return derivative

def _prune_state_list(self, current_time: datetime) -> None:
# filter out all derivatives older than `time_window` from our window list
old_len = len(self._state_list)
self._state_list = [
(time_start, time_end, state)
for time_start, time_end, state in self._state_list
if (current_time - time_end).total_seconds() < self._time_window
]
_LOGGER.debug(
"%s: Pruned %d elements from state list",
self.entity_id,
old_len - len(self._state_list),
)

def _handle_invalid_source_state(self, state: State | None) -> bool:
# Check the source state for unknown/unavailable condition. If unusable, write unknown/unavailable state and return false.
Expand Down Expand Up @@ -292,6 +305,10 @@ def _calc_derivative_on_max_sub_interval_exceeded_callback(
) -> None:
"""Calculate derivative based on time and reschedule."""

_LOGGER.debug(
"%s: Recalculating derivative due to max_sub_interval time elapsed",
self.entity_id,
)
self._prune_state_list(now)
derivative = self._calc_derivative_from_state_list(now)
self._write_native_value(derivative)
Expand All @@ -300,6 +317,11 @@ def _calc_derivative_on_max_sub_interval_exceeded_callback(
if derivative != 0:
schedule_max_sub_interval_exceeded(source_state)

_LOGGER.debug(
"%s: Scheduling max_sub_interval_callback in %s",
self.entity_id,
self._max_sub_interval,
)
self._cancel_max_sub_interval_exceeded_callback = async_call_later(
self.hass,
self._max_sub_interval,
Expand All @@ -309,6 +331,9 @@ def _calc_derivative_on_max_sub_interval_exceeded_callback(
@callback
def on_state_reported(event: Event[EventStateReportedData]) -> None:
"""Handle constant sensor state."""
_LOGGER.debug(
"%s: New state reported event: %s", self.entity_id, event.data
)
self._cancel_max_sub_interval_exceeded_callback()
new_state = event.data["new_state"]
if not self._handle_invalid_source_state(new_state):
Expand All @@ -330,6 +355,7 @@ def on_state_reported(event: Event[EventStateReportedData]) -> None:
@callback
def on_state_changed(event: Event[EventStateChangedData]) -> None:
"""Handle changed sensor state."""
_LOGGER.debug("%s: New state changed event: %s", self.entity_id, event.data)
self._cancel_max_sub_interval_exceeded_callback()
new_state = event.data["new_state"]
if not self._handle_invalid_source_state(new_state):
Expand Down Expand Up @@ -382,15 +408,32 @@ def calc_derivative(
/ Decimal(self._unit_prefix)
* Decimal(self._unit_time)
)
_LOGGER.debug(
"%s: Calculated new derivative segment as %f / %f / %f * %f = %f",
self.entity_id,
delta_value,
elapsed_time,
self._unit_prefix,
self._unit_time,
new_derivative,
)

except ValueError as err:
_LOGGER.warning("While calculating derivative: %s", err)
_LOGGER.warning(
"%s: While calculating derivative: %s", self.entity_id, err
)
except DecimalException as err:
_LOGGER.warning(
"Invalid state (%s > %s): %s", old_value, new_state.state, err
"%s: Invalid state (%s > %s): %s",
self.entity_id,
old_value,
new_state.state,
err,
)
except AssertionError as err:
_LOGGER.error("Could not calculate derivative: %s", err)
_LOGGER.error(
"%s: Could not calculate derivative: %s", self.entity_id, err
)

# For total inreasing sensors, the value is expected to continuously increase.
# A negative derivative for a total increasing sensor likely indicates the
Expand All @@ -400,6 +443,10 @@ def calc_derivative(
== SensorStateClass.TOTAL_INCREASING
and new_derivative < 0
):
_LOGGER.debug(
"%s: Dropping sample as source total_increasing sensor decreased",
self.entity_id,
)
return

# add latest derivative to the window list
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/solarlog/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"iot_class": "local_polling",
"loggers": ["solarlog_cli"],
"quality_scale": "platinum",
"requirements": ["solarlog_cli==0.5.0"]
"requirements": ["solarlog_cli==0.6.0"]
}
1 change: 1 addition & 0 deletions homeassistant/components/sonos/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"documentation": "https://www.home-assistant.io/integrations/sonos",
"iot_class": "local_push",
"loggers": ["soco", "sonos_websocket"],
"quality_scale": "bronze",
"requirements": ["soco==0.30.11", "sonos-websocket==0.1.3"],
"ssdp": [
{
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/sonos/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ def volume_down(self) -> None:
@soco_error()
def set_volume_level(self, volume: float) -> None:
"""Set volume level, range 0..1."""
self.soco.volume = int(volume * 100)
self.soco.volume = int(round(volume * 100))

@soco_error(UPNP_ERRORS_TO_IGNORE)
def set_shuffle(self, shuffle: bool) -> None:
Expand Down
34 changes: 34 additions & 0 deletions homeassistant/components/victron_remote_monitoring/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""The Victron VRM Solar Forecast integration."""

from __future__ import annotations

from homeassistant.const import Platform
from homeassistant.core import HomeAssistant

from .coordinator import (
VictronRemoteMonitoringConfigEntry,
VictronRemoteMonitoringDataUpdateCoordinator,
)

_PLATFORMS: list[Platform] = [Platform.SENSOR]


async def async_setup_entry(
hass: HomeAssistant, entry: VictronRemoteMonitoringConfigEntry
) -> bool:
"""Set up VRM from a config entry."""
coordinator = VictronRemoteMonitoringDataUpdateCoordinator(hass, entry)
await coordinator.async_config_entry_first_refresh()

entry.runtime_data = coordinator

await hass.config_entries.async_forward_entry_setups(entry, _PLATFORMS)

return True


async def async_unload_entry(
hass: HomeAssistant, entry: VictronRemoteMonitoringConfigEntry
) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, _PLATFORMS)
Loading
Loading