Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Matt Doran <mattdoran76@gmail.com>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
Co-authored-by: Makrit <sinticlee@gmail.com>
Co-authored-by: Claudio Ruggeri - CR-Tech <41435902+crug80@users.noreply.github.com>
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
Co-authored-by: Yuxin Wang <yuxinwang.dev@gmail.com>
Co-authored-by: Åke Strandberg <ake@strandberg.eu>
Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Klaas Schoute <klaas_schoute@hotmail.com>
Fix slave id equal to 0 (#136263)
This commit is contained in:
Franck Nijhof 2025-01-24 09:50:20 +01:00 committed by GitHub
commit 6145ea2323
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 114 additions and 45 deletions

View File

@ -44,7 +44,10 @@ class APCUPSdData(dict[str, str]):
@property
def serial_no(self) -> str | None:
"""Return the unique serial number of the UPS, if available."""
return self.get("SERIALNO")
sn = self.get("SERIALNO")
# We had user reports that some UPS models simply return "Blank" as serial number, in
# which case we fall back to `None` to indicate that it is actually not available.
return None if sn == "Blank" else sn
class APCUPSdCoordinator(DataUpdateCoordinator[APCUPSdData]):

View File

@ -21,5 +21,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20250109.0"]
"requirements": ["home-assistant-frontend==20250109.2"]
}

View File

@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/holiday",
"iot_class": "local_polling",
"requirements": ["holidays==0.64", "babel==2.15.0"]
"requirements": ["holidays==0.65", "babel==2.15.0"]
}

View File

@ -68,7 +68,7 @@ ZONE_BINARY_SENSORS: tuple[HydrawiseBinarySensorEntityDescription, ...] = (
)
SCHEMA_START_WATERING: VolDictType = {
vol.Optional("duration"): vol.All(vol.Coerce(int), vol.Range(min=0, max=90)),
vol.Optional("duration"): vol.All(vol.Coerce(int), vol.Range(min=0, max=1440)),
}
SCHEMA_SUSPEND: VolDictType = {
vol.Required("until"): cv.datetime,

View File

@ -10,7 +10,7 @@ start_watering:
selector:
number:
min: 0
max: 90
max: 1440
unit_of_measurement: min
mode: box
suspend:

View File

@ -44,9 +44,15 @@ class LinkPlayBaseEntity(Entity):
if model != MANUFACTURER_GENERIC:
model_id = bridge.device.properties["project"]
connections: set[tuple[str, str]] = set()
if "MAC" in bridge.device.properties:
connections.add(
(dr.CONNECTION_NETWORK_MAC, bridge.device.properties["MAC"])
)
self._attr_device_info = dr.DeviceInfo(
configuration_url=bridge.endpoint,
connections={(dr.CONNECTION_NETWORK_MAC, bridge.device.properties["MAC"])},
connections=connections,
hw_version=bridge.device.properties["hardware"],
identifiers={(DOMAIN, bridge.device.uuid)},
manufacturer=manufacturer,

View File

@ -79,7 +79,10 @@ class BasePlatform(Entity):
"""Initialize the Modbus binary sensor."""
self._hub = hub
self._slave = entry.get(CONF_SLAVE) or entry.get(CONF_DEVICE_ADDRESS, 0)
if (conf_slave := entry.get(CONF_SLAVE)) is not None:
self._slave = conf_slave
else:
self._slave = entry.get(CONF_DEVICE_ADDRESS, 1)
self._address = int(entry[CONF_ADDRESS])
self._input_type = entry[CONF_INPUT_TYPE]
self._value: str | None = None

View File

@ -368,7 +368,9 @@ class ModbusHub:
self, slave: int | None, address: int, value: int | list[int], use_call: str
) -> ModbusPDU | None:
"""Call sync. pymodbus."""
kwargs = {"slave": slave} if slave else {}
kwargs: dict[str, Any] = (
{ATTR_SLAVE: slave} if slave is not None else {ATTR_SLAVE: 1}
)
entry = self._pb_request[use_call]
try:
result: ModbusPDU = await entry.func(address, value, **kwargs)

View File

@ -325,10 +325,10 @@ class MyUplinkEnumSensor(MyUplinkDevicePointSensor):
}
@property
def native_value(self) -> str:
def native_value(self) -> str | None:
"""Sensor state value for enum sensor."""
device_point = self.coordinator.data.points[self.device_id][self.point_id]
return self.options_map[str(int(device_point.value))] # type: ignore[no-any-return]
return self.options_map.get(str(int(device_point.value)))
class MyUplinkEnumRawSensor(MyUplinkDevicePointSensor):

View File

@ -23,7 +23,7 @@ PEBLAR_CHARGE_LIMITER_TO_HOME_ASSISTANT = {
ChargeLimiter.INSTALLATION_LIMIT: "installation_limit",
ChargeLimiter.LOCAL_MODBUS_API: "local_modbus_api",
ChargeLimiter.LOCAL_REST_API: "local_rest_api",
ChargeLimiter.LOCAL_SCHEDULED: "local_scheduled",
ChargeLimiter.LOCAL_SCHEDULED_CHARGING: "local_scheduled_charging",
ChargeLimiter.OCPP_SMART_CHARGING: "ocpp_smart_charging",
ChargeLimiter.OVERCURRENT_PROTECTION: "overcurrent_protection",
ChargeLimiter.PHASE_IMBALANCE: "phase_imbalance",

View File

@ -7,6 +7,6 @@
"integration_type": "device",
"iot_class": "local_polling",
"quality_scale": "platinum",
"requirements": ["peblar==0.3.3"],
"requirements": ["peblar==0.4.0"],
"zeroconf": [{ "type": "_http._tcp.local.", "name": "pblr-*" }]
}

View File

@ -96,6 +96,7 @@
"installation_limit": "Installation limit",
"local_modbus_api": "Modbus API",
"local_rest_api": "REST API",
"local_scheduled_charging": "Scheduled charging",
"ocpp_smart_charging": "OCPP smart charging",
"overcurrent_protection": "Overcurrent protection",
"phase_imbalance": "Phase imbalance",

View File

@ -6,7 +6,7 @@
"documentation": "https://www.home-assistant.io/integrations/powerfox",
"iot_class": "cloud_polling",
"quality_scale": "silver",
"requirements": ["powerfox==1.2.0"],
"requirements": ["powerfox==1.2.1"],
"zeroconf": [
{
"type": "_http._tcp.local.",

View File

@ -122,7 +122,7 @@ class TwitchCoordinator(DataUpdateCoordinator[dict[str, TwitchUpdate]]):
stream.game_name if stream else None,
stream.title if stream else None,
stream.started_at if stream else None,
stream.thumbnail_url if stream else None,
stream.thumbnail_url.format(width="", height="") if stream else None,
channel.profile_image_url,
bool(sub),
sub.is_gift if sub else None,

View File

@ -13,5 +13,5 @@
"documentation": "https://www.home-assistant.io/integrations/withings",
"iot_class": "cloud_push",
"loggers": ["aiowithings"],
"requirements": ["aiowithings==3.1.4"]
"requirements": ["aiowithings==3.1.5"]
}

View File

@ -7,5 +7,5 @@
"iot_class": "local_polling",
"loggers": ["holidays"],
"quality_scale": "internal",
"requirements": ["holidays==0.64"]
"requirements": ["holidays==0.65"]
}

View File

@ -25,7 +25,7 @@ if TYPE_CHECKING:
APPLICATION_NAME: Final = "HomeAssistant"
MAJOR_VERSION: Final = 2025
MINOR_VERSION: Final = 1
PATCH_VERSION: Final = "3"
PATCH_VERSION: Final = "4"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 12, 0)

View File

@ -35,7 +35,7 @@ habluetooth==3.7.0
hass-nabucasa==0.87.0
hassil==2.1.0
home-assistant-bluetooth==1.13.0
home-assistant-frontend==20250109.0
home-assistant-frontend==20250109.2
home-assistant-intents==2025.1.1
httpx==0.27.2
ifaddr==0.2.0

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "homeassistant"
version = "2025.1.3"
version = "2025.1.4"
license = {text = "Apache-2.0"}
description = "Open-source home automation platform running on Python 3."
readme = "README.rst"

View File

@ -416,7 +416,7 @@ aiowatttime==0.1.1
aiowebostv==0.4.2
# homeassistant.components.withings
aiowithings==3.1.4
aiowithings==3.1.5
# homeassistant.components.yandex_transport
aioymaps==1.2.5
@ -1131,10 +1131,10 @@ hole==0.8.0
# homeassistant.components.holiday
# homeassistant.components.workday
holidays==0.64
holidays==0.65
# homeassistant.components.frontend
home-assistant-frontend==20250109.0
home-assistant-frontend==20250109.2
# homeassistant.components.conversation
home-assistant-intents==2025.1.1
@ -1603,7 +1603,7 @@ panasonic-viera==0.4.2
pdunehd==1.3.2
# homeassistant.components.peblar
peblar==0.3.3
peblar==0.4.0
# homeassistant.components.peco
peco==0.0.30
@ -1650,7 +1650,7 @@ pmsensor==0.4
poolsense==0.0.8
# homeassistant.components.powerfox
powerfox==1.2.0
powerfox==1.2.1
# homeassistant.components.reddit
praw==7.5.0

View File

@ -398,7 +398,7 @@ aiowatttime==0.1.1
aiowebostv==0.4.2
# homeassistant.components.withings
aiowithings==3.1.4
aiowithings==3.1.5
# homeassistant.components.yandex_transport
aioymaps==1.2.5
@ -960,10 +960,10 @@ hole==0.8.0
# homeassistant.components.holiday
# homeassistant.components.workday
holidays==0.64
holidays==0.65
# homeassistant.components.frontend
home-assistant-frontend==20250109.0
home-assistant-frontend==20250109.2
# homeassistant.components.conversation
home-assistant-intents==2025.1.1
@ -1330,7 +1330,7 @@ panasonic-viera==0.4.2
pdunehd==1.3.2
# homeassistant.components.peblar
peblar==0.3.3
peblar==0.4.0
# homeassistant.components.peco
peco==0.0.30
@ -1360,7 +1360,7 @@ plumlightpad==0.0.11
poolsense==0.0.8
# homeassistant.components.powerfox
powerfox==1.2.0
powerfox==1.2.1
# homeassistant.components.reddit
praw==7.5.0

View File

@ -125,6 +125,8 @@ async def test_flow_works(hass: HomeAssistant) -> None:
({"UPSNAME": "Friendly Name"}, "Friendly Name"),
({"MODEL": "MODEL X"}, "MODEL X"),
({"SERIALNO": "ZZZZ"}, "ZZZZ"),
# Some models report "Blank" as serial number, which we should treat it as not reported.
({"SERIALNO": "Blank"}, "APC UPS"),
({}, "APC UPS"),
],
)

View File

@ -31,6 +31,8 @@ from tests.common import MockConfigEntry, async_fire_time_changed
# Does not contain either "SERIALNO" field.
# We should _not_ create devices for the entities and their IDs will not have prefixes.
MOCK_MINIMAL_STATUS,
# Some models report "Blank" as SERIALNO, but we should treat it as not reported.
MOCK_MINIMAL_STATUS | {"SERIALNO": "Blank"},
],
)
async def test_async_setup_entry(hass: HomeAssistant, status: OrderedDict) -> None:
@ -41,7 +43,7 @@ async def test_async_setup_entry(hass: HomeAssistant, status: OrderedDict) -> No
await async_init_integration(hass, status=status)
prefix = ""
if "SERIALNO" in status:
if "SERIALNO" in status and status["SERIALNO"] != "Blank":
prefix = slugify(status.get("UPSNAME", "APC UPS")) + "_"
# Verify successful setup by querying the status sensor.
@ -56,6 +58,8 @@ async def test_async_setup_entry(hass: HomeAssistant, status: OrderedDict) -> No
[
# We should not create device entries if SERIALNO is not reported.
MOCK_MINIMAL_STATUS,
# Some models report "Blank" as SERIALNO, but we should treat it as not reported.
MOCK_MINIMAL_STATUS | {"SERIALNO": "Blank"},
# We should set the device name to be the friendly UPSNAME field if available.
MOCK_MINIMAL_STATUS | {"SERIALNO": "XXXX", "UPSNAME": "MyUPS"},
# Otherwise, we should fall back to default device name --- "APC UPS".
@ -71,7 +75,7 @@ async def test_device_entry(
await async_init_integration(hass, status=status)
# Verify device info is properly set up.
if "SERIALNO" not in status:
if "SERIALNO" not in status or status["SERIALNO"] == "Blank":
assert len(device_registry.devices) == 0
return

View File

@ -1265,3 +1265,56 @@ async def test_no_entities(hass: HomeAssistant) -> None:
]
}
assert await async_setup_component(hass, DOMAIN, config) is False
@pytest.mark.parametrize(
("do_config", "expected_slave_value"),
[
(
{
CONF_SENSORS: [
{
CONF_NAME: "dummy",
CONF_ADDRESS: 1234,
},
],
},
1,
),
(
{
CONF_SENSORS: [
{
CONF_NAME: "dummy",
CONF_ADDRESS: 1234,
CONF_SLAVE: 0,
},
],
},
0,
),
(
{
CONF_SENSORS: [
{
CONF_NAME: "dummy",
CONF_ADDRESS: 1234,
CONF_DEVICE_ADDRESS: 6,
},
],
},
6,
),
],
)
async def test_check_default_slave(
hass: HomeAssistant,
mock_modbus,
do_config,
mock_do_cycle,
expected_slave_value: int,
) -> None:
"""Test default slave."""
assert mock_modbus.read_holding_registers.mock_calls
first_call = mock_modbus.read_holding_registers.mock_calls[0]
assert first_call.kwargs["slave"] == expected_slave_value

View File

@ -822,7 +822,7 @@
"parameterUnit": "",
"writable": false,
"timestamp": "2024-02-08T19:13:05+00:00",
"value": 30,
"value": 31,
"strVal": "Heating",
"smartHomeCategories": [],
"minValue": null,

View File

@ -883,7 +883,7 @@
"parameterUnit": "",
"writable": false,
"timestamp": "2024-02-08T19:13:05+00:00",
"value": 30,
"value": 31,
"strVal": "Heating",
"smartHomeCategories": [],
"minValue": null,
@ -2045,7 +2045,7 @@
"parameterUnit": "",
"writable": false,
"timestamp": "2024-02-08T19:13:05+00:00",
"value": 30,
"value": 31,
"strVal": "Heating",
"smartHomeCategories": [],
"minValue": null,

View File

@ -3396,7 +3396,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'Heating',
'state': 'unknown',
})
# ---
# name: test_sensor_states[sensor.gotham_city_priority_2-entry]
@ -3462,7 +3462,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'Heating',
'state': 'unknown',
})
# ---
# name: test_sensor_states[sensor.gotham_city_priority_raw-entry]
@ -3508,7 +3508,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '30',
'state': '31',
})
# ---
# name: test_sensor_states[sensor.gotham_city_priority_raw_2-entry]
@ -3554,7 +3554,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '30',
'state': '31',
})
# ---
# name: test_sensor_states[sensor.gotham_city_r_start_diff_additional_heat-entry]

View File

@ -51,10 +51,8 @@
'Hostname': 'PBLR-0000645',
'HwFixedCableRating': 20,
'HwFwCompat': 'wlac-2',
'HwHas4pRelay': False,
'HwHasBop': True,
'HwHasBuzzer': True,
'HwHasDualSocket': False,
'HwHasEichrechtLaserMarking': False,
'HwHasEthernet': True,
'HwHasLed': True,
@ -64,13 +62,11 @@
'HwHasPlc': False,
'HwHasRfid': True,
'HwHasRs485': True,
'HwHasShutter': False,
'HwHasSocket': False,
'HwHasTpm': False,
'HwHasWlan': True,
'HwMaxCurrent': 16,
'HwOneOrThreePhase': 3,
'HwUKCompliant': False,
'MainboardPn': '6004-2300-7600',
'MainboardSn': '23-38-A4E-2MC',
'MeterCalIGainA': 267369,
@ -86,7 +82,6 @@
'MeterCalVGainB': 246074,
'MeterCalVGainC': 230191,
'MeterFwIdent': 'b9cbcd',
'NorFlash': 'True',
'ProductModelName': 'WLAC1-H11R0WE0ICR00',
'ProductPn': '6004-2300-8002',
'ProductSn': '23-45-A4O-MOF',

View File

@ -302,7 +302,7 @@
'installation_limit',
'local_modbus_api',
'local_rest_api',
'local_scheduled',
'local_scheduled_charging',
'ocpp_smart_charging',
'overcurrent_protection',
'phase_imbalance',
@ -354,7 +354,7 @@
'installation_limit',
'local_modbus_api',
'local_rest_api',
'local_scheduled',
'local_scheduled_charging',
'ocpp_smart_charging',
'overcurrent_protection',
'phase_imbalance',