Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UniFi WLAN Password sensor #114419

Merged
merged 13 commits into from
Mar 30, 2024
Merged
19 changes: 19 additions & 0 deletions homeassistant/components/unifi/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ def async_device_outlet_supported_fn(hub: UnifiHub, obj_id: str) -> bool:
return hub.api.devices[obj_id].outlet_ac_power_budget is not None


@callback
def async_get_password_supported_fn(hub: UnifiHub, obj_id: str) -> bool:
"""Determine if a WLAN supports reading the password."""
return hasattr(hub.api.wlans[obj_id], "x_passphrase")
brunohenriquy marked this conversation as resolved.
Show resolved Hide resolved


@callback
def async_client_is_connected_fn(hub: UnifiHub, obj_id: str) -> bool:
"""Check if client was last seen recently."""
Expand Down Expand Up @@ -339,6 +345,19 @@ class UnifiSensorEntityDescription(
value_fn=async_device_state_value_fn,
options=list(DEVICE_STATES.values()),
),
UnifiSensorEntityDescription[Wlans, Wlan](
brunohenriquy marked this conversation as resolved.
Show resolved Hide resolved
key="WLAN password",
name_fn=lambda wlan: "Password",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
api_handler_fn=lambda api: api.wlans,
available_fn=async_wlan_available_fn,
device_info_fn=async_wlan_device_info_fn,
object_fn=lambda api, obj_id: api.wlans[obj_id],
supported_fn=async_get_password_supported_fn,
unique_id_fn=lambda hub, obj_id: f"password-{obj_id}",
value_fn=lambda hub, obj: obj.x_passphrase,
),
)


Expand Down
71 changes: 71 additions & 0 deletions tests/components/unifi/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -998,3 +998,74 @@ async def test_device_state(
device["state"] = i
mock_unifi_websocket(message=MessageKey.DEVICE, data=device)
assert hass.states.get("sensor.device_state").state == DEVICE_STATES[i]


async def test_wlan_password(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
aioclient_mock: AiohttpClientMocker,
mock_unifi_websocket,
websocket_mock,
) -> None:
"""Test the WLAN password sensor behavior."""
await setup_unifi_integration(hass, aioclient_mock, wlans_response=[WLAN])

sensor_password = "sensor.ssid_1_password"
password = "password"
new_password = "new_password"

ent_reg_entry = entity_registry.async_get(sensor_password)
assert ent_reg_entry.unique_id == "password-012345678910111213141516"
assert ent_reg_entry.disabled_by == RegistryEntryDisabler.INTEGRATION
assert ent_reg_entry.entity_category is EntityCategory.DIAGNOSTIC

# Enable entity
entity_registry.async_update_entity(entity_id=sensor_password, disabled_by=None)
await hass.async_block_till_done()

async_fire_time_changed(
hass,
dt_util.utcnow() + timedelta(seconds=RELOAD_AFTER_UPDATE_DELAY + 1),
)
await hass.async_block_till_done()

# Validate state object
wlan_password_sensor_1 = hass.states.get(sensor_password)
assert wlan_password_sensor_1.state == password

# Update state object - same password - no change to state
mock_unifi_websocket(message=MessageKey.WLAN_CONF_UPDATED, data=WLAN)
await hass.async_block_till_done()
wlan_password_sensor_2 = hass.states.get(sensor_password)
assert wlan_password_sensor_1.state == wlan_password_sensor_2.state

# Update state object - changed password - new state
data = deepcopy(WLAN)
data["x_passphrase"] = new_password
mock_unifi_websocket(message=MessageKey.WLAN_CONF_UPDATED, data=data)
await hass.async_block_till_done()
wlan_password_sensor_3 = hass.states.get(sensor_password)
assert wlan_password_sensor_1.state != wlan_password_sensor_3.state

# Availability signaling

# Controller disconnects
await websocket_mock.disconnect()
assert hass.states.get(sensor_password).state == STATE_UNAVAILABLE

# Controller reconnects
await websocket_mock.reconnect()
assert hass.states.get(sensor_password).state == new_password

# WLAN gets disabled
wlan_1 = deepcopy(WLAN)
wlan_1["enabled"] = False
mock_unifi_websocket(message=MessageKey.WLAN_CONF_UPDATED, data=wlan_1)
await hass.async_block_till_done()
assert hass.states.get(sensor_password).state == STATE_UNAVAILABLE

# WLAN gets re-enabled
wlan_1["enabled"] = True
mock_unifi_websocket(message=MessageKey.WLAN_CONF_UPDATED, data=wlan_1)
await hass.async_block_till_done()
assert hass.states.get(sensor_password).state == password