Skip to content
Merged

2023.2.3 #87652

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1df82f3
Speed up purge time with newer MariaDB versions (#87409)
bdraco Feb 5, 2023
5b0c732
Add missing name field to emulated_hue config (#87456)
gregoryhaynes Feb 5, 2023
4a7aee4
Bump lupupy to 0.2.7 (#87469)
majuss Feb 6, 2023
b2ccf2e
Bump py-synologydsm-api to 2.1.4 (#87471)
mib1185 Feb 5, 2023
423d8f0
Disable uptime sensor by default in Unifi (#87484)
tkdrob Feb 5, 2023
e30ea3e
Add the correct loggers to velbus manifest.json (#87488)
cereal2nd Feb 6, 2023
8a257df
Fix recorder run history during schema migration and startup (#87492)
bdraco Feb 6, 2023
1a72b64
Bump xiaomi-ble to 0.16.1 (#87496)
Ernst79 Feb 5, 2023
08c23dd
Bump jaraco.abode to 3.3.0 (#87498)
Feb 6, 2023
f59eb6c
Bump bimmer_connected to 0.12.1 (#87506)
rikroe Feb 5, 2023
7fd3f40
Add LD2410BLE support for new firmware version (#87507)
bencorrado Feb 6, 2023
bc01df6
Bump env_canada to 0.5.28 (#87509)
michaeldavie Feb 6, 2023
590bdc1
Optimize history.get_last_state_changes query (#87554)
bdraco Feb 7, 2023
5bc49b1
OpenAI: Ignore devices without a name (#87558)
balloob Feb 7, 2023
9657567
Bump oralb-ble to 0.17.4 (#87570)
Lash-L Feb 6, 2023
d06e640
Fix matter remove config entry device (#87571)
MartinHjelmare Feb 6, 2023
0d9393c
Fix indent on slow_range_in_select for MySQL/MariaDB (#87581)
bdraco Feb 7, 2023
e0a6a6c
Fix LD2410 BLE detection with passive scans (#87584)
bdraco Feb 7, 2023
be83753
Bump inkbird-ble to 0.5.6 (#87590)
bdraco Feb 7, 2023
ab14671
Bump sensorpro-ble to 0.5.3 (#87591)
bdraco Feb 7, 2023
c4470fc
Bump thermopro-ble to 0.4.5 (#87592)
bdraco Feb 7, 2023
e67d998
Bump bluemaestro-ble to 0.2.3 (#87594)
bdraco Feb 7, 2023
a2c9f92
Bump sensorpush-ble to 1.5.5 (#87595)
bdraco Feb 7, 2023
2e541e7
Improve rainbird device reliability by sending requests serially (#87…
allenporter Feb 7, 2023
354d77d
Do not return cached values for entity states in emulated_hue (#87642)
Tho85 Feb 7, 2023
08fb16a
Bumped version to 2023.2.3
balloob Feb 7, 2023
aacd8e0
Bump pyrainbird to 2.0.0 (#86851)
allenporter Jan 29, 2023
8e8a170
Bump PyISY to 3.1.13, check portal for network buttons (#87650)
shbatm Feb 7, 2023
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/abode/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "Abode",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/abode",
"requirements": ["jaraco.abode==3.2.1"],
"requirements": ["jaraco.abode==3.3.0"],
"codeowners": ["@shred86"],
"homekit": {
"models": ["Abode", "Iota"]
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/bluemaestro/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"connectable": false
}
],
"requirements": ["bluemaestro-ble==0.2.1"],
"requirements": ["bluemaestro-ble==0.2.3"],
"dependencies": ["bluetooth_adapters"],
"codeowners": ["@bdraco"],
"iot_class": "local_push"
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/bmw_connected_drive/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"domain": "bmw_connected_drive",
"name": "BMW Connected Drive",
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
"requirements": ["bimmer_connected==0.12.0"],
"requirements": ["bimmer_connected==0.12.1"],
"codeowners": ["@gerard33", "@rikroe"],
"config_flow": true,
"iot_class": "cloud_polling",
Expand Down
153 changes: 85 additions & 68 deletions homeassistant/components/emulated_hue/hue_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,77 +634,87 @@ def get_entity_state_dict(config: Config, entity: State) -> dict[str, Any]:
# Remove the now stale cached entry.
config.cached_states.pop(entity.entity_id)

if cached_state is None:
return _build_entity_state_dict(entity)

data: dict[str, Any] = cached_state
# Make sure brightness is valid
if data[STATE_BRIGHTNESS] is None:
data[STATE_BRIGHTNESS] = HUE_API_STATE_BRI_MAX if data[STATE_ON] else 0

# Make sure hue/saturation are valid
if (data[STATE_HUE] is None) or (data[STATE_SATURATION] is None):
data[STATE_HUE] = 0
data[STATE_SATURATION] = 0

# If the light is off, set the color to off
if data[STATE_BRIGHTNESS] == 0:
data[STATE_HUE] = 0
data[STATE_SATURATION] = 0

_clamp_values(data)
return data


@lru_cache(maxsize=512)
def _build_entity_state_dict(entity: State) -> dict[str, Any]:
"""Build a state dict for an entity."""
data: dict[str, Any] = {
STATE_ON: False,
STATE_ON: entity.state != STATE_OFF,
STATE_BRIGHTNESS: None,
STATE_HUE: None,
STATE_SATURATION: None,
STATE_COLOR_TEMP: None,
}
if data[STATE_ON]:
data[STATE_BRIGHTNESS] = hass_to_hue_brightness(
entity.attributes.get(ATTR_BRIGHTNESS, 0)
)
hue_sat = entity.attributes.get(ATTR_HS_COLOR)
if hue_sat is not None:
hue = hue_sat[0]
sat = hue_sat[1]
# Convert hass hs values back to hue hs values
data[STATE_HUE] = int((hue / 360.0) * HUE_API_STATE_HUE_MAX)
data[STATE_SATURATION] = int((sat / 100.0) * HUE_API_STATE_SAT_MAX)
else:
data[STATE_HUE] = HUE_API_STATE_HUE_MIN
data[STATE_SATURATION] = HUE_API_STATE_SAT_MIN
data[STATE_COLOR_TEMP] = entity.attributes.get(ATTR_COLOR_TEMP, 0)

if cached_state is None:
data[STATE_ON] = entity.state != STATE_OFF
else:
data[STATE_BRIGHTNESS] = 0
data[STATE_HUE] = 0
data[STATE_SATURATION] = 0
data[STATE_COLOR_TEMP] = 0

if entity.domain == climate.DOMAIN:
temperature = entity.attributes.get(ATTR_TEMPERATURE, 0)
# Convert 0-100 to 0-254
data[STATE_BRIGHTNESS] = round(temperature * HUE_API_STATE_BRI_MAX / 100)
elif entity.domain == humidifier.DOMAIN:
humidity = entity.attributes.get(ATTR_HUMIDITY, 0)
# Convert 0-100 to 0-254
data[STATE_BRIGHTNESS] = round(humidity * HUE_API_STATE_BRI_MAX / 100)
elif entity.domain == media_player.DOMAIN:
level = entity.attributes.get(
ATTR_MEDIA_VOLUME_LEVEL, 1.0 if data[STATE_ON] else 0.0
)
# Convert 0.0-1.0 to 0-254
data[STATE_BRIGHTNESS] = round(min(1.0, level) * HUE_API_STATE_BRI_MAX)
elif entity.domain == fan.DOMAIN:
percentage = entity.attributes.get(ATTR_PERCENTAGE) or 0
# Convert 0-100 to 0-254
data[STATE_BRIGHTNESS] = round(percentage * HUE_API_STATE_BRI_MAX / 100)
elif entity.domain == cover.DOMAIN:
level = entity.attributes.get(ATTR_CURRENT_POSITION, 0)
data[STATE_BRIGHTNESS] = round(level / 100 * HUE_API_STATE_BRI_MAX)
_clamp_values(data)
return data

if data[STATE_ON]:
data[STATE_BRIGHTNESS] = hass_to_hue_brightness(
entity.attributes.get(ATTR_BRIGHTNESS, 0)
)
hue_sat = entity.attributes.get(ATTR_HS_COLOR)
if hue_sat is not None:
hue = hue_sat[0]
sat = hue_sat[1]
# Convert hass hs values back to hue hs values
data[STATE_HUE] = int((hue / 360.0) * HUE_API_STATE_HUE_MAX)
data[STATE_SATURATION] = int((sat / 100.0) * HUE_API_STATE_SAT_MAX)
else:
data[STATE_HUE] = HUE_API_STATE_HUE_MIN
data[STATE_SATURATION] = HUE_API_STATE_SAT_MIN
data[STATE_COLOR_TEMP] = entity.attributes.get(ATTR_COLOR_TEMP, 0)

else:
data[STATE_BRIGHTNESS] = 0
data[STATE_HUE] = 0
data[STATE_SATURATION] = 0
data[STATE_COLOR_TEMP] = 0

if entity.domain == climate.DOMAIN:
temperature = entity.attributes.get(ATTR_TEMPERATURE, 0)
# Convert 0-100 to 0-254
data[STATE_BRIGHTNESS] = round(temperature * HUE_API_STATE_BRI_MAX / 100)
elif entity.domain == humidifier.DOMAIN:
humidity = entity.attributes.get(ATTR_HUMIDITY, 0)
# Convert 0-100 to 0-254
data[STATE_BRIGHTNESS] = round(humidity * HUE_API_STATE_BRI_MAX / 100)
elif entity.domain == media_player.DOMAIN:
level = entity.attributes.get(
ATTR_MEDIA_VOLUME_LEVEL, 1.0 if data[STATE_ON] else 0.0
)
# Convert 0.0-1.0 to 0-254
data[STATE_BRIGHTNESS] = round(min(1.0, level) * HUE_API_STATE_BRI_MAX)
elif entity.domain == fan.DOMAIN:
percentage = entity.attributes.get(ATTR_PERCENTAGE) or 0
# Convert 0-100 to 0-254
data[STATE_BRIGHTNESS] = round(percentage * HUE_API_STATE_BRI_MAX / 100)
elif entity.domain == cover.DOMAIN:
level = entity.attributes.get(ATTR_CURRENT_POSITION, 0)
data[STATE_BRIGHTNESS] = round(level / 100 * HUE_API_STATE_BRI_MAX)
else:
data = cached_state
# Make sure brightness is valid
if data[STATE_BRIGHTNESS] is None:
data[STATE_BRIGHTNESS] = HUE_API_STATE_BRI_MAX if data[STATE_ON] else 0

# Make sure hue/saturation are valid
if (data[STATE_HUE] is None) or (data[STATE_SATURATION] is None):
data[STATE_HUE] = 0
data[STATE_SATURATION] = 0

# If the light is off, set the color to off
if data[STATE_BRIGHTNESS] == 0:
data[STATE_HUE] = 0
data[STATE_SATURATION] = 0

# Clamp brightness, hue, saturation, and color temp to valid values
def _clamp_values(data: dict[str, Any]) -> None:
"""Clamp brightness, hue, saturation, and color temp to valid values."""
for key, v_min, v_max in (
(STATE_BRIGHTNESS, HUE_API_STATE_BRI_MIN, HUE_API_STATE_BRI_MAX),
(STATE_HUE, HUE_API_STATE_HUE_MIN, HUE_API_STATE_HUE_MAX),
Expand All @@ -714,8 +724,6 @@ def get_entity_state_dict(config: Config, entity: State) -> dict[str, Any]:
if data[key] is not None:
data[key] = max(v_min, min(data[key], v_max))

return data


@lru_cache(maxsize=1024)
def _entity_unique_id(entity_id: str) -> str:
Expand Down Expand Up @@ -831,6 +839,7 @@ def create_hue_success_response(
def create_config_model(config: Config, request: web.Request) -> dict[str, Any]:
"""Create a config resource."""
return {
"name": "HASS BRIDGE",
"mac": "00:00:00:00:00:00",
"swversion": "01003542",
"apiversion": "1.17.0",
Expand All @@ -842,10 +851,18 @@ def create_config_model(config: Config, request: web.Request) -> dict[str, Any]:

def create_list_of_entities(config: Config, request: web.Request) -> dict[str, Any]:
"""Create a list of all entities."""
json_response: dict[str, Any] = {
config.entity_id_to_number(state.entity_id): state_to_json(config, state)
for state in config.get_exposed_states()
}
hass: core.HomeAssistant = request.app["hass"]

json_response: dict[str, Any] = {}
for cached_state in config.get_exposed_states():
entity_id = cached_state.entity_id
state = hass.states.get(entity_id)
assert state is not None

json_response[config.entity_id_to_number(entity_id)] = state_to_json(
config, state
)

return json_response


Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/environment_canada/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"domain": "environment_canada",
"name": "Environment Canada",
"documentation": "https://www.home-assistant.io/integrations/environment_canada",
"requirements": ["env_canada==0.5.27"],
"requirements": ["env_canada==0.5.28"],
"codeowners": ["@gwww", "@michaeldavie"],
"config_flow": true,
"iot_class": "cloud_polling",
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/inkbird/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
{ "local_name": "xBBQ*", "connectable": false },
{ "local_name": "tps", "connectable": false }
],
"requirements": ["inkbird-ble==0.5.5"],
"requirements": ["inkbird-ble==0.5.6"],
"dependencies": ["bluetooth_adapters"],
"codeowners": ["@bdraco"],
"iot_class": "local_push"
Expand Down
8 changes: 5 additions & 3 deletions homeassistant/components/isy994/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from aiohttp import CookieJar
import async_timeout
from pyisy import ISY, ISYConnectionError, ISYInvalidAuthError, ISYResponseParseError
from pyisy.constants import CONFIG_NETWORKING, CONFIG_PORTAL
import voluptuous as vol

from homeassistant import config_entries
Expand Down Expand Up @@ -43,7 +44,6 @@
ISY_CONF_FIRMWARE,
ISY_CONF_MODEL,
ISY_CONF_NAME,
ISY_CONF_NETWORKING,
MANUFACTURER,
PLATFORMS,
SCHEME_HTTP,
Expand Down Expand Up @@ -220,9 +220,11 @@ async def async_setup_entry(
numbers = isy_data.variables[Platform.NUMBER]
for vtype, _, vid in isy.variables.children:
numbers.append(isy.variables[vtype][vid])
if isy.conf[ISY_CONF_NETWORKING]:
if (
isy.conf[CONFIG_NETWORKING] or isy.conf[CONFIG_PORTAL]
) and isy.networking.nobjs:
isy_data.devices[CONF_NETWORK] = _create_service_device_info(
isy, name=ISY_CONF_NETWORKING, unique_id=CONF_NETWORK
isy, name=CONFIG_NETWORKING, unique_id=CONF_NETWORK
)
for resource in isy.networking.nobjs:
isy_data.net_resources.append(resource)
Expand Down
1 change: 0 additions & 1 deletion homeassistant/components/isy994/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@
# (they can turn off, and report their state)
ISY_GROUP_PLATFORM = Platform.SWITCH

ISY_CONF_NETWORKING = "Networking Module"
ISY_CONF_UUID = "uuid"
ISY_CONF_NAME = "name"
ISY_CONF_MODEL = "model"
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/isy994/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "Universal Devices ISY/IoX",
"integration_type": "hub",
"documentation": "https://www.home-assistant.io/integrations/isy994",
"requirements": ["pyisy==3.1.11"],
"requirements": ["pyisy==3.1.13"],
"codeowners": ["@bdraco", "@shbatm"],
"config_flow": true,
"ssdp": [
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/isy994/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.service import entity_service_call

from .const import _LOGGER, CONF_NETWORK, DOMAIN, ISY_CONF_NAME, ISY_CONF_NETWORKING
from .const import _LOGGER, CONF_NETWORK, DOMAIN, ISY_CONF_NAME
from .util import _async_cleanup_registry_entries

# Common Services for All Platforms:
Expand Down Expand Up @@ -233,7 +233,7 @@ async def async_run_network_resource_service_handler(service: ServiceCall) -> No
isy = isy_data.root
if isy_name and isy_name != isy.conf[ISY_CONF_NAME]:
continue
if isy.networking is None or not isy.conf[ISY_CONF_NETWORKING]:
if isy.networking is None:
continue
command = None
if address:
Expand Down
14 changes: 13 additions & 1 deletion homeassistant/components/ld2410_ble/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,19 @@
"requirements": ["bluetooth-data-tools==0.3.1", "ld2410-ble==0.1.1"],
"dependencies": ["bluetooth_adapters"],
"codeowners": ["@930913"],
"bluetooth": [{ "local_name": "HLK-LD2410B_*" }],
"bluetooth": [
{
"local_name": "HLK-LD2410B_*"
},
{
"local_name": "HLK-LD2410_*"
},
{
"manufacturer_id": 256,
"manufacturer_data_start": [7, 1],
"service_uuid": "0000af30-0000-1000-8000-00805f9b34fb"
}
],
"integration_type": "device",
"iot_class": "local_push"
}
2 changes: 1 addition & 1 deletion homeassistant/components/lupusec/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"domain": "lupusec",
"name": "Lupus Electronics LUPUSEC",
"documentation": "https://www.home-assistant.io/integrations/lupusec",
"requirements": ["lupupy==0.2.5"],
"requirements": ["lupupy==0.2.7"],
"codeowners": ["@majuss"],
"iot_class": "local_polling",
"loggers": ["lupupy"]
Expand Down
20 changes: 5 additions & 15 deletions homeassistant/components/matter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from .api import async_register_api
from .const import CONF_INTEGRATION_CREATED_ADDON, CONF_USE_ADDON, DOMAIN, LOGGER
from .device_platform import DEVICE_PLATFORM
from .helpers import MatterEntryData, get_matter
from .helpers import MatterEntryData, get_matter, get_node_from_device_entry

CONNECT_TIMEOUT = 10
LISTEN_READY_TIMEOUT = 30
Expand Down Expand Up @@ -192,23 +192,13 @@ async def async_remove_config_entry_device(
hass: HomeAssistant, config_entry: ConfigEntry, device_entry: dr.DeviceEntry
) -> bool:
"""Remove a config entry from a device."""
unique_id = None
node = await get_node_from_device_entry(hass, device_entry)

for ident in device_entry.identifiers:
if ident[0] == DOMAIN:
unique_id = ident[1]
break

if not unique_id:
if node is None:
return True

matter_entry_data: MatterEntryData = hass.data[DOMAIN][config_entry.entry_id]
matter_client = matter_entry_data.adapter.matter_client

for node in await matter_client.get_nodes():
if node.unique_id == unique_id:
await matter_client.remove_node(node.node_id)
break
matter = get_matter(hass)
await matter.matter_client.remove_node(node.node_id)

return True

Expand Down
Loading