Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
48671e1
Bump modbus-connection to 4.12.0 (#182149)
balloob Sep 14, 2026
6933d28
Bump midea-local to 12.0.0 (#182144)
rokam Sep 14, 2026
2e45fe3
Bump total_connect_cline to 2026.9 (#182142)
austinmroczek Sep 14, 2026
3873569
Midea: Add AC water pump running binary sensor (#182064)
peterdey Sep 14, 2026
60aad92
Add Tuya ksdjml video peephole camera category (#182145)
srescio Sep 14, 2026
c96751f
Bump Besen to 0.4.2 to support additional BS20 hardware variants (#18…
moryoav Sep 14, 2026
720ead6
Suggest close matches for unknown YAML configuration keys (#182124)
frenck Sep 14, 2026
0e5d6b5
Abort Teslemetry Bluetooth vehicle setup when no Bluetooth is availab…
Bre77 Sep 14, 2026
7ee22de
Add switch support for Tuya WG2 alarm panel (Duosmart C30) (#165835)
ChayoteJarocho Sep 14, 2026
ce436dd
Require admin for the mqtt.publish and mqtt.dump actions (#182045)
frenck Sep 14, 2026
a3c9107
Simplify generation of Tuya electricity sensor descriptions (#181919)
epenet Sep 14, 2026
ef3acfd
Match the known address first in Yale Access Bluetooth discovery (#18…
frenck Sep 14, 2026
09986be
Bump pnpm/action-setup from 6.0.10 to 6.1.0 (#182155)
dependabot[bot] Sep 14, 2026
3db8e88
Add additional Tuya phase power sensors (#181549)
huangrenwei79 Sep 14, 2026
b9b2c9a
Remove myself as wake_on_lan codeowner (#182153)
ntilley905 Sep 14, 2026
07a04e7
Fix K‑series that need encrypted pairing in SamsungTV (#180619)
chemelli74 Sep 14, 2026
831013c
Fix blocking client creation in twilio integration (#182157)
rasadregmi Sep 14, 2026
0f3d3e5
Handle ConfigEntry errors in integration async_migrate_entry (#175349)
gjohansson-ST Sep 14, 2026
f864cf2
Revert "Add Tuya ksdjml video peephole camera category" (#182163)
epenet Sep 14, 2026
9997d83
Add comment in core_files for telegram integration (#182159)
epenet Sep 14, 2026
691257f
Type the twilio integration with a HassKey (#182158)
epenet Sep 14, 2026
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
3 changes: 3 additions & 0 deletions .core_files.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,13 @@ components: &components
- homeassistant/components/sun/**
- homeassistant/components/system_health/**
- homeassistant/components/tag/**
# telegram_bot is used by telegram
- homeassistant/components/telegram_bot/**
- homeassistant/components/template/**
- homeassistant/components/timer/**
- homeassistant/components/trace/**
# twilio is used by twilio_call and twilio_sms
- homeassistant/components/twilio/**
- homeassistant/components/usb/**
- homeassistant/components/webhook/**
- homeassistant/components/websocket_api/**
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
persist-credentials: false

- name: Set up pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
package_json_file: tests/e2e/package.json

Expand Down
2 changes: 0 additions & 2 deletions CODEOWNERS

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

2 changes: 1 addition & 1 deletion homeassistant/components/besen/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
"integration_type": "device",
"iot_class": "local_push",
"quality_scale": "bronze",
"requirements": ["besen==0.4.0"]
"requirements": ["besen==0.4.2"]
}
3 changes: 0 additions & 3 deletions homeassistant/components/izone/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,6 @@ def _async_on_address_changed(endpoint: pizone.ControllerEndpoint) -> None:
async def async_migrate_entry(hass: HomeAssistant, entry: IZoneConfigEntry) -> bool:
"""Migrate old config entry schema to the current version."""
if entry.version == 1:
# Clear legacy data only.
# Raising ConfigEntryNotReady from async_migrate_entry would permanently land
# the entry in MIGRATION_ERROR with no retry path.
hass.config_entries.async_update_entry(entry, version=2, data={})
return True
return False
Expand Down
6 changes: 6 additions & 0 deletions homeassistant/components/midea/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@
device_class=BinarySensorDeviceClass.PROBLEM,
entity_category=EntityCategory.DIAGNOSTIC,
),
BinarySensorEntityDescription(
key="water_pump_running",
translation_key="water_pump_running",
device_class=BinarySensorDeviceClass.RUNNING,
entity_category=EntityCategory.DIAGNOSTIC,
),
BinarySensorEntityDescription(
key="filter_cleaning_reminder",
translation_key="filter_cleaning_reminder",
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/midea/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ def __init__(
) -> None:
"""Midea FB Climate entity init."""
super().__init__(device, description)
self._attr_preset_modes: list[str] = self._device.modes
self._attr_preset_modes: list[str] = cast(list[str], self._device.modes)

@property
@override
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/midea/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"binary_sensor": {
"header_exist": {
"default": "mdi:air-filter"
},
"water_pump_running": {
"default": "mdi:water-pump"
}
},
"climate": {
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/midea/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
"iot_class": "local_polling",
"loggers": ["midealocal"],
"quality_scale": "bronze",
"requirements": ["midea-local==11.0.1"]
"requirements": ["midea-local==12.0.0"]
}
3 changes: 3 additions & 0 deletions homeassistant/components/midea/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@
},
"tank_full": {
"name": "Tank full"
},
"water_pump_running": {
"name": "Water pump running"
}
},
"button": {
Expand Down
22 changes: 22 additions & 0 deletions homeassistant/components/modbus/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,27 @@
)


def _canonical(params: ModbusParams) -> ModbusParams:
"""Return the params in the form the link is actually built from.

RTU and ASCII frame a serial line, so `ModbusTcpParams` carrying one builds
the serial link over a `socket://` device that `ModbusSerialParams` names
directly. Both spellings of that link have to reach the same connection.
"""
if not isinstance(params, ModbusTcpParams) or params.framer not in ("rtu", "ascii"):
return params
# An IPv6 literal is bracketed, or its own colons read as the port separator.
host = f"[{params.host}]" if ":" in params.host else params.host
return ModbusSerialParams(
device=f"socket://{host}:{params.port}",
framer=params.framer,
# The line speed modbus-connection gives a socket-carried serial framing.
# It is fast enough to land on tmodbus's 1.75 ms inter-frame gap floor,
# which any value at or above 19200 baud also gets.
baudrate=115200,
)


@dataclass
class _SharedConnection:
"""A connection and the units held on it."""
Expand Down Expand Up @@ -77,6 +98,7 @@ def _async_acquire(
Raises `HomeAssistantError` if the device is already in use over different
link settings, which cannot both be honoured on one connection.
"""
params = _canonical(params)
endpoint = params.endpoint
connections = hass.data.setdefault(DATA_MODBUS_CONNECTIONS, {})
if (shared := connections.get(endpoint)) is None:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/modbus/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"loggers": ["pymodbus"],
"requirements": [
"pymodbus==3.13.1",
"modbus-connection[tmodbus]==4.11.1",
"modbus-connection[tmodbus]==4.12.0",
"tmodbus==0.6.2"
]
}
7 changes: 4 additions & 3 deletions homeassistant/components/mqtt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,8 @@ async def async_publish_service(call: ServiceCall) -> None:
message_expiry_interval=message_expiry_interval,
)

hass.services.async_register(
DOMAIN, SERVICE_PUBLISH, async_publish_service, schema=MQTT_PUBLISH_SCHEMA
async_register_admin_service(
hass, DOMAIN, SERVICE_PUBLISH, async_publish_service, MQTT_PUBLISH_SCHEMA
)

async def async_dump_service(call: ServiceCall) -> None:
Expand All @@ -395,7 +395,8 @@ async def finish_dump(_: datetime) -> None:

ev.async_call_later(hass, call.data["duration"], finish_dump)

hass.services.async_register(
async_register_admin_service(
hass,
DOMAIN,
SERVICE_DUMP,
async_dump_service,
Expand Down
12 changes: 12 additions & 0 deletions homeassistant/components/samsungtv/bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@ def model_requires_encryption(model: str | None) -> bool:
return model is not None and len(model) > 4 and model[4] in ("H", "J")


def model_may_require_encryption(model: str | None) -> bool:
"""Return True for models that might need encrypted pairing.

Some 2016 K-series models advertise the plain websocket method in their REST
device info but only pair via the encrypted PIN flow. Unlike H/J models (see
model_requires_encryption), other sets in the same series work with the
websocket method, so the encrypted method is only tried as a fallback once
websocket pairing has failed to connect.
"""
return model is not None and len(model) > 4 and model[4] == "K"


async def async_get_device_info(
hass: HomeAssistant,
host: str,
Expand Down
28 changes: 27 additions & 1 deletion homeassistant/components/samsungtv/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,20 @@
)
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo

from .bridge import SamsungTVBridge, async_get_device_info, mac_from_device_info
from .bridge import (
SamsungTVBridge,
async_get_device_info,
mac_from_device_info,
model_may_require_encryption,
)
from .const import (
CONF_MANUFACTURER,
CONF_SESSION_ID,
CONF_SSDP_MAIN_TV_AGENT_LOCATION,
CONF_SSDP_RENDERING_CONTROL_LOCATION,
DEFAULT_MANUFACTURER,
DOMAIN,
ENCRYPTED_WEBSOCKET_PORT,
LOGGER,
METHOD_ENCRYPTED_WEBSOCKET,
METHOD_LEGACY,
Expand Down Expand Up @@ -324,6 +330,26 @@ async def async_step_pairing(
result = await self._bridge.async_try_connect()
if result == RESULT_SUCCESS:
return self._get_entry_from_bridge()
if result == RESULT_CANNOT_CONNECT and model_may_require_encryption(
self._model
):
# Some 2016 K-series sets advertise the websocket method but only
# pair via the encrypted PIN flow; try it before giving up
LOGGER.debug(
"Websocket pairing failed for %s (%s), falling back to encrypted",
self._host,
self._model,
)
encrypted_bridge = SamsungTVBridge.get_bridge(
self.hass,
METHOD_ENCRYPTED_WEBSOCKET,
self._host,
ENCRYPTED_WEBSOCKET_PORT,
)
if await encrypted_bridge.async_try_connect() == RESULT_CANNOT_CONNECT:
raise AbortFlow(RESULT_CANNOT_CONNECT)
self._bridge = encrypted_bridge
return await self.async_step_encrypted_pairing()
if result != RESULT_AUTH_MISSING:
raise AbortFlow(result)
errors = {"base": RESULT_AUTH_MISSING}
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/teslemetry/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from homeassistant.components.bluetooth import (
async_discovered_service_info,
async_request_active_scan,
async_scanner_count,
)
from homeassistant.config_entries import (
SOURCE_REAUTH,
Expand Down Expand Up @@ -227,6 +228,8 @@ async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> SubentryFlowResult:
"""Select an account vehicle to add over Bluetooth, then pair it."""
if not async_scanner_count(self.hass, connectable=True):
return self.async_abort(reason="bluetooth_not_available")
entry = self._get_entry()
if entry.state is not ConfigEntryState.LOADED:
return self.async_abort(reason="entry_not_loaded")
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/teslemetry/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"vehicle": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"bluetooth_not_available": "At least one Bluetooth adapter or remote Bluetooth proxy must be configured to add a vehicle over Bluetooth.",
"entry_not_loaded": "The Teslemetry configuration entry is not loaded. Please ensure it is set up correctly before adding a vehicle.",
"no_vehicles": "Every eligible vehicle in your Teslemetry account has already been added over Bluetooth."
},
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/totalconnect/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["total_connect_client"],
"requirements": ["total-connect-client==2026.7"]
"requirements": ["total-connect-client==2026.9"]
}
2 changes: 2 additions & 0 deletions homeassistant/components/tuya/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -888,13 +888,15 @@ class DPCode(StrEnum):
SWITCH_7 = "switch_7" # Switch 7
SWITCH_8 = "switch_8" # Switch 8
SWITCH_ALARM_LIGHT = "switch_alarm_light"
SWITCH_ALARM_PROPEL = "switch_alarm_propel"
SWITCH_ALARM_SOUND = "switch_alarm_sound"
SWITCH_BACKLIGHT = "switch_backlight" # Backlight switch
SWITCH_CHARGE = "switch_charge"
SWITCH_CONTROLLER = "switch_controller"
SWITCH_DISTURB = "switch_disturb"
SWITCH_FAN = "switch_fan"
SWITCH_HORIZONTAL = "switch_horizontal" # Horizontal swing flap switch
SWITCH_KB_SOUND = "switch_kb_sound"
SWITCH_LED = "switch_led" # Switch
SWITCH_LED_1 = "switch_led_1"
SWITCH_LED_2 = "switch_led_2"
Expand Down
Loading
Loading