Skip to content

Commit

Permalink
2023.9.1 (#99950)
Browse files Browse the repository at this point in the history
  • Loading branch information
bramkragten authored Sep 8, 2023
2 parents f70469d + 2dcf6a6 commit 9e6ac1d
Show file tree
Hide file tree
Showing 49 changed files with 729 additions and 199 deletions.
1 change: 0 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,6 @@ omit =
homeassistant/components/freebox/device_tracker.py
homeassistant/components/freebox/home_base.py
homeassistant/components/freebox/router.py
homeassistant/components/freebox/sensor.py
homeassistant/components/freebox/switch.py
homeassistant/components/fritz/common.py
homeassistant/components/fritz/device_tracker.py
Expand Down
7 changes: 7 additions & 0 deletions homeassistant/components/alexa/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ class AlexaUnsupportedThermostatModeError(AlexaError):
error_type = "UNSUPPORTED_THERMOSTAT_MODE"


class AlexaUnsupportedThermostatTargetStateError(AlexaError):
"""Class to represent unsupported climate target state error."""

namespace = "Alexa.ThermostatController"
error_type = "INVALID_TARGET_STATE"


class AlexaTempRangeError(AlexaError):
"""Class to represent TempRange errors."""

Expand Down
9 changes: 8 additions & 1 deletion homeassistant/components/alexa/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
AlexaSecurityPanelAuthorizationRequired,
AlexaTempRangeError,
AlexaUnsupportedThermostatModeError,
AlexaUnsupportedThermostatTargetStateError,
AlexaVideoActionNotPermittedForContentError,
)
from .state_report import AlexaDirective, AlexaResponse, async_enable_proactive_mode
Expand Down Expand Up @@ -911,7 +912,13 @@ async def async_api_adjust_target_temp(
}
)
else:
target_temp = float(entity.attributes[ATTR_TEMPERATURE]) + temp_delta
current_target_temp: str | None = entity.attributes.get(ATTR_TEMPERATURE)
if current_target_temp is None:
raise AlexaUnsupportedThermostatTargetStateError(
"The current target temperature is not set, "
"cannot adjust target temperature"
)
target_temp = float(current_target_temp) + temp_delta

if target_temp < min_temp or target_temp > max_temp:
raise AlexaTempRangeError(hass, target_temp, min_temp, max_temp)
Expand Down
23 changes: 18 additions & 5 deletions homeassistant/components/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@
)
import homeassistant.core as ha
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceNotFound, TemplateError, Unauthorized
from homeassistant.exceptions import (
InvalidEntityFormatError,
InvalidStateError,
ServiceNotFound,
TemplateError,
Unauthorized,
)
from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers.json import json_dumps
from homeassistant.helpers.service import async_get_all_descriptions
Expand Down Expand Up @@ -222,7 +228,7 @@ async def post(self, request, entity_id):
"""Update state of entity."""
if not request["hass_user"].is_admin:
raise Unauthorized(entity_id=entity_id)
hass = request.app["hass"]
hass: HomeAssistant = request.app["hass"]
try:
data = await request.json()
except ValueError:
Expand All @@ -237,9 +243,16 @@ async def post(self, request, entity_id):
is_new_state = hass.states.get(entity_id) is None

# Write state
hass.states.async_set(
entity_id, new_state, attributes, force_update, self.context(request)
)
try:
hass.states.async_set(
entity_id, new_state, attributes, force_update, self.context(request)
)
except InvalidEntityFormatError:
return self.json_message(
"Invalid entity ID specified.", HTTPStatus.BAD_REQUEST
)
except InvalidStateError:
return self.json_message("Invalid state specified.", HTTPStatus.BAD_REQUEST)

# Read the state back for our response
status_code = HTTPStatus.CREATED if is_new_state else HTTPStatus.OK
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/bluetooth/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
"quality_scale": "internal",
"requirements": [
"bleak==0.21.0",
"bleak-retry-connector==3.1.2",
"bluetooth-adapters==0.16.0",
"bluetooth-auto-recovery==1.2.1",
"bleak-retry-connector==3.1.3",
"bluetooth-adapters==0.16.1",
"bluetooth-auto-recovery==1.2.2",
"bluetooth-data-tools==1.11.0",
"dbus-fast==1.94.1"
"dbus-fast==1.95.2"
]
}
2 changes: 2 additions & 0 deletions homeassistant/components/elkm1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,8 @@ async def async_added_to_hass(self) -> None:
def device_info(self) -> DeviceInfo:
"""Device info connecting via the ElkM1 system."""
return DeviceInfo(
name=self._element.name,
identifiers={(DOMAIN, self._unique_id)},
via_device=(DOMAIN, f"{self._prefix}_system"),
)

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/emulated_kasa/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"iot_class": "local_push",
"loggers": ["sense_energy"],
"quality_scale": "internal",
"requirements": ["sense_energy==0.12.0"]
"requirements": ["sense_energy==0.12.1"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/enphase_envoy/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"documentation": "https://www.home-assistant.io/integrations/enphase_envoy",
"iot_class": "local_polling",
"loggers": ["pyenphase"],
"requirements": ["pyenphase==1.9.1"],
"requirements": ["pyenphase==1.11.0"],
"zeroconf": [
{
"type": "_enphase-envoy._tcp.local."
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/epson/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/epson",
"iot_class": "local_polling",
"loggers": ["epson_projector"],
"requirements": ["epson-projector==0.5.0"]
"requirements": ["epson-projector==0.5.1"]
}
4 changes: 2 additions & 2 deletions homeassistant/components/epson/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from epson_projector import Projector, ProjectorUnavailableError
from epson_projector.const import (
BACK,
BUSY,
BUSY_CODES,
CMODE,
CMODE_LIST,
CMODE_LIST_SET,
Expand Down Expand Up @@ -147,7 +147,7 @@ async def async_update(self) -> None:
self._attr_volume_level = float(volume)
except ValueError:
self._attr_volume_level = None
elif power_state == BUSY:
elif power_state in BUSY_CODES:
self._attr_state = MediaPlayerState.ON
else:
self._attr_state = MediaPlayerState.OFF
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/freebox/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,7 @@ class FreeboxHomeCategory(enum.StrEnum):

HOME_COMPATIBLE_CATEGORIES = [
FreeboxHomeCategory.CAMERA,
FreeboxHomeCategory.DWS,
FreeboxHomeCategory.KFB,
FreeboxHomeCategory.PIR,
]
7 changes: 6 additions & 1 deletion homeassistant/components/freebox/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,12 @@ async def _update_disks_sensors(self) -> None:
fbx_disks: list[dict[str, Any]] = await self._api.storage.get_disks() or []

for fbx_disk in fbx_disks:
self.disks[fbx_disk["id"]] = fbx_disk
disk: dict[str, Any] = {**fbx_disk}
disk_part: dict[int, dict[str, Any]] = {}
for fbx_disk_part in fbx_disk["partitions"]:
disk_part[fbx_disk_part["id"]] = fbx_disk_part
disk["partitions"] = disk_part
self.disks[fbx_disk["id"]] = disk

async def _update_raids_sensors(self) -> None:
"""Update Freebox raids."""
Expand Down
13 changes: 7 additions & 6 deletions homeassistant/components/freebox/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ async def async_setup_entry(
entities.extend(
FreeboxDiskSensor(router, disk, partition, description)
for disk in router.disks.values()
for partition in disk["partitions"]
for partition in disk["partitions"].values()
for description in DISK_PARTITION_SENSORS
)

Expand Down Expand Up @@ -197,7 +197,8 @@ def __init__(
) -> None:
"""Initialize a Freebox disk sensor."""
super().__init__(router, description)
self._partition = partition
self._disk_id = disk["id"]
self._partition_id = partition["id"]
self._attr_name = f"{partition['label']} {description.name}"
self._attr_unique_id = (
f"{router.mac} {description.key} {disk['id']} {partition['id']}"
Expand All @@ -218,10 +219,10 @@ def __init__(
def async_update_state(self) -> None:
"""Update the Freebox disk sensor."""
value = None
if self._partition.get("total_bytes"):
value = round(
self._partition["free_bytes"] * 100 / self._partition["total_bytes"], 2
)
disk: dict[str, Any] = self._router.disks[self._disk_id]
partition: dict[str, Any] = disk["partitions"][self._partition_id]
if partition.get("total_bytes"):
value = round(partition["free_bytes"] * 100 / partition["total_bytes"], 2)
self._attr_native_value = value


Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/frontend/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20230906.1"]
"requirements": ["home-assistant-frontend==20230908.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/hydrawise/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def _handle_coordinator_update(self) -> None:
"""Get the latest data and updates the state."""
LOGGER.debug("Updating Hydrawise binary sensor: %s", self.name)
if self.entity_description.key == "status":
self._attr_is_on = self.coordinator.api.status == "All good!"
self._attr_is_on = self.coordinator.last_update_success
elif self.entity_description.key == "is_watering":
relay_data = self.coordinator.api.relays_by_zone_number[self.data["relay"]]
self._attr_is_on = relay_data["timestr"] == "Now"
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/livisi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ async def async_setup_entry(hass: core.HomeAssistant, entry: ConfigEntry) -> boo
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
config_entry_id=coordinator.serial_number,
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, entry.entry_id)},
manufacturer="Livisi",
name=f"SHC {coordinator.controller_type} {coordinator.serial_number}",
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/lutron_caseta/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
},
"iot_class": "local_push",
"loggers": ["pylutron_caseta"],
"requirements": ["pylutron-caseta==0.18.1"],
"requirements": ["pylutron-caseta==0.18.2"],
"zeroconf": [
{
"type": "_lutron._tcp.local.",
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/mill/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/mill",
"iot_class": "local_polling",
"loggers": ["mill", "mill_local"],
"requirements": ["millheater==0.11.1", "mill-local==0.2.0"]
"requirements": ["millheater==0.11.2", "mill-local==0.2.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/modbus/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"iot_class": "local_polling",
"loggers": ["pymodbus"],
"quality_scale": "gold",
"requirements": ["pymodbus==3.4.1"]
"requirements": ["pymodbus==3.5.1"]
}
4 changes: 2 additions & 2 deletions homeassistant/components/mqtt/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def state_message_received(msg: ReceiveMessage) -> None:
"Empty template output for entity: %s with state topic: %s."
" Payload: '%s', with value template '%s'"
),
self._config[CONF_NAME],
self.entity_id,
self._config[CONF_STATE_TOPIC],
msg.payload,
self._config.get(CONF_VALUE_TEMPLATE),
Expand All @@ -240,7 +240,7 @@ def state_message_received(msg: ReceiveMessage) -> None:
"No matching payload found for entity: %s with state topic: %s."
" Payload: '%s'%s"
),
self._config[CONF_NAME],
self.entity_id,
self._config[CONF_STATE_TOPIC],
msg.payload,
template_info,
Expand Down
49 changes: 37 additions & 12 deletions homeassistant/components/noaa_tides/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from datetime import datetime, timedelta
import logging
from typing import TYPE_CHECKING, Any, Literal, TypedDict

import noaa_coops as coops
import requests
Expand All @@ -17,6 +18,9 @@
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util.unit_system import METRIC_SYSTEM

if TYPE_CHECKING:
from pandas import Timestamp

_LOGGER = logging.getLogger(__name__)

CONF_STATION_ID = "station_id"
Expand Down Expand Up @@ -76,40 +80,56 @@ def setup_platform(
add_entities([noaa_sensor], True)


class NOAATidesData(TypedDict):
"""Representation of a single tide."""

time_stamp: list[Timestamp]
hi_lo: list[Literal["L"] | Literal["H"]]
predicted_wl: list[float]


class NOAATidesAndCurrentsSensor(SensorEntity):
"""Representation of a NOAA Tides and Currents sensor."""

_attr_attribution = "Data provided by NOAA"

def __init__(self, name, station_id, timezone, unit_system, station):
def __init__(self, name, station_id, timezone, unit_system, station) -> None:
"""Initialize the sensor."""
self._name = name
self._station_id = station_id
self._timezone = timezone
self._unit_system = unit_system
self._station = station
self.data = None
self.data: NOAATidesData | None = None

@property
def name(self):
def name(self) -> str:
"""Return the name of the sensor."""
return self._name

@property
def extra_state_attributes(self):
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes of this device."""
attr = {}
attr: dict[str, Any] = {}
if self.data is None:
return attr
if self.data["hi_lo"][1] == "H":
attr["high_tide_time"] = self.data.index[1].strftime("%Y-%m-%dT%H:%M")
attr["high_tide_time"] = self.data["time_stamp"][1].strftime(
"%Y-%m-%dT%H:%M"
)
attr["high_tide_height"] = self.data["predicted_wl"][1]
attr["low_tide_time"] = self.data.index[2].strftime("%Y-%m-%dT%H:%M")
attr["low_tide_time"] = self.data["time_stamp"][2].strftime(
"%Y-%m-%dT%H:%M"
)
attr["low_tide_height"] = self.data["predicted_wl"][2]
elif self.data["hi_lo"][1] == "L":
attr["low_tide_time"] = self.data.index[1].strftime("%Y-%m-%dT%H:%M")
attr["low_tide_time"] = self.data["time_stamp"][1].strftime(
"%Y-%m-%dT%H:%M"
)
attr["low_tide_height"] = self.data["predicted_wl"][1]
attr["high_tide_time"] = self.data.index[2].strftime("%Y-%m-%dT%H:%M")
attr["high_tide_time"] = self.data["time_stamp"][2].strftime(
"%Y-%m-%dT%H:%M"
)
attr["high_tide_height"] = self.data["predicted_wl"][2]
return attr

Expand All @@ -118,7 +138,7 @@ def native_value(self):
"""Return the state of the device."""
if self.data is None:
return None
api_time = self.data.index[0]
api_time = self.data["time_stamp"][0]
if self.data["hi_lo"][0] == "H":
tidetime = api_time.strftime("%-I:%M %p")
return f"High tide at {tidetime}"
Expand All @@ -142,8 +162,13 @@ def update(self) -> None:
units=self._unit_system,
time_zone=self._timezone,
)
self.data = df_predictions.head()
_LOGGER.debug("Data = %s", self.data)
api_data = df_predictions.head()
self.data = NOAATidesData(
time_stamp=list(api_data.index),
hi_lo=list(api_data["hi_lo"].values),
predicted_wl=list(api_data["predicted_wl"].values),
)
_LOGGER.debug("Data = %s", api_data)
_LOGGER.debug(
"Recent Tide data queried with start time set to %s",
begin.strftime("%m-%d-%Y %H:%M"),
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/overkiz/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["boto3", "botocore", "pyhumps", "pyoverkiz", "s3transfer"],
"requirements": ["pyoverkiz==1.10.1"],
"requirements": ["pyoverkiz==1.9.0"],
"zeroconf": [
{
"type": "_kizbox._tcp.local.",
Expand Down
Loading

0 comments on commit 9e6ac1d

Please sign in to comment.