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
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ jobs:
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

- name: Initialize CodeQL
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
uses: github/codeql-action/init@303c0aef88fc2fe5ff6d63d3b1596bfd83dfa1f9 # v3.30.4
with:
languages: python

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
uses: github/codeql-action/analyze@303c0aef88fc2fe5ff6d63d3b1596bfd83dfa1f9 # v3.30.4
with:
category: "/language:python"
10 changes: 5 additions & 5 deletions build.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
image: ghcr.io/home-assistant/{arch}-homeassistant
build_from:
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2025.09.1
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2025.09.1
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2025.09.1
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2025.09.1
i386: ghcr.io/home-assistant/i386-homeassistant-base:2025.09.1
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2025.09.2
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2025.09.2
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2025.09.2
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2025.09.2
i386: ghcr.io/home-assistant/i386-homeassistant-base:2025.09.2
codenotary:
signer: [email protected]
base_image: [email protected]
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/esphome/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"mqtt": ["esphome/discover/#"],
"quality_scale": "platinum",
"requirements": [
"aioesphomeapi==41.10.0",
"aioesphomeapi==41.11.0",
"esphome-dashboard-api==1.3.0",
"bleak-esphome==3.3.0"
],
Expand Down
18 changes: 12 additions & 6 deletions homeassistant/components/ezviz/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ def __init__(
super().__init__(coordinator)
self._serial = serial
self._camera_name = self.data["name"]

connections = set()
if mac_address := self.data["mac_address"]:
connections.add((CONNECTION_NETWORK_MAC, mac_address))

self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, serial)},
connections={
(CONNECTION_NETWORK_MAC, self.data["mac_address"]),
},
connections=connections,
manufacturer=MANUFACTURER,
model=self.data["device_sub_category"],
name=self.data["name"],
Expand Down Expand Up @@ -62,11 +65,14 @@ def __init__(
self._serial = serial
self.coordinator = coordinator
self._camera_name = self.data["name"]

connections = set()
if mac_address := self.data["mac_address"]:
connections.add((CONNECTION_NETWORK_MAC, mac_address))

self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, serial)},
connections={
(CONNECTION_NETWORK_MAC, self.data["mac_address"]),
},
connections=connections,
manufacturer=MANUFACTURER,
model=self.data["device_sub_category"],
name=self.data["name"],
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/geniushub/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ def extra_state_attributes(self) -> dict[str, Any]:

async def async_update(self) -> None:
"""Update an entity's state data."""
if "_state" in self._device.data: # only via v3 API
self._last_comms = dt_util.utc_from_timestamp(
self._device.data["_state"]["lastComms"]
)
if (state := self._device.data.get("_state")) and (
last_comms := state.get("lastComms")
) is not None: # only via v3 API
self._last_comms = dt_util.utc_from_timestamp(last_comms)


class GeniusZone(GeniusEntity):
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/lamarzocco/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@
"iot_class": "cloud_push",
"loggers": ["pylamarzocco"],
"quality_scale": "platinum",
"requirements": ["pylamarzocco==2.1.0"]
"requirements": ["pylamarzocco==2.1.1"]
}
2 changes: 2 additions & 0 deletions homeassistant/components/switchbot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
SupportedModels.RGBICWW_STRIP_LIGHT.value: [Platform.LIGHT, Platform.SENSOR],
SupportedModels.PLUG_MINI_EU.value: [Platform.SWITCH, Platform.SENSOR],
SupportedModels.RELAY_SWITCH_2PM.value: [Platform.SWITCH, Platform.SENSOR],
SupportedModels.GARAGE_DOOR_OPENER.value: [Platform.COVER, Platform.SENSOR],
}
CLASS_BY_DEVICE = {
SupportedModels.CEILING_LIGHT.value: switchbot.SwitchbotCeilingLight,
Expand Down Expand Up @@ -133,6 +134,7 @@
SupportedModels.RGBICWW_STRIP_LIGHT.value: switchbot.SwitchbotRgbicLight,
SupportedModels.PLUG_MINI_EU.value: switchbot.SwitchbotRelaySwitch,
SupportedModels.RELAY_SWITCH_2PM.value: switchbot.SwitchbotRelaySwitch2PM,
SupportedModels.GARAGE_DOOR_OPENER.value: switchbot.SwitchbotGarageDoorOpener,
}


Expand Down
4 changes: 4 additions & 0 deletions homeassistant/components/switchbot/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class SupportedModels(StrEnum):
PLUG_MINI_EU = "plug_mini_eu"
RELAY_SWITCH_2PM = "relay_switch_2pm"
K11_PLUS_VACUUM = "k11+_vacuum"
GARAGE_DOOR_OPENER = "garage_door_opener"


CONNECTABLE_SUPPORTED_MODEL_TYPES = {
Expand Down Expand Up @@ -91,6 +92,7 @@ class SupportedModels(StrEnum):
SwitchbotModel.PLUG_MINI_EU: SupportedModels.PLUG_MINI_EU,
SwitchbotModel.RELAY_SWITCH_2PM: SupportedModels.RELAY_SWITCH_2PM,
SwitchbotModel.K11_VACUUM: SupportedModels.K11_PLUS_VACUUM,
SwitchbotModel.GARAGE_DOOR_OPENER: SupportedModels.GARAGE_DOOR_OPENER,
}

NON_CONNECTABLE_SUPPORTED_MODEL_TYPES = {
Expand Down Expand Up @@ -126,6 +128,7 @@ class SupportedModels(StrEnum):
SwitchbotModel.RGBICWW_FLOOR_LAMP,
SwitchbotModel.PLUG_MINI_EU,
SwitchbotModel.RELAY_SWITCH_2PM,
SwitchbotModel.GARAGE_DOOR_OPENER,
}

ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS: dict[
Expand All @@ -146,6 +149,7 @@ class SupportedModels(StrEnum):
SwitchbotModel.RGBICWW_FLOOR_LAMP: switchbot.SwitchbotRgbicLight,
SwitchbotModel.PLUG_MINI_EU: switchbot.SwitchbotRelaySwitch,
SwitchbotModel.RELAY_SWITCH_2PM: switchbot.SwitchbotRelaySwitch2PM,
SwitchbotModel.GARAGE_DOOR_OPENER: switchbot.SwitchbotRelaySwitch,
}

HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL = {
Expand Down
31 changes: 30 additions & 1 deletion homeassistant/components/switchbot/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ async def async_setup_entry(
) -> None:
"""Set up Switchbot curtain based on a config entry."""
coordinator = entry.runtime_data
if isinstance(coordinator.device, switchbot.SwitchbotBlindTilt):
if isinstance(coordinator.device, switchbot.SwitchbotGarageDoorOpener):
async_add_entities([SwitchbotGarageDoorOpenerEntity(coordinator)])
elif isinstance(coordinator.device, switchbot.SwitchbotBlindTilt):
async_add_entities([SwitchBotBlindTiltEntity(coordinator)])
elif isinstance(coordinator.device, switchbot.SwitchbotRollerShade):
async_add_entities([SwitchBotRollerShadeEntity(coordinator)])
Expand Down Expand Up @@ -295,3 +297,30 @@ def _handle_coordinator_update(self) -> None:
self._attr_is_closed = self.parsed_data["position"] <= 20

self.async_write_ha_state()


class SwitchbotGarageDoorOpenerEntity(SwitchbotEntity, CoverEntity):
"""Representation of a Switchbot garage door."""

_device: switchbot.SwitchbotGarageDoorOpener
_attr_device_class = CoverDeviceClass.GARAGE
_attr_supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
_attr_translation_key = "garage_door"
_attr_name = None

@property
def is_closed(self) -> bool | None:
"""Return true if cover is closed, else False."""
return not self._device.door_open()

@exception_handler
async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the garage door."""
await self._device.open()
self.async_write_ha_state()

@exception_handler
async def async_close_cover(self, **kwargs: Any) -> None:
"""Close the garage door."""
await self._device.close()
self.async_write_ha_state()
15 changes: 8 additions & 7 deletions homeassistant/components/uptimerobot/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ async def async_setup_entry(
known_devices: set[int] = set()

def _check_device() -> None:
current_devices = {monitor.id for monitor in coordinator.data}
new_devices = current_devices - known_devices
if new_devices:
known_devices.update(new_devices)
async_add_entities(
entities: list[UptimeRobotSensor] = []
for monitor in coordinator.data:
if monitor.id in known_devices:
continue
known_devices.add(monitor.id)
entities.append(
UptimeRobotSensor(
coordinator,
SensorEntityDescription(
Expand All @@ -59,9 +60,9 @@ def _check_device() -> None:
),
monitor=monitor,
)
for monitor in coordinator.data
if monitor.id in new_devices
)
if entities:
async_add_entities(entities)

_check_device()
entry.async_on_unload(coordinator.async_add_listener(_check_device))
Expand Down
15 changes: 8 additions & 7 deletions homeassistant/components/uptimerobot/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ async def async_setup_entry(
known_devices: set[int] = set()

def _check_device() -> None:
current_devices = {monitor.id for monitor in coordinator.data}
new_devices = current_devices - known_devices
if new_devices:
known_devices.update(new_devices)
async_add_entities(
entities: list[UptimeRobotSwitch] = []
for monitor in coordinator.data:
if monitor.id in known_devices:
continue
known_devices.add(monitor.id)
entities.append(
UptimeRobotSwitch(
coordinator,
SwitchEntityDescription(
Expand All @@ -47,9 +48,9 @@ def _check_device() -> None:
),
monitor=monitor,
)
for monitor in coordinator.data
if monitor.id in new_devices
)
if entities:
async_add_entities(entities)

_check_device()
entry.async_on_unload(coordinator.async_add_listener(_check_device))
Expand Down
41 changes: 30 additions & 11 deletions homeassistant/components/zwave_js/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,10 +376,10 @@ async def _async_set_addon_config(self, config_updates: dict) -> None:

new_addon_config = addon_config | config_updates

if not new_addon_config[CONF_ADDON_DEVICE]:
new_addon_config.pop(CONF_ADDON_DEVICE)
if not new_addon_config[CONF_ADDON_SOCKET]:
new_addon_config.pop(CONF_ADDON_SOCKET)
if new_addon_config.get(CONF_ADDON_DEVICE) is None:
new_addon_config.pop(CONF_ADDON_DEVICE, None)
if new_addon_config.get(CONF_ADDON_SOCKET) is None:
new_addon_config.pop(CONF_ADDON_SOCKET, None)

if new_addon_config == addon_config:
return
Expand Down Expand Up @@ -1470,14 +1470,33 @@ async def async_step_esphome(
if not is_hassio(self.hass):
return self.async_abort(reason="not_hassio")

if discovery_info.zwave_home_id:
await self.async_set_unique_id(str(discovery_info.zwave_home_id))
self._abort_if_unique_id_configured(
{
CONF_USB_PATH: None,
CONF_SOCKET_PATH: discovery_info.socket_path,
}
if (
discovery_info.zwave_home_id
and (
current_config_entries := self._async_current_entries(
include_ignore=False
)
)
and (home_id := str(discovery_info.zwave_home_id))
and (
existing_entry := next(
(
entry
for entry in current_config_entries
if entry.unique_id == home_id
),
None,
)
)
# Only update existing entries that are configured via sockets
and existing_entry.data.get(CONF_SOCKET_PATH)
):
await self._async_set_addon_config(
{CONF_ADDON_SOCKET: discovery_info.socket_path}
)
# Reloading will sync add-on options to config entry data
self.hass.config_entries.async_schedule_reload(existing_entry.entry_id)
return self.async_abort(reason="already_configured")

self.socket_path = discovery_info.socket_path
self.context["title_placeholders"] = {
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/helpers/selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -1162,7 +1162,7 @@ class ObjectSelectorConfig(BaseSelectorConfig):
fields: dict[str, ObjectSelectorField]
multiple: bool
label_field: str
description_field: bool
description_field: str
translation_key: str


Expand Down
4 changes: 2 additions & 2 deletions requirements_all.txt

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

4 changes: 2 additions & 2 deletions requirements_test_all.txt

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

44 changes: 44 additions & 0 deletions tests/components/switchbot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1127,3 +1127,47 @@ def make_advertisement(
connectable=True,
tx_power=-127,
)


RELAY_SWITCH_1_SERVICE_INFO = BluetoothServiceInfoBleak(
name="Relay Switch 1",
manufacturer_data={2409: b"$X|\x0866G\x81\x00\x00\x001\x00\x00\x00\x00"},
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b";\x00\x00\x00"},
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
address="AA:BB:CC:DD:EE:FF",
rssi=-60,
source="local",
advertisement=generate_advertisement_data(
local_name="Relay Switch 1",
manufacturer_data={2409: b"$X|\x0866G\x81\x00\x00\x001\x00\x00\x00\x00"},
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"=\x00\x00\x00"},
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
),
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Relay Switch 1"),
time=0,
connectable=True,
tx_power=-127,
)


GARAGE_DOOR_OPENER_SERVICE_INFO = BluetoothServiceInfoBleak(
name="Garage Door Opener",
manufacturer_data={2409: b"$X|\x05BN\x0f\x00\x00\x03\x00\x00\x00\x00\x00\x00"},
service_data={
"0000fd3d-0000-1000-8000-00805f9b34fb": b">\x00\x00\x00",
},
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
address="AA:BB:CC:DD:EE:FF",
rssi=-60,
source="local",
advertisement=generate_advertisement_data(
local_name="Garage Door Opener",
manufacturer_data={2409: b"$X|\x05BN\x0f\x00\x00\x03\x00\x00\x00\x00\x00\x00"},
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b">\x00\x00\x00"},
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
),
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Garage Door Opener"),
time=0,
connectable=True,
tx_power=-127,
)
Loading
Loading