Skip to content
4 changes: 1 addition & 3 deletions homeassistant/components/google_health/quality_scale.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,7 @@ rules:
docs-troubleshooting: done
docs-use-cases: done
dynamic-devices: done
entity-category:
status: exempt
comment: All entities are user-facing primary sensors.
entity-category: done
entity-device-class: done
entity-disabled-by-default:
status: exempt
Expand Down
5 changes: 5 additions & 0 deletions homeassistant/components/google_health/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
)
from homeassistant.const import (
PERCENTAGE,
EntityCategory,
UnitOfEnergy,
UnitOfLength,
UnitOfMass,
Expand Down Expand Up @@ -81,6 +82,7 @@ class GoogleHealthSensorEntityDescription[
key="active_calories",
translation_key="active_calories",
native_unit_of_measurement=UnitOfEnergy.KILO_CALORIE,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda data: (
data.active_energy_burned.kcal_sum
Expand All @@ -92,6 +94,7 @@ class GoogleHealthSensorEntityDescription[
key="total_calories",
translation_key="total_calories",
native_unit_of_measurement=UnitOfEnergy.KILO_CALORIE,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda data: (
data.total_calories.kcal_sum if data and data.total_calories else 0.0
Expand Down Expand Up @@ -230,6 +233,7 @@ class GoogleHealthSensorEntityDescription[
key="calories_consumed",
translation_key="calories_consumed",
native_unit_of_measurement=UnitOfEnergy.KILO_CALORIE,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda data: (
data.nutrition.energy.kcal_sum
Expand Down Expand Up @@ -374,6 +378,7 @@ class GoogleHealthDeviceSensor(
"""Device-specific Google Health sensor entity."""

_attr_has_entity_name = True
_attr_entity_category = EntityCategory.DIAGNOSTIC
entity_description: GoogleHealthDeviceSensorEntityDescription

def __init__(
Expand Down
4 changes: 1 addition & 3 deletions homeassistant/components/myuplink/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import asyncio.timeouts
from dataclasses import dataclass
from datetime import datetime, timedelta
from datetime import timedelta
import logging
from typing import override

Expand All @@ -22,7 +22,6 @@ class CoordinatorData:
systems: list[System]
devices: dict[str, Device]
points: dict[str, dict[str, DevicePoint]]
time: datetime


type MyUplinkConfigEntry = ConfigEntry[MyUplinkDataCoordinator]
Expand Down Expand Up @@ -75,5 +74,4 @@ async def _async_update_data(self) -> CoordinatorData:
systems=systems,
devices=devices,
points=points,
time=datetime.now(), # pylint: disable=home-assistant-enforce-naive-now
)
2 changes: 1 addition & 1 deletion homeassistant/components/tado/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ async def set_temperature_offset(self, device_id, offset):

async def set_meter_reading(self, reading: int) -> dict[str, Any]:
"""Send meter reading to Tado."""
dt: str = datetime.now().strftime("%Y-%m-%d") # pylint: disable=home-assistant-enforce-naive-now
dt: str = dt_util.now().strftime("%Y-%m-%d")
if self._tado is None:
raise HomeAssistantError("Tado client is not initialized")

Expand Down
4 changes: 1 addition & 3 deletions homeassistant/components/teslemetry/coordinator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Teslemetry Data Coordinator."""

from datetime import datetime, timedelta
from datetime import timedelta
from typing import TYPE_CHECKING, Any, override

from tesla_fleet_api.const import TeslaEnergyPeriod, VehicleDataEndpoint
Expand Down Expand Up @@ -113,7 +113,6 @@ class TeslemetryVehicleDataCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Class to manage fetching data from the Teslemetry API."""

config_entry: TeslemetryConfigEntry
last_active: datetime

def __init__(
self,
Expand All @@ -135,7 +134,6 @@ def __init__(

self.api = api
self.data = flatten(product)
self.last_active = datetime.now() # pylint: disable=home-assistant-enforce-naive-now

@override
async def _async_update_data(self) -> dict[str, Any]:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/watts/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
# Update intervals
UPDATE_INTERVAL_SECONDS = 30
FAST_POLLING_INTERVAL_SECONDS = 5
DISCOVERY_INTERVAL_MINUTES = 15
DISCOVERY_INTERVAL_SECONDS = 15 * 60

# Mapping from Watts Vision+ modes to Home Assistant HVAC modes
THERMOSTAT_MODE_TO_HVAC: dict[ThermostatMode, HVACMode] = {
Expand Down
24 changes: 13 additions & 11 deletions homeassistant/components/watts/coordinator.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""Data coordinator for Watts Vision integration."""

from dataclasses import dataclass
from datetime import datetime, timedelta
from datetime import timedelta
import logging
import time
from typing import TYPE_CHECKING, override

from visionpluspython.client import WattsVisionClient
Expand All @@ -22,7 +23,7 @@
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import (
DISCOVERY_INTERVAL_MINUTES,
DISCOVERY_INTERVAL_SECONDS,
DOMAIN,
FAST_POLLING_INTERVAL_SECONDS,
UPDATE_INTERVAL_SECONDS,
Expand Down Expand Up @@ -61,18 +62,17 @@ def __init__(
config_entry=config_entry,
)
self.client = client
self.last_discovery: datetime | None = None
self.last_discovery: float | None = None
self.previous_devices: set[str] = set()

@override
async def _async_update_data(self) -> dict[str, Device]:
"""Fetch data and periodic device discovery."""
now = datetime.now() # pylint: disable=home-assistant-enforce-naive-now
now = time.time()
is_first_refresh = self.last_discovery is None
discovery_interval_elapsed = (
self.last_discovery is not None
and now - self.last_discovery
>= timedelta(minutes=DISCOVERY_INTERVAL_MINUTES)
and now - self.last_discovery >= DISCOVERY_INTERVAL_SECONDS
)

if is_first_refresh or discovery_interval_elapsed:
Expand Down Expand Up @@ -185,7 +185,7 @@ def __init__(
self.client = client
self.device_id = device_id
self.hub_coordinator = hub_coordinator
self.fast_polling_until: datetime | None = None
self.fast_polling_until: float | None = None

# Listen to hub coordinator updates
self.unsubscribe_hub_listener = hub_coordinator.async_add_listener(
Expand All @@ -208,7 +208,7 @@ def _handle_hub_update(self) -> None:
@override
async def _async_update_data(self) -> WattsVisionDeviceData:
"""Refresh specific device."""
if self.fast_polling_until and datetime.now() > self.fast_polling_until: # pylint: disable=home-assistant-enforce-naive-now
if self.fast_polling_until and time.time() > self.fast_polling_until:
self.fast_polling_until = None
self.update_interval = None
_LOGGER.debug(
Expand Down Expand Up @@ -244,10 +244,12 @@ async def _async_update_data(self) -> WattsVisionDeviceData:
_LOGGER.debug("Refreshed device %s", self.device_id)
return WattsVisionDeviceData(device=device)

def trigger_fast_polling(self, duration: int = 60) -> None:
def trigger_fast_polling(self, duration_seconds: int = 60) -> None:
"""Activate fast polling for a specified duration after a command."""
self.fast_polling_until = datetime.now() + timedelta(seconds=duration) # pylint: disable=home-assistant-enforce-naive-now
self.fast_polling_until = time.time() + duration_seconds
self.update_interval = timedelta(seconds=FAST_POLLING_INTERVAL_SECONDS)
_LOGGER.debug(
"Device %s: Activated fast polling for %d seconds", self.device_id, duration
"Device %s: Activated fast polling for %d seconds",
self.device_id,
duration_seconds,
)
13 changes: 9 additions & 4 deletions homeassistant/components/watts/diagnostics.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"""Diagnostics support for Watts Vision +."""

import dataclasses
from datetime import datetime
import time
from typing import Any

from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util

from . import WattsVisionConfigEntry

Expand All @@ -21,7 +22,7 @@ async def async_get_config_entry_diagnostics(
runtime_data = entry.runtime_data
hub_coordinator = runtime_data.hub_coordinator
device_coordinators = runtime_data.device_coordinators
now = datetime.now() # pylint: disable=home-assistant-enforce-naive-now
now = time.time()

return async_redact_data(
{
Expand All @@ -34,7 +35,9 @@ async def async_get_config_entry_diagnostics(
else None
),
"last_discovery": (
hub_coordinator.last_discovery.isoformat()
dt_util.utc_from_timestamp(
hub_coordinator.last_discovery
).isoformat()
if hub_coordinator.last_discovery
else None
),
Expand All @@ -54,7 +57,9 @@ async def async_get_config_entry_diagnostics(
and coordinator.fast_polling_until > now
),
"fast_polling_until": (
coordinator.fast_polling_until.isoformat()
dt_util.utc_from_timestamp(
coordinator.fast_polling_until
).isoformat()
if coordinator.fast_polling_until is not None
and coordinator.fast_polling_until > now
else None
Expand Down
10 changes: 7 additions & 3 deletions homeassistant/components/zwave_js/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -696,11 +696,15 @@ async def async_register_node_in_dev_reg(self, node: ZwaveNode) -> dr.DeviceEntr
node_id_device = self.dev_reg.async_get_device_by_identifier(
device_id, self.config_entry.entry_id
)
via_identifier = None
via_device_id: str | None = None
controller = driver.controller
# Get the controller node device ID if this node is not the controller
if controller.own_node and controller.own_node != node:
via_identifier = get_device_id(driver, controller.own_node)
via_device_id = dr.async_get_device_id_by_identifier(
self.hass,
get_device_id(driver, controller.own_node),
config_entry_id=self.config_entry.entry_id,
)

if device_id_ext:
# If there is a device with this node ID but with a different hardware
Expand Down Expand Up @@ -747,7 +751,7 @@ async def async_register_node_in_dev_reg(self, node: ZwaveNode) -> dr.DeviceEntr
model=node.device_config.label,
manufacturer=node.device_config.manufacturer,
suggested_area=node.location or UNDEFINED,
via_device=via_identifier,
via_device_id=via_device_id,
)

async_dispatcher_send(self.hass, EVENT_DEVICE_ADDED_TO_REGISTRY, device)
Expand Down
14 changes: 9 additions & 5 deletions homeassistant/components/zwave_js/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1141,18 +1141,22 @@ async def websocket_provision_smart_start_node(
manufacturer = device_info.manufacturer
model = device_info.label

via_device_id: str | None = None
if driver.controller.own_node:
via_device_id = dr.async_get_device_id_by_identifier(
hass,
get_device_id(driver, driver.controller.own_node),
config_entry_id=entry.entry_id,
)

# Create an empty device
device = dev_reg.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={device_identifier},
name=device_name,
manufacturer=manufacturer,
model=model,
via_device=(
get_device_id(driver, driver.controller.own_node)
if driver.controller.own_node
else None
),
via_device_id=via_device_id,
)
dev_reg.async_update_device(
device.id, area_id=msg.get(AREA_ID), name_by_user=device_name
Expand Down
6 changes: 0 additions & 6 deletions homeassistant/helpers/trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,8 +623,6 @@ def report_not_triggered(reason: str, /, **data: Any) -> None:
if not self.is_valid_state(to_state, report_not_triggered):
return

# The trigger should never fire if the origin state is excluded
# or the transition is not valid.
if (
from_state.state in self._excluded_from_states
or not self.is_valid_transition(from_state, to_state)
Expand Down Expand Up @@ -657,9 +655,6 @@ def report_not_triggered(reason: str, /, **data: Any) -> None:
@callback
def call_action() -> None:
"""Call action with right context."""
# After a `for` delay, keep the original triggering event payload.
# `async_track_same_state` only verifies the state remained valid
# for the configured duration before firing the action.
run_action(
{
ATTR_ENTITY_ID: entity_id,
Expand All @@ -672,7 +667,6 @@ def call_action() -> None:
)

if not self._duration:
# Call action immediately if duration is not specified or 0
call_action()
return

Expand Down
Loading
Loading