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/alexa_devices/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "platinum",
"requirements": ["aioamazondevices==10.0.0"]
"requirements": ["aioamazondevices==11.0.2"]
}
11 changes: 7 additions & 4 deletions homeassistant/components/hikvision/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
CONF_SSL,
CONF_USERNAME,
)
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant, callback
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import config_validation as cv, issue_registry as ir
from homeassistant.helpers.device_registry import DeviceInfo
Expand Down Expand Up @@ -227,7 +227,10 @@ async def async_added_to_hass(self) -> None:
# Register callback with pyhik
self._camera.add_update_callback(self._update_callback, self._callback_id)

@callback
def _update_callback(self, msg: str) -> None:
"""Update the sensor's state when callback is triggered."""
self.async_write_ha_state()
"""Update the sensor's state when callback is triggered.

This is called from pyhik's event stream thread, so we use
schedule_update_ha_state which is thread-safe.
"""
self.schedule_update_ha_state()
12 changes: 6 additions & 6 deletions homeassistant/components/hydrawise/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,21 @@ def _get_water_use(sensor: HydrawiseSensor) -> ControllerWaterUseSummary:
HydrawiseSensorEntityDescription(
key="daily_total_water_use",
translation_key="daily_total_water_use",
device_class=SensorDeviceClass.VOLUME,
device_class=SensorDeviceClass.WATER,
suggested_display_precision=1,
value_fn=lambda sensor: _get_water_use(sensor).total_use,
),
HydrawiseSensorEntityDescription(
key="daily_active_water_use",
translation_key="daily_active_water_use",
device_class=SensorDeviceClass.VOLUME,
device_class=SensorDeviceClass.WATER,
suggested_display_precision=1,
value_fn=lambda sensor: _get_water_use(sensor).total_active_use,
),
HydrawiseSensorEntityDescription(
key="daily_inactive_water_use",
translation_key="daily_inactive_water_use",
device_class=SensorDeviceClass.VOLUME,
device_class=SensorDeviceClass.WATER,
suggested_display_precision=1,
value_fn=lambda sensor: _get_water_use(sensor).total_inactive_use,
),
Expand All @@ -91,7 +91,7 @@ def _get_water_use(sensor: HydrawiseSensor) -> ControllerWaterUseSummary:
HydrawiseSensorEntityDescription(
key="daily_active_water_use",
translation_key="daily_active_water_use",
device_class=SensorDeviceClass.VOLUME,
device_class=SensorDeviceClass.WATER,
suggested_display_precision=1,
value_fn=lambda sensor: float(
_get_water_use(sensor).active_use_by_zone_id.get(sensor.zone.id, 0.0)
Expand Down Expand Up @@ -204,7 +204,7 @@ class HydrawiseSensor(HydrawiseEntity, SensorEntity):
@property
def native_unit_of_measurement(self) -> str | None:
"""Return the unit_of_measurement of the sensor."""
if self.entity_description.device_class != SensorDeviceClass.VOLUME:
if self.entity_description.device_class != SensorDeviceClass.WATER:
return self.entity_description.native_unit_of_measurement
return (
UnitOfVolume.GALLONS
Expand All @@ -217,7 +217,7 @@ def icon(self) -> str | None:
"""Icon of the entity based on the value."""
if (
self.entity_description.key in FLOW_MEASUREMENT_KEYS
and self.entity_description.device_class == SensorDeviceClass.VOLUME
and self.entity_description.device_class == SensorDeviceClass.WATER
and round(self.state, 2) == 0.0
):
return "mdi:water-outline"
Expand Down
12 changes: 3 additions & 9 deletions homeassistant/components/netgear_lte/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@

PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.NOTIFY,
Platform.SENSOR,
]

Expand All @@ -61,6 +60,7 @@
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up Netgear LTE component."""
hass.data[DATA_HASS_CONFIG] = config
async_setup_services(hass)

return True

Expand Down Expand Up @@ -96,19 +96,15 @@ def fire_sms_event(sms: SMS) -> None:
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator

async_setup_services(hass)

await discovery.async_load_platform(
hass,
Platform.NOTIFY,
DOMAIN,
{CONF_NAME: entry.title, "modem": modem},
{CONF_NAME: entry.title, "modem": modem, "entry": entry},
hass.data[DATA_HASS_CONFIG],
)

await hass.config_entries.async_forward_entry_setups(
entry, [platform for platform in PLATFORMS if platform != Platform.NOTIFY]
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True

Expand All @@ -118,7 +114,5 @@ async def async_unload_entry(hass: HomeAssistant, entry: NetgearLTEConfigEntry)
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if not hass.config_entries.async_loaded_entries(DOMAIN):
hass.data.pop(DOMAIN, None)
for service_name in hass.services.async_services()[DOMAIN]:
hass.services.async_remove(DOMAIN, service_name)

return unload_ok
5 changes: 1 addition & 4 deletions homeassistant/components/netgear_lte/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_create_clientsession

from .const import DEFAULT_HOST, DOMAIN, LOGGER, MANUFACTURER
from .const import DEFAULT_HOST, DOMAIN, MANUFACTURER


class NetgearLTEFlowHandler(ConfigFlow, domain=DOMAIN):
Expand Down Expand Up @@ -72,9 +72,6 @@ async def _async_validate_input(self, host: str, password: str) -> Information:
info = await modem.information()
except Error as ex:
raise InputValidationError("cannot_connect") from ex
except Exception as ex:
LOGGER.exception("Unexpected exception")
raise InputValidationError("unknown") from ex
await modem.logout()
return info

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/netgear_lte/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["eternalegypt"],
"requirements": ["eternalegypt==0.0.16"]
"requirements": ["eternalegypt==0.0.18"]
}
1 change: 1 addition & 0 deletions homeassistant/components/netgear_lte/notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def __init__(
"""Initialize the service."""
self.config = config
self.modem: Modem = discovery_info["modem"]
discovery_info["entry"].async_on_unload(self.async_unregister_services)

async def async_send_message(self, message="", **kwargs):
"""Send a message to a user."""
Expand Down
9 changes: 6 additions & 3 deletions homeassistant/components/netgear_lte/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv

from .const import (
Expand All @@ -14,7 +15,6 @@
AUTOCONNECT_MODES,
DOMAIN,
FAILOVER_MODES,
LOGGER,
)
from .coordinator import NetgearLTEConfigEntry

Expand Down Expand Up @@ -56,8 +56,11 @@ async def _service_handler(call: ServiceCall) -> None:
break

if not entry or not (modem := entry.runtime_data.modem).token:
LOGGER.error("%s: host %s unavailable", call.service, host)
return
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="config_entry_not_found",
translation_placeholders={"service": call.service},
)

if call.service == SERVICE_DELETE_SMS:
for sms_id in call.data[ATTR_SMS_ID]:
Expand Down
5 changes: 5 additions & 0 deletions homeassistant/components/netgear_lte/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@
}
}
},
"exceptions": {
"config_entry_not_found": {
"message": "Failed to perform action \"{service}\". Config entry for target not found"
}
},
"services": {
"connect_lte": {
"description": "Asks the modem to establish the LTE connection.",
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/sonos/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"quality_scale": "bronze",
"requirements": [
"defusedxml==0.7.1",
"soco==0.30.13",
"soco==0.30.14",
"sonos-websocket==0.1.3"
],
"ssdp": [
Expand Down
2 changes: 0 additions & 2 deletions homeassistant/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2412,10 +2412,8 @@ class Service:

__slots__ = [
"description_placeholders",
"domain",
"job",
"schema",
"service",
"supports_response",
]

Expand Down
6 changes: 3 additions & 3 deletions requirements_all.txt

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

6 changes: 3 additions & 3 deletions requirements_test_all.txt

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

4 changes: 4 additions & 0 deletions tests/components/hikvision/test_binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,10 @@ async def test_binary_sensor_update_callback(
callback_func = add_callback_call[0][0]
callback_func("motion detected")

# Wait for the event loop to process the scheduled state update
# (callback uses call_soon_threadsafe to schedule update in event loop)
await hass.async_block_till_done()

# Verify state was updated
state = hass.states.get("binary_sensor.front_camera_motion")
assert state is not None
Expand Down
20 changes: 10 additions & 10 deletions tests/components/hydrawise/snapshots/test_sensor.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
'suggested_unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
}),
'original_device_class': <SensorDeviceClass.VOLUME: 'volume'>,
'original_device_class': <SensorDeviceClass.WATER: 'water'>,
'original_icon': None,
'original_name': 'Daily active water use',
'platform': 'hydrawise',
Expand All @@ -44,7 +44,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by hydrawise.com',
'device_class': 'volume',
'device_class': 'water',
'friendly_name': 'Home Controller Daily active water use',
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
Expand Down Expand Up @@ -139,7 +139,7 @@
'suggested_unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
}),
'original_device_class': <SensorDeviceClass.VOLUME: 'volume'>,
'original_device_class': <SensorDeviceClass.WATER: 'water'>,
'original_icon': None,
'original_name': 'Daily inactive water use',
'platform': 'hydrawise',
Expand All @@ -155,7 +155,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by hydrawise.com',
'device_class': 'volume',
'device_class': 'water',
'friendly_name': 'Home Controller Daily inactive water use',
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
Expand Down Expand Up @@ -196,7 +196,7 @@
'suggested_unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
}),
'original_device_class': <SensorDeviceClass.VOLUME: 'volume'>,
'original_device_class': <SensorDeviceClass.WATER: 'water'>,
'original_icon': None,
'original_name': 'Daily total water use',
'platform': 'hydrawise',
Expand All @@ -212,7 +212,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by hydrawise.com',
'device_class': 'volume',
'device_class': 'water',
'friendly_name': 'Home Controller Daily total water use',
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
Expand Down Expand Up @@ -253,7 +253,7 @@
'suggested_unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
}),
'original_device_class': <SensorDeviceClass.VOLUME: 'volume'>,
'original_device_class': <SensorDeviceClass.WATER: 'water'>,
'original_icon': None,
'original_name': 'Daily active water use',
'platform': 'hydrawise',
Expand All @@ -269,7 +269,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by hydrawise.com',
'device_class': 'volume',
'device_class': 'water',
'friendly_name': 'Zone One Daily active water use',
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
Expand Down Expand Up @@ -464,7 +464,7 @@
'suggested_unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
}),
'original_device_class': <SensorDeviceClass.VOLUME: 'volume'>,
'original_device_class': <SensorDeviceClass.WATER: 'water'>,
'original_icon': 'mdi:water-outline',
'original_name': 'Daily active water use',
'platform': 'hydrawise',
Expand All @@ -480,7 +480,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by hydrawise.com',
'device_class': 'volume',
'device_class': 'water',
'friendly_name': 'Zone Two Daily active water use',
'icon': 'mdi:water-outline',
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
Expand Down
15 changes: 0 additions & 15 deletions tests/components/netgear_lte/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,3 @@ async def test_flow_user_cannot_connect(
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"]["base"] == "cannot_connect"


async def test_flow_user_unknown_error(hass: HomeAssistant, unknown: None) -> None:
"""Test unknown error."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={CONF_SOURCE: SOURCE_USER},
)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input=CONF_DATA,
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"]["base"] == "unknown"
Loading
Loading