Skip to content

Commit

Permalink
Merge branch 'dev' into mqtt_subs_no_coro
Browse files Browse the repository at this point in the history
  • Loading branch information
jbouwh committed May 25, 2024
2 parents bab46c8 + 6fc6d10 commit 3714cbe
Show file tree
Hide file tree
Showing 41 changed files with 406 additions and 313 deletions.
2 changes: 1 addition & 1 deletion homeassistant/components/envisalink/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/envisalink",
"iot_class": "local_push",
"loggers": ["pyenvisalink"],
"requirements": ["pyenvisalink==4.6"]
"requirements": ["pyenvisalink==4.7"]
}
20 changes: 9 additions & 11 deletions homeassistant/components/firmata/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Support for Arduino-compatible Microcontrollers through Firmata."""

import asyncio
from copy import copy
import logging

Expand Down Expand Up @@ -212,16 +211,15 @@ async def handle_shutdown(event) -> None:
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Shutdown and close a Firmata board for a config entry."""
_LOGGER.debug("Closing Firmata board %s", config_entry.data[CONF_NAME])

unload_entries = []
for conf, platform in CONF_PLATFORM_MAP.items():
if conf in config_entry.data:
unload_entries.append(
hass.config_entries.async_forward_entry_unload(config_entry, platform)
)
results = []
if unload_entries:
results = await asyncio.gather(*unload_entries)
results: list[bool] = []
if platforms := [
platform
for conf, platform in CONF_PLATFORM_MAP.items()
if conf in config_entry.data
]:
results.append(
await hass.config_entries.async_unload_platforms(config_entry, platforms)
)
results.append(await hass.data[DOMAIN].pop(config_entry.entry_id).async_reset())

return False not in results
15 changes: 7 additions & 8 deletions homeassistant/components/forecast_solar/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
CONF_DAMPING_EVENING,
CONF_DAMPING_MORNING,
CONF_MODULES_POWER,
DOMAIN,
)
from .coordinator import ForecastSolarDataUpdateCoordinator

PLATFORMS = [Platform.SENSOR]

ForecastSolarConfigEntry = ConfigEntry[ForecastSolarDataUpdateCoordinator]


async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Migrate old config entry."""
Expand All @@ -36,12 +37,14 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return True


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_setup_entry(
hass: HomeAssistant, entry: ForecastSolarConfigEntry
) -> bool:
"""Set up Forecast.Solar from a config entry."""
coordinator = ForecastSolarDataUpdateCoordinator(hass, entry)
await coordinator.async_config_entry_first_refresh()

hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
entry.runtime_data = coordinator

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

Expand All @@ -52,11 +55,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)


async def async_update_options(hass: HomeAssistant, entry: ConfigEntry) -> None:
Expand Down
10 changes: 3 additions & 7 deletions homeassistant/components/forecast_solar/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@

from typing import Any

from forecast_solar import Estimate

from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

from .const import DOMAIN
from . import ForecastSolarConfigEntry

TO_REDACT = {
CONF_API_KEY,
Expand All @@ -22,10 +18,10 @@


async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry
hass: HomeAssistant, entry: ForecastSolarConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
coordinator: DataUpdateCoordinator[Estimate] = hass.data[DOMAIN][entry.entry_id]
coordinator = entry.runtime_data

return {
"entry": {
Expand Down
8 changes: 5 additions & 3 deletions homeassistant/components/forecast_solar/energy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@

from homeassistant.core import HomeAssistant

from .const import DOMAIN
from .coordinator import ForecastSolarDataUpdateCoordinator


async def async_get_solar_forecast(
hass: HomeAssistant, config_entry_id: str
) -> dict[str, dict[str, float | int]] | None:
"""Get solar forecast for a config entry ID."""
if (coordinator := hass.data[DOMAIN].get(config_entry_id)) is None:
if (
entry := hass.config_entries.async_get_entry(config_entry_id)
) is None or not isinstance(entry.runtime_data, ForecastSolarDataUpdateCoordinator):
return None

return {
"wh_hours": {
timestamp.isoformat(): val
for timestamp, val in coordinator.data.wh_period.items()
for timestamp, val in entry.runtime_data.data.wh_period.items()
}
}
8 changes: 5 additions & 3 deletions homeassistant/components/forecast_solar/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfEnergy, UnitOfPower
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from . import ForecastSolarConfigEntry
from .const import DOMAIN
from .coordinator import ForecastSolarDataUpdateCoordinator

Expand Down Expand Up @@ -133,10 +133,12 @@ class ForecastSolarSensorEntityDescription(SensorEntityDescription):


async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
hass: HomeAssistant,
entry: ForecastSolarConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Defer sensor setup to the shared sensor module."""
coordinator: ForecastSolarDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
coordinator = entry.runtime_data

async_add_entities(
ForecastSolarSensorEntity(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
RECOMMENDED_OPTIONS = {
CONF_RECOMMENDED: True,
CONF_LLM_HASS_API: llm.LLM_API_ASSIST,
CONF_PROMPT: "",
CONF_PROMPT: DEFAULT_PROMPT,
}


Expand Down Expand Up @@ -181,8 +181,7 @@ async def google_generative_ai_config_option_schema(
schema = {
vol.Optional(
CONF_PROMPT,
description={"suggested_value": options.get(CONF_PROMPT)},
default=DEFAULT_PROMPT,
description={"suggested_value": options.get(CONF_PROMPT, DEFAULT_PROMPT)},
): TemplateSelector(),
vol.Optional(
CONF_LLM_HASS_API,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@
CONF_HATE_BLOCK_THRESHOLD = "hate_block_threshold"
CONF_SEXUAL_BLOCK_THRESHOLD = "sexual_block_threshold"
CONF_DANGEROUS_BLOCK_THRESHOLD = "dangerous_block_threshold"
RECOMMENDED_HARM_BLOCK_THRESHOLD = "BLOCK_LOW_AND_ABOVE"
RECOMMENDED_HARM_BLOCK_THRESHOLD = "BLOCK_MEDIUM_AND_ABOVE"
Original file line number Diff line number Diff line change
Expand Up @@ -205,15 +205,6 @@ async def async_process(
messages = [{}, {}]

try:
prompt = template.Template(
self.entry.options.get(CONF_PROMPT, DEFAULT_PROMPT), self.hass
).async_render(
{
"ha_name": self.hass.config.location_name,
},
parse_result=False,
)

if llm_api:
empty_tool_input = llm.ToolInput(
tool_name="",
Expand All @@ -226,9 +217,24 @@ async def async_process(
device_id=user_input.device_id,
)

prompt = (
await llm_api.async_get_api_prompt(empty_tool_input) + "\n" + prompt
api_prompt = await llm_api.async_get_api_prompt(empty_tool_input)

else:
api_prompt = llm.PROMPT_NO_API_CONFIGURED

prompt = "\n".join(
(
template.Template(
self.entry.options.get(CONF_PROMPT, DEFAULT_PROMPT), self.hass
).async_render(
{
"ha_name": self.hass.config.location_name,
},
parse_result=False,
),
api_prompt,
)
)

except TemplateError as err:
LOGGER.error("Error rendering prompt: %s", err)
Expand Down Expand Up @@ -257,10 +263,20 @@ async def async_process(
genai_types.BlockedPromptException,
genai_types.StopCandidateException,
) as err:
LOGGER.error("Error sending message: %s", err)
LOGGER.error("Error sending message: %s %s", type(err), err)

if isinstance(
err, genai_types.StopCandidateException
) and "finish_reason: SAFETY\n" in str(err):
error = "The message got blocked by your safety settings"
else:
error = (
f"Sorry, I had a problem talking to Google Generative AI: {err}"
)

intent_response.async_set_error(
intent.IntentResponseErrorCode.UNKNOWN,
f"Sorry, I had a problem talking to Google Generative AI: {err}",
error,
)
return conversation.ConversationResult(
response=intent_response, conversation_id=conversation_id
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/integration/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"domain": "integration",
"name": "Integration - Riemann sum integral",
"name": "Integral",
"after_dependencies": ["counter"],
"codeowners": ["@dgomes"],
"config_flow": true,
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/integration/strings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"title": "Integration - Riemann sum integral sensor",
"title": "Integral sensor",
"config": {
"step": {
"user": {
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/meraki/device_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
CONF_VALIDATOR = "validator"
CONF_SECRET = "secret"
URL = "/api/meraki"
VERSION = "2.0"
ACCEPTED_VERSIONS = ["2.0", "2.1"]


_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -74,7 +74,7 @@ async def post(self, request):
if data["secret"] != self.secret:
_LOGGER.error("Invalid Secret received from Meraki")
return self.json_message("Invalid secret", HTTPStatus.UNPROCESSABLE_ENTITY)
if data["version"] != VERSION:
if data["version"] not in ACCEPTED_VERSIONS:
_LOGGER.error("Invalid API version: %s", data["version"])
return self.json_message("Invalid version", HTTPStatus.UNPROCESSABLE_ENTITY)
_LOGGER.debug("Valid Secret")
Expand Down
17 changes: 3 additions & 14 deletions homeassistant/components/mqtt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,24 +523,13 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
mqtt_client = mqtt_data.client

# Unload publish and dump services.
hass.services.async_remove(
DOMAIN,
SERVICE_PUBLISH,
)
hass.services.async_remove(
DOMAIN,
SERVICE_DUMP,
)
hass.services.async_remove(DOMAIN, SERVICE_PUBLISH)
hass.services.async_remove(DOMAIN, SERVICE_DUMP)

# Stop the discovery
await discovery.async_stop(hass)
# Unload the platforms
await asyncio.gather(
*(
hass.config_entries.async_forward_entry_unload(entry, component)
for component in mqtt_data.platforms_loaded
)
)
await hass.config_entries.async_unload_platforms(entry, mqtt_data.platforms_loaded)
mqtt_data.platforms_loaded = set()
await asyncio.sleep(0)
# Unsubscribe reload dispatchers
Expand Down
24 changes: 10 additions & 14 deletions homeassistant/components/mqtt/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,10 +444,10 @@ def __init__(
self.config_entry = config_entry
self.conf = conf

self._simple_subscriptions: defaultdict[str, list[Subscription]] = defaultdict(
list
self._simple_subscriptions: defaultdict[str, set[Subscription]] = defaultdict(
set
)
self._wildcard_subscriptions: list[Subscription] = []
self._wildcard_subscriptions: set[Subscription] = set()
# _retained_topics prevents a Subscription from receiving a
# retained message more than once per topic. This prevents flooding
# already active subscribers when new subscribers subscribe to a topic
Expand All @@ -467,7 +467,7 @@ def __init__(
self._should_reconnect: bool = True
self._available_future: asyncio.Future[bool] | None = None

self._max_qos: dict[str, int] = {} # topic, max qos
self._max_qos: defaultdict[str, int] = defaultdict(int) # topic, max qos
self._pending_subscriptions: dict[str, int] = {} # topic, qos
self._unsubscribe_debouncer = EnsureJobAfterCooldown(
UNSUBSCRIBE_COOLDOWN, self._async_perform_unsubscribes
Expand Down Expand Up @@ -804,9 +804,9 @@ def _async_track_subscription(self, subscription: Subscription) -> None:
The caller is responsible clearing the cache of _matching_subscriptions.
"""
if subscription.is_simple_match:
self._simple_subscriptions[subscription.topic].append(subscription)
self._simple_subscriptions[subscription.topic].add(subscription)
else:
self._wildcard_subscriptions.append(subscription)
self._wildcard_subscriptions.add(subscription)

@callback
def _async_untrack_subscription(self, subscription: Subscription) -> None:
Expand Down Expand Up @@ -835,8 +835,8 @@ def _async_queue_subscriptions(
"""Queue requested subscriptions."""
for subscription in subscriptions:
topic, qos = subscription
max_qos = max(qos, self._max_qos.setdefault(topic, qos))
self._max_qos[topic] = max_qos
if (max_qos := self._max_qos[topic]) < qos:
self._max_qos[topic] = (max_qos := qos)
self._pending_subscriptions[topic] = max_qos
# Cancel any pending unsubscribe since we are subscribing now
if topic in self._pending_unsubscribes:
Expand Down Expand Up @@ -1270,19 +1270,15 @@ async def _discovery_cooldown(self) -> None:

last_discovery = self._mqtt_data.last_discovery
last_subscribe = now if self._pending_subscriptions else self._last_subscribe
wait_until = max(
last_discovery + DISCOVERY_COOLDOWN, last_subscribe + DISCOVERY_COOLDOWN
)
wait_until = max(last_discovery, last_subscribe) + DISCOVERY_COOLDOWN
while now < wait_until:
await asyncio.sleep(wait_until - now)
now = time.monotonic()
last_discovery = self._mqtt_data.last_discovery
last_subscribe = (
now if self._pending_subscriptions else self._last_subscribe
)
wait_until = max(
last_discovery + DISCOVERY_COOLDOWN, last_subscribe + DISCOVERY_COOLDOWN
)
wait_until = max(last_discovery, last_subscribe) + DISCOVERY_COOLDOWN


def _matcher_for_topic(subscription: str) -> Callable[[str], bool]:
Expand Down
Loading

0 comments on commit 3714cbe

Please sign in to comment.