mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Prompt for pin only on open / unlock (#23751)
* Prompt for pin only on open / unlock * Fixed test cases
This commit is contained in:
parent
ce1974b014
commit
7f0953766b
@ -742,11 +742,10 @@ class LockUnlockTrait(_Trait):
|
|||||||
|
|
||||||
async def execute(self, command, data, params, challenge):
|
async def execute(self, command, data, params, challenge):
|
||||||
"""Execute an LockUnlock command."""
|
"""Execute an LockUnlock command."""
|
||||||
_verify_pin_challenge(data, challenge)
|
|
||||||
|
|
||||||
if params['lock']:
|
if params['lock']:
|
||||||
service = lock.SERVICE_LOCK
|
service = lock.SERVICE_LOCK
|
||||||
else:
|
else:
|
||||||
|
_verify_pin_challenge(data, challenge)
|
||||||
service = lock.SERVICE_UNLOCK
|
service = lock.SERVICE_UNLOCK
|
||||||
|
|
||||||
await self.hass.services.async_call(lock.DOMAIN, service, {
|
await self.hass.services.async_call(lock.DOMAIN, service, {
|
||||||
@ -1095,36 +1094,36 @@ class OpenCloseTrait(_Trait):
|
|||||||
domain = self.state.domain
|
domain = self.state.domain
|
||||||
|
|
||||||
if domain == cover.DOMAIN:
|
if domain == cover.DOMAIN:
|
||||||
if self.state.attributes.get(ATTR_DEVICE_CLASS) in (
|
svc_params = {ATTR_ENTITY_ID: self.state.entity_id}
|
||||||
cover.DEVICE_CLASS_DOOR, cover.DEVICE_CLASS_GARAGE
|
|
||||||
):
|
|
||||||
_verify_pin_challenge(data, challenge)
|
|
||||||
|
|
||||||
if params['openPercent'] == 0:
|
if params['openPercent'] == 0:
|
||||||
await self.hass.services.async_call(
|
service = cover.SERVICE_CLOSE_COVER
|
||||||
cover.DOMAIN, cover.SERVICE_CLOSE_COVER, {
|
should_verify = False
|
||||||
ATTR_ENTITY_ID: self.state.entity_id
|
|
||||||
}, blocking=True, context=data.context)
|
|
||||||
elif params['openPercent'] == 100:
|
elif params['openPercent'] == 100:
|
||||||
await self.hass.services.async_call(
|
service = cover.SERVICE_OPEN_COVER
|
||||||
cover.DOMAIN, cover.SERVICE_OPEN_COVER, {
|
should_verify = True
|
||||||
ATTR_ENTITY_ID: self.state.entity_id
|
|
||||||
}, blocking=True, context=data.context)
|
|
||||||
elif (self.state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) &
|
elif (self.state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) &
|
||||||
cover.SUPPORT_SET_POSITION):
|
cover.SUPPORT_SET_POSITION):
|
||||||
await self.hass.services.async_call(
|
service = cover.SERVICE_SET_COVER_POSITION
|
||||||
cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION, {
|
should_verify = True
|
||||||
ATTR_ENTITY_ID: self.state.entity_id,
|
svc_params[cover.ATTR_POSITION] = params['openPercent']
|
||||||
cover.ATTR_POSITION: params['openPercent']
|
|
||||||
}, blocking=True, context=data.context)
|
|
||||||
else:
|
else:
|
||||||
raise SmartHomeError(
|
raise SmartHomeError(
|
||||||
ERR_FUNCTION_NOT_SUPPORTED,
|
ERR_FUNCTION_NOT_SUPPORTED,
|
||||||
'Setting a position is not supported')
|
'Setting a position is not supported')
|
||||||
|
|
||||||
|
if (should_verify and
|
||||||
|
self.state.attributes.get(ATTR_DEVICE_CLASS)
|
||||||
|
in (cover.DEVICE_CLASS_DOOR,
|
||||||
|
cover.DEVICE_CLASS_GARAGE)):
|
||||||
|
_verify_pin_challenge(data, challenge)
|
||||||
|
|
||||||
|
await self.hass.services.async_call(
|
||||||
|
cover.DOMAIN, service, svc_params,
|
||||||
|
blocking=True, context=data.context)
|
||||||
|
|
||||||
if (self.state.attributes.get(ATTR_ASSUMED_STATE) or
|
if (self.state.attributes.get(ATTR_ASSUMED_STATE) or
|
||||||
self.state.state == STATE_UNKNOWN):
|
self.state.state == STATE_UNKNOWN):
|
||||||
print("YOO")
|
|
||||||
self.override_position = params['openPercent']
|
self.override_position = params['openPercent']
|
||||||
|
|
||||||
|
|
||||||
|
@ -845,38 +845,20 @@ async def test_lock_unlock_lock(hass):
|
|||||||
None)
|
None)
|
||||||
|
|
||||||
trt = trait.LockUnlockTrait(hass,
|
trt = trait.LockUnlockTrait(hass,
|
||||||
State('lock.front_door', lock.STATE_UNLOCKED),
|
State('lock.front_door', lock.STATE_LOCKED),
|
||||||
PIN_CONFIG)
|
PIN_CONFIG)
|
||||||
|
|
||||||
assert trt.sync_attributes() == {}
|
assert trt.sync_attributes() == {}
|
||||||
|
|
||||||
assert trt.query_attributes() == {
|
assert trt.query_attributes() == {
|
||||||
'isLocked': False
|
'isLocked': True
|
||||||
}
|
}
|
||||||
|
|
||||||
assert trt.can_execute(trait.COMMAND_LOCKUNLOCK, {'lock': True})
|
assert trt.can_execute(trait.COMMAND_LOCKUNLOCK, {'lock': True})
|
||||||
|
|
||||||
calls = async_mock_service(hass, lock.DOMAIN, lock.SERVICE_LOCK)
|
calls = async_mock_service(hass, lock.DOMAIN, lock.SERVICE_LOCK)
|
||||||
|
|
||||||
# No challenge data
|
await trt.execute(trait.COMMAND_LOCKUNLOCK, PIN_DATA, {'lock': True}, {})
|
||||||
with pytest.raises(error.ChallengeNeeded) as err:
|
|
||||||
await trt.execute(
|
|
||||||
trait.COMMAND_LOCKUNLOCK, PIN_DATA, {'lock': True}, {})
|
|
||||||
assert len(calls) == 0
|
|
||||||
assert err.code == const.ERR_CHALLENGE_NEEDED
|
|
||||||
assert err.challenge_type == const.CHALLENGE_PIN_NEEDED
|
|
||||||
|
|
||||||
# invalid pin
|
|
||||||
with pytest.raises(error.ChallengeNeeded) as err:
|
|
||||||
await trt.execute(
|
|
||||||
trait.COMMAND_LOCKUNLOCK, PIN_DATA, {'lock': True},
|
|
||||||
{'pin': 9999})
|
|
||||||
assert len(calls) == 0
|
|
||||||
assert err.code == const.ERR_CHALLENGE_NEEDED
|
|
||||||
assert err.challenge_type == const.CHALLENGE_FAILED_PIN_NEEDED
|
|
||||||
|
|
||||||
await trt.execute(trait.COMMAND_LOCKUNLOCK, PIN_DATA, {'lock': True},
|
|
||||||
{'pin': '1234'})
|
|
||||||
|
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert calls[0].data == {
|
assert calls[0].data == {
|
||||||
@ -908,18 +890,18 @@ async def test_lock_unlock_unlock(hass):
|
|||||||
with pytest.raises(error.ChallengeNeeded) as err:
|
with pytest.raises(error.ChallengeNeeded) as err:
|
||||||
await trt.execute(
|
await trt.execute(
|
||||||
trait.COMMAND_LOCKUNLOCK, PIN_DATA, {'lock': False}, {})
|
trait.COMMAND_LOCKUNLOCK, PIN_DATA, {'lock': False}, {})
|
||||||
assert len(calls) == 0
|
assert len(calls) == 0
|
||||||
assert err.code == const.ERR_CHALLENGE_NEEDED
|
assert err.value.code == const.ERR_CHALLENGE_NEEDED
|
||||||
assert err.challenge_type == const.CHALLENGE_PIN_NEEDED
|
assert err.value.challenge_type == const.CHALLENGE_PIN_NEEDED
|
||||||
|
|
||||||
# invalid pin
|
# invalid pin
|
||||||
with pytest.raises(error.ChallengeNeeded) as err:
|
with pytest.raises(error.ChallengeNeeded) as err:
|
||||||
await trt.execute(
|
await trt.execute(
|
||||||
trait.COMMAND_LOCKUNLOCK, PIN_DATA, {'lock': False},
|
trait.COMMAND_LOCKUNLOCK, PIN_DATA, {'lock': False},
|
||||||
{'pin': 9999})
|
{'pin': 9999})
|
||||||
assert len(calls) == 0
|
assert len(calls) == 0
|
||||||
assert err.code == const.ERR_CHALLENGE_NEEDED
|
assert err.value.code == const.ERR_CHALLENGE_NEEDED
|
||||||
assert err.challenge_type == const.CHALLENGE_FAILED_PIN_NEEDED
|
assert err.value.challenge_type == const.CHALLENGE_FAILED_PIN_NEEDED
|
||||||
|
|
||||||
await trt.execute(
|
await trt.execute(
|
||||||
trait.COMMAND_LOCKUNLOCK, PIN_DATA, {'lock': False}, {'pin': '1234'})
|
trait.COMMAND_LOCKUNLOCK, PIN_DATA, {'lock': False}, {'pin': '1234'})
|
||||||
@ -929,6 +911,17 @@ async def test_lock_unlock_unlock(hass):
|
|||||||
ATTR_ENTITY_ID: 'lock.front_door'
|
ATTR_ENTITY_ID: 'lock.front_door'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Test without pin
|
||||||
|
trt = trait.LockUnlockTrait(hass,
|
||||||
|
State('lock.front_door', lock.STATE_LOCKED),
|
||||||
|
BASIC_CONFIG)
|
||||||
|
|
||||||
|
with pytest.raises(error.SmartHomeError) as err:
|
||||||
|
await trt.execute(
|
||||||
|
trait.COMMAND_LOCKUNLOCK, BASIC_DATA, {'lock': False}, {})
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert err.value.code == const.ERR_CHALLENGE_NOT_SETUP
|
||||||
|
|
||||||
|
|
||||||
async def test_fan_speed(hass):
|
async def test_fan_speed(hass):
|
||||||
"""Test FanSpeed trait speed control support for fan domain."""
|
"""Test FanSpeed trait speed control support for fan domain."""
|
||||||
@ -1243,18 +1236,18 @@ async def test_openclose_cover_secure(hass, device_class):
|
|||||||
await trt.execute(
|
await trt.execute(
|
||||||
trait.COMMAND_OPENCLOSE, PIN_DATA,
|
trait.COMMAND_OPENCLOSE, PIN_DATA,
|
||||||
{'openPercent': 50}, {})
|
{'openPercent': 50}, {})
|
||||||
assert len(calls) == 0
|
assert len(calls) == 0
|
||||||
assert err.code == const.ERR_CHALLENGE_NEEDED
|
assert err.value.code == const.ERR_CHALLENGE_NEEDED
|
||||||
assert err.challenge_type == const.CHALLENGE_PIN_NEEDED
|
assert err.value.challenge_type == const.CHALLENGE_PIN_NEEDED
|
||||||
|
|
||||||
# invalid pin
|
# invalid pin
|
||||||
with pytest.raises(error.ChallengeNeeded) as err:
|
with pytest.raises(error.ChallengeNeeded) as err:
|
||||||
await trt.execute(
|
await trt.execute(
|
||||||
trait.COMMAND_OPENCLOSE, PIN_DATA,
|
trait.COMMAND_OPENCLOSE, PIN_DATA,
|
||||||
{'openPercent': 50}, {'pin': '9999'})
|
{'openPercent': 50}, {'pin': '9999'})
|
||||||
assert len(calls) == 0
|
assert len(calls) == 0
|
||||||
assert err.code == const.ERR_CHALLENGE_NEEDED
|
assert err.value.code == const.ERR_CHALLENGE_NEEDED
|
||||||
assert err.challenge_type == const.CHALLENGE_FAILED_PIN_NEEDED
|
assert err.value.challenge_type == const.CHALLENGE_FAILED_PIN_NEEDED
|
||||||
|
|
||||||
await trt.execute(
|
await trt.execute(
|
||||||
trait.COMMAND_OPENCLOSE, PIN_DATA,
|
trait.COMMAND_OPENCLOSE, PIN_DATA,
|
||||||
@ -1265,6 +1258,17 @@ async def test_openclose_cover_secure(hass, device_class):
|
|||||||
cover.ATTR_POSITION: 50
|
cover.ATTR_POSITION: 50
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# no challenge on close
|
||||||
|
calls = async_mock_service(
|
||||||
|
hass, cover.DOMAIN, cover.SERVICE_CLOSE_COVER)
|
||||||
|
await trt.execute(
|
||||||
|
trait.COMMAND_OPENCLOSE, PIN_DATA,
|
||||||
|
{'openPercent': 0}, {})
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].data == {
|
||||||
|
ATTR_ENTITY_ID: 'cover.bla'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('device_class', (
|
@pytest.mark.parametrize('device_class', (
|
||||||
binary_sensor.DEVICE_CLASS_DOOR,
|
binary_sensor.DEVICE_CLASS_DOOR,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user