Add HomeKit low battery threshold config (#23363)
This commit is contained in:
parent
2aee31ec6a
commit
b4e2a0ef84
5 changed files with 61 additions and 27 deletions
|
@ -19,8 +19,8 @@ from homeassistant.util import dt as dt_util
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_DISPLAY_NAME, ATTR_VALUE, BRIDGE_MODEL, BRIDGE_SERIAL_NUMBER,
|
ATTR_DISPLAY_NAME, ATTR_VALUE, BRIDGE_MODEL, BRIDGE_SERIAL_NUMBER,
|
||||||
CHAR_BATTERY_LEVEL, CHAR_CHARGING_STATE, CHAR_STATUS_LOW_BATTERY,
|
CHAR_BATTERY_LEVEL, CHAR_CHARGING_STATE, CHAR_STATUS_LOW_BATTERY,
|
||||||
CONF_LINKED_BATTERY_SENSOR, DEBOUNCE_TIMEOUT, EVENT_HOMEKIT_CHANGED,
|
CONF_LINKED_BATTERY_SENSOR, CONF_LOW_BATTERY_THRESHOLD, DEBOUNCE_TIMEOUT,
|
||||||
MANUFACTURER, SERV_BATTERY_SERVICE)
|
EVENT_HOMEKIT_CHANGED, MANUFACTURER, SERV_BATTERY_SERVICE)
|
||||||
from .util import convert_to_float, dismiss_setup_message, show_setup_message
|
from .util import convert_to_float, dismiss_setup_message, show_setup_message
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -73,6 +73,8 @@ class HomeAccessory(Accessory):
|
||||||
self._support_battery_charging = True
|
self._support_battery_charging = True
|
||||||
self.linked_battery_sensor = \
|
self.linked_battery_sensor = \
|
||||||
self.config.get(CONF_LINKED_BATTERY_SENSOR)
|
self.config.get(CONF_LINKED_BATTERY_SENSOR)
|
||||||
|
self.low_battery_threshold = \
|
||||||
|
self.config.get(CONF_LOW_BATTERY_THRESHOLD)
|
||||||
|
|
||||||
"""Add battery service if available"""
|
"""Add battery service if available"""
|
||||||
battery_found = self.hass.states.get(self.entity_id).attributes \
|
battery_found = self.hass.states.get(self.entity_id).attributes \
|
||||||
|
@ -147,7 +149,8 @@ class HomeAccessory(Accessory):
|
||||||
if battery_level is None:
|
if battery_level is None:
|
||||||
return
|
return
|
||||||
self._char_battery.set_value(battery_level)
|
self._char_battery.set_value(battery_level)
|
||||||
self._char_low_battery.set_value(battery_level < 20)
|
self._char_low_battery.set_value(
|
||||||
|
battery_level < self.low_battery_threshold)
|
||||||
_LOGGER.debug('%s: Updated battery level to %d', self.entity_id,
|
_LOGGER.debug('%s: Updated battery level to %d', self.entity_id,
|
||||||
battery_level)
|
battery_level)
|
||||||
if not self._support_battery_charging:
|
if not self._support_battery_charging:
|
||||||
|
|
|
@ -16,10 +16,12 @@ CONF_FEATURE = 'feature'
|
||||||
CONF_FEATURE_LIST = 'feature_list'
|
CONF_FEATURE_LIST = 'feature_list'
|
||||||
CONF_FILTER = 'filter'
|
CONF_FILTER = 'filter'
|
||||||
CONF_LINKED_BATTERY_SENSOR = 'linked_battery_sensor'
|
CONF_LINKED_BATTERY_SENSOR = 'linked_battery_sensor'
|
||||||
|
CONF_LOW_BATTERY_THRESHOLD = 'low_battery_threshold'
|
||||||
CONF_SAFE_MODE = 'safe_mode'
|
CONF_SAFE_MODE = 'safe_mode'
|
||||||
|
|
||||||
# #### Config Defaults ####
|
# #### Config Defaults ####
|
||||||
DEFAULT_AUTO_START = True
|
DEFAULT_AUTO_START = True
|
||||||
|
DEFAULT_LOW_BATTERY_THRESHOLD = 20
|
||||||
DEFAULT_PORT = 51827
|
DEFAULT_PORT = 51827
|
||||||
DEFAULT_SAFE_MODE = False
|
DEFAULT_SAFE_MODE = False
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,8 @@ import homeassistant.util.temperature as temp_util
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_FEATURE, CONF_FEATURE_LIST, CONF_LINKED_BATTERY_SENSOR,
|
CONF_FEATURE, CONF_FEATURE_LIST, CONF_LINKED_BATTERY_SENSOR,
|
||||||
FEATURE_ON_OFF, FEATURE_PLAY_PAUSE, FEATURE_PLAY_STOP, FEATURE_TOGGLE_MUTE,
|
CONF_LOW_BATTERY_THRESHOLD, DEFAULT_LOW_BATTERY_THRESHOLD, FEATURE_ON_OFF,
|
||||||
|
FEATURE_PLAY_PAUSE, FEATURE_PLAY_STOP, FEATURE_TOGGLE_MUTE,
|
||||||
HOMEKIT_NOTIFY_ID, TYPE_FAUCET, TYPE_OUTLET, TYPE_SHOWER, TYPE_SPRINKLER,
|
HOMEKIT_NOTIFY_ID, TYPE_FAUCET, TYPE_OUTLET, TYPE_SHOWER, TYPE_SPRINKLER,
|
||||||
TYPE_SWITCH, TYPE_VALVE)
|
TYPE_SWITCH, TYPE_VALVE)
|
||||||
|
|
||||||
|
@ -23,6 +24,8 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
BASIC_INFO_SCHEMA = vol.Schema({
|
BASIC_INFO_SCHEMA = vol.Schema({
|
||||||
vol.Optional(CONF_NAME): cv.string,
|
vol.Optional(CONF_NAME): cv.string,
|
||||||
vol.Optional(CONF_LINKED_BATTERY_SENSOR): cv.entity_domain(sensor.DOMAIN),
|
vol.Optional(CONF_LINKED_BATTERY_SENSOR): cv.entity_domain(sensor.DOMAIN),
|
||||||
|
vol.Optional(CONF_LOW_BATTERY_THRESHOLD,
|
||||||
|
default=DEFAULT_LOW_BATTERY_THRESHOLD): cv.positive_int,
|
||||||
})
|
})
|
||||||
|
|
||||||
FEATURE_SCHEMA = BASIC_INFO_SCHEMA.extend({
|
FEATURE_SCHEMA = BASIC_INFO_SCHEMA.extend({
|
||||||
|
|
|
@ -13,7 +13,8 @@ from homeassistant.components.homekit.const import (
|
||||||
ATTR_DISPLAY_NAME, ATTR_VALUE,
|
ATTR_DISPLAY_NAME, ATTR_VALUE,
|
||||||
BRIDGE_MODEL, BRIDGE_NAME, BRIDGE_SERIAL_NUMBER, CHAR_FIRMWARE_REVISION,
|
BRIDGE_MODEL, BRIDGE_NAME, BRIDGE_SERIAL_NUMBER, CHAR_FIRMWARE_REVISION,
|
||||||
CHAR_MANUFACTURER, CHAR_MODEL, CHAR_NAME, CHAR_SERIAL_NUMBER,
|
CHAR_MANUFACTURER, CHAR_MODEL, CHAR_NAME, CHAR_SERIAL_NUMBER,
|
||||||
CONF_LINKED_BATTERY_SENSOR, MANUFACTURER, SERV_ACCESSORY_INFO)
|
CONF_LINKED_BATTERY_SENSOR, CONF_LOW_BATTERY_THRESHOLD,
|
||||||
|
DEFAULT_LOW_BATTERY_THRESHOLD, MANUFACTURER, SERV_ACCESSORY_INFO)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
__version__, ATTR_BATTERY_CHARGING, ATTR_BATTERY_LEVEL, ATTR_ENTITY_ID,
|
__version__, ATTR_BATTERY_CHARGING, ATTR_BATTERY_LEVEL, ATTR_ENTITY_ID,
|
||||||
ATTR_SERVICE, ATTR_NOW, EVENT_TIME_CHANGED)
|
ATTR_SERVICE, ATTR_NOW, EVENT_TIME_CHANGED)
|
||||||
|
@ -106,7 +107,9 @@ async def test_battery_service(hass, hk_driver, caplog):
|
||||||
hass.states.async_set(entity_id, None, {ATTR_BATTERY_LEVEL: 50})
|
hass.states.async_set(entity_id, None, {ATTR_BATTERY_LEVEL: 50})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
acc = HomeAccessory(hass, hk_driver, 'Battery Service', entity_id, 2, None)
|
acc = HomeAccessory(hass, hk_driver, 'Battery Service', entity_id, 2,
|
||||||
|
{CONF_LOW_BATTERY_THRESHOLD:
|
||||||
|
DEFAULT_LOW_BATTERY_THRESHOLD})
|
||||||
acc.update_state = lambda x: None
|
acc.update_state = lambda x: None
|
||||||
assert acc._char_battery.value == 0
|
assert acc._char_battery.value == 0
|
||||||
assert acc._char_low_battery.value == 0
|
assert acc._char_low_battery.value == 0
|
||||||
|
@ -136,7 +139,9 @@ async def test_battery_service(hass, hk_driver, caplog):
|
||||||
ATTR_BATTERY_LEVEL: 10, ATTR_BATTERY_CHARGING: True})
|
ATTR_BATTERY_LEVEL: 10, ATTR_BATTERY_CHARGING: True})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
acc = HomeAccessory(hass, hk_driver, 'Battery Service', entity_id, 2, None)
|
acc = HomeAccessory(hass, hk_driver, 'Battery Service', entity_id, 2,
|
||||||
|
{CONF_LOW_BATTERY_THRESHOLD:
|
||||||
|
DEFAULT_LOW_BATTERY_THRESHOLD})
|
||||||
acc.update_state = lambda x: None
|
acc.update_state = lambda x: None
|
||||||
assert acc._char_battery.value == 0
|
assert acc._char_battery.value == 0
|
||||||
assert acc._char_low_battery.value == 0
|
assert acc._char_low_battery.value == 0
|
||||||
|
@ -165,7 +170,9 @@ async def test_linked_battery_sensor(hass, hk_driver, caplog):
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
acc = HomeAccessory(hass, hk_driver, 'Battery Service', entity_id, 2,
|
acc = HomeAccessory(hass, hk_driver, 'Battery Service', entity_id, 2,
|
||||||
{CONF_LINKED_BATTERY_SENSOR: linked_battery})
|
{CONF_LINKED_BATTERY_SENSOR: linked_battery,
|
||||||
|
CONF_LOW_BATTERY_THRESHOLD:
|
||||||
|
DEFAULT_LOW_BATTERY_THRESHOLD})
|
||||||
acc.update_state = lambda x: None
|
acc.update_state = lambda x: None
|
||||||
assert acc.linked_battery_sensor == linked_battery
|
assert acc.linked_battery_sensor == linked_battery
|
||||||
|
|
||||||
|
@ -191,17 +198,18 @@ async def test_linked_battery_sensor(hass, hk_driver, caplog):
|
||||||
assert acc._char_battery.value == 10
|
assert acc._char_battery.value == 10
|
||||||
assert 'ERROR' not in caplog.text
|
assert 'ERROR' not in caplog.text
|
||||||
|
|
||||||
# Test charging
|
# Test charging & low battery threshold
|
||||||
hass.states.async_set(linked_battery, 20, {ATTR_BATTERY_CHARGING: True})
|
hass.states.async_set(linked_battery, 20, {ATTR_BATTERY_CHARGING: True})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
acc = HomeAccessory(hass, hk_driver, 'Battery Service', entity_id, 2,
|
acc = HomeAccessory(hass, hk_driver, 'Battery Service', entity_id, 2,
|
||||||
{CONF_LINKED_BATTERY_SENSOR: linked_battery})
|
{CONF_LINKED_BATTERY_SENSOR: linked_battery,
|
||||||
|
CONF_LOW_BATTERY_THRESHOLD: 50})
|
||||||
acc.update_state = lambda x: None
|
acc.update_state = lambda x: None
|
||||||
await hass.async_add_job(acc.run)
|
await hass.async_add_job(acc.run)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc._char_battery.value == 20
|
assert acc._char_battery.value == 20
|
||||||
assert acc._char_low_battery.value == 0
|
assert acc._char_low_battery.value == 1
|
||||||
assert acc._char_charging.value == 1
|
assert acc._char_charging.value == 1
|
||||||
|
|
||||||
hass.states.async_set(linked_battery, 100, {ATTR_BATTERY_CHARGING: False})
|
hass.states.async_set(linked_battery, 100, {ATTR_BATTERY_CHARGING: False})
|
||||||
|
|
|
@ -4,8 +4,9 @@ import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.homekit.const import (
|
from homeassistant.components.homekit.const import (
|
||||||
CONF_FEATURE, CONF_FEATURE_LIST, CONF_LINKED_BATTERY_SENSOR,
|
CONF_FEATURE, CONF_FEATURE_LIST, CONF_LINKED_BATTERY_SENSOR,
|
||||||
FEATURE_ON_OFF, FEATURE_PLAY_PAUSE, HOMEKIT_NOTIFY_ID, TYPE_FAUCET,
|
CONF_LOW_BATTERY_THRESHOLD, FEATURE_ON_OFF, FEATURE_PLAY_PAUSE,
|
||||||
TYPE_OUTLET, TYPE_SHOWER, TYPE_SPRINKLER, TYPE_SWITCH, TYPE_VALVE)
|
HOMEKIT_NOTIFY_ID, TYPE_FAUCET, TYPE_OUTLET, TYPE_SHOWER, TYPE_SPRINKLER,
|
||||||
|
TYPE_SWITCH, TYPE_VALVE)
|
||||||
from homeassistant.components.homekit.util import (
|
from homeassistant.components.homekit.util import (
|
||||||
HomeKitSpeedMapping, SpeedRange, convert_to_float, density_to_air_quality,
|
HomeKitSpeedMapping, SpeedRange, convert_to_float, density_to_air_quality,
|
||||||
dismiss_setup_message, show_setup_message, temperature_to_homekit,
|
dismiss_setup_message, show_setup_message, temperature_to_homekit,
|
||||||
|
@ -28,6 +29,9 @@ def test_validate_entity_config():
|
||||||
{'binary_sensor.demo': {CONF_LINKED_BATTERY_SENSOR: None}},
|
{'binary_sensor.demo': {CONF_LINKED_BATTERY_SENSOR: None}},
|
||||||
{'binary_sensor.demo': {CONF_LINKED_BATTERY_SENSOR:
|
{'binary_sensor.demo': {CONF_LINKED_BATTERY_SENSOR:
|
||||||
'switch.demo'}},
|
'switch.demo'}},
|
||||||
|
{'binary_sensor.demo': {CONF_LOW_BATTERY_THRESHOLD:
|
||||||
|
'switch.demo'}},
|
||||||
|
{'binary_sensor.demo': {CONF_LOW_BATTERY_THRESHOLD: -10}},
|
||||||
{'demo.test': 'test'}, {'demo.test': [1, 2]},
|
{'demo.test': 'test'}, {'demo.test': [1, 2]},
|
||||||
{'demo.test': None}, {'demo.test': {CONF_NAME: None}},
|
{'demo.test': None}, {'demo.test': {CONF_NAME: None}},
|
||||||
{'media_player.test': {CONF_FEATURE_LIST: [
|
{'media_player.test': {CONF_FEATURE_LIST: [
|
||||||
|
@ -43,42 +47,56 @@ def test_validate_entity_config():
|
||||||
|
|
||||||
assert vec({}) == {}
|
assert vec({}) == {}
|
||||||
assert vec({'demo.test': {CONF_NAME: 'Name'}}) == \
|
assert vec({'demo.test': {CONF_NAME: 'Name'}}) == \
|
||||||
{'demo.test': {CONF_NAME: 'Name'}}
|
{'demo.test': {CONF_NAME: 'Name', CONF_LOW_BATTERY_THRESHOLD: 20}}
|
||||||
|
|
||||||
assert vec({'binary_sensor.demo': {CONF_LINKED_BATTERY_SENSOR:
|
assert vec({'binary_sensor.demo': {CONF_LINKED_BATTERY_SENSOR:
|
||||||
'sensor.demo_battery'}}) == \
|
'sensor.demo_battery'}}) == \
|
||||||
{'binary_sensor.demo': {CONF_LINKED_BATTERY_SENSOR:
|
{'binary_sensor.demo': {CONF_LINKED_BATTERY_SENSOR:
|
||||||
'sensor.demo_battery'}}
|
'sensor.demo_battery',
|
||||||
|
CONF_LOW_BATTERY_THRESHOLD: 20}}
|
||||||
|
assert vec({'binary_sensor.demo': {CONF_LOW_BATTERY_THRESHOLD: 50}}) == \
|
||||||
|
{'binary_sensor.demo': {CONF_LOW_BATTERY_THRESHOLD: 50}}
|
||||||
|
|
||||||
assert vec({'alarm_control_panel.demo': {}}) == \
|
assert vec({'alarm_control_panel.demo': {}}) == \
|
||||||
{'alarm_control_panel.demo': {ATTR_CODE: None}}
|
{'alarm_control_panel.demo': {ATTR_CODE: None,
|
||||||
|
CONF_LOW_BATTERY_THRESHOLD: 20}}
|
||||||
assert vec({'alarm_control_panel.demo': {ATTR_CODE: '1234'}}) == \
|
assert vec({'alarm_control_panel.demo': {ATTR_CODE: '1234'}}) == \
|
||||||
{'alarm_control_panel.demo': {ATTR_CODE: '1234'}}
|
{'alarm_control_panel.demo': {ATTR_CODE: '1234',
|
||||||
|
CONF_LOW_BATTERY_THRESHOLD: 20}}
|
||||||
|
|
||||||
assert vec({'lock.demo': {}}) == {'lock.demo': {ATTR_CODE: None}}
|
assert vec({'lock.demo': {}}) == \
|
||||||
|
{'lock.demo': {ATTR_CODE: None, CONF_LOW_BATTERY_THRESHOLD: 20}}
|
||||||
assert vec({'lock.demo': {ATTR_CODE: '1234'}}) == \
|
assert vec({'lock.demo': {ATTR_CODE: '1234'}}) == \
|
||||||
{'lock.demo': {ATTR_CODE: '1234'}}
|
{'lock.demo': {ATTR_CODE: '1234', CONF_LOW_BATTERY_THRESHOLD: 20}}
|
||||||
|
|
||||||
assert vec({'media_player.demo': {}}) == \
|
assert vec({'media_player.demo': {}}) == \
|
||||||
{'media_player.demo': {CONF_FEATURE_LIST: {}}}
|
{'media_player.demo': {CONF_FEATURE_LIST: {},
|
||||||
|
CONF_LOW_BATTERY_THRESHOLD: 20}}
|
||||||
config = {CONF_FEATURE_LIST: [{CONF_FEATURE: FEATURE_ON_OFF},
|
config = {CONF_FEATURE_LIST: [{CONF_FEATURE: FEATURE_ON_OFF},
|
||||||
{CONF_FEATURE: FEATURE_PLAY_PAUSE}]}
|
{CONF_FEATURE: FEATURE_PLAY_PAUSE}]}
|
||||||
assert vec({'media_player.demo': config}) == \
|
assert vec({'media_player.demo': config}) == \
|
||||||
{'media_player.demo': {CONF_FEATURE_LIST:
|
{'media_player.demo': {CONF_FEATURE_LIST:
|
||||||
{FEATURE_ON_OFF: {}, FEATURE_PLAY_PAUSE: {}}}}
|
{FEATURE_ON_OFF: {}, FEATURE_PLAY_PAUSE: {}},
|
||||||
|
CONF_LOW_BATTERY_THRESHOLD: 20}}
|
||||||
|
|
||||||
assert vec({'switch.demo': {CONF_TYPE: TYPE_FAUCET}}) == \
|
assert vec({'switch.demo': {CONF_TYPE: TYPE_FAUCET}}) == \
|
||||||
{'switch.demo': {CONF_TYPE: TYPE_FAUCET}}
|
{'switch.demo': {CONF_TYPE: TYPE_FAUCET, CONF_LOW_BATTERY_THRESHOLD:
|
||||||
|
20}}
|
||||||
assert vec({'switch.demo': {CONF_TYPE: TYPE_OUTLET}}) == \
|
assert vec({'switch.demo': {CONF_TYPE: TYPE_OUTLET}}) == \
|
||||||
{'switch.demo': {CONF_TYPE: TYPE_OUTLET}}
|
{'switch.demo': {CONF_TYPE: TYPE_OUTLET, CONF_LOW_BATTERY_THRESHOLD:
|
||||||
|
20}}
|
||||||
assert vec({'switch.demo': {CONF_TYPE: TYPE_SHOWER}}) == \
|
assert vec({'switch.demo': {CONF_TYPE: TYPE_SHOWER}}) == \
|
||||||
{'switch.demo': {CONF_TYPE: TYPE_SHOWER}}
|
{'switch.demo': {CONF_TYPE: TYPE_SHOWER, CONF_LOW_BATTERY_THRESHOLD:
|
||||||
|
20}}
|
||||||
assert vec({'switch.demo': {CONF_TYPE: TYPE_SPRINKLER}}) == \
|
assert vec({'switch.demo': {CONF_TYPE: TYPE_SPRINKLER}}) == \
|
||||||
{'switch.demo': {CONF_TYPE: TYPE_SPRINKLER}}
|
{'switch.demo': {CONF_TYPE: TYPE_SPRINKLER, CONF_LOW_BATTERY_THRESHOLD:
|
||||||
|
20}}
|
||||||
assert vec({'switch.demo': {CONF_TYPE: TYPE_SWITCH}}) == \
|
assert vec({'switch.demo': {CONF_TYPE: TYPE_SWITCH}}) == \
|
||||||
{'switch.demo': {CONF_TYPE: TYPE_SWITCH}}
|
{'switch.demo': {CONF_TYPE: TYPE_SWITCH, CONF_LOW_BATTERY_THRESHOLD:
|
||||||
|
20}}
|
||||||
assert vec({'switch.demo': {CONF_TYPE: TYPE_VALVE}}) == \
|
assert vec({'switch.demo': {CONF_TYPE: TYPE_VALVE}}) == \
|
||||||
{'switch.demo': {CONF_TYPE: TYPE_VALVE}}
|
{'switch.demo': {CONF_TYPE: TYPE_VALVE, CONF_LOW_BATTERY_THRESHOLD:
|
||||||
|
20}}
|
||||||
|
|
||||||
|
|
||||||
def test_validate_media_player_features():
|
def test_validate_media_player_features():
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue