mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Show device details in homekit accessory service info (#35100)
* Show device info in homekit accessory service info * fix conflict
This commit is contained in:
parent
6d9f561853
commit
0a9b373edb
@ -34,6 +34,7 @@ from homeassistant.helpers.entityfilter import (
|
|||||||
CONF_INCLUDE_ENTITIES,
|
CONF_INCLUDE_ENTITIES,
|
||||||
convert_filter,
|
convert_filter,
|
||||||
)
|
)
|
||||||
|
from homeassistant.loader import async_get_integration
|
||||||
from homeassistant.util import get_local_ip
|
from homeassistant.util import get_local_ip
|
||||||
|
|
||||||
from .accessories import get_accessory
|
from .accessories import get_accessory
|
||||||
@ -41,6 +42,10 @@ from .aidmanager import AccessoryAidStorage
|
|||||||
from .const import (
|
from .const import (
|
||||||
AID_STORAGE,
|
AID_STORAGE,
|
||||||
ATTR_DISPLAY_NAME,
|
ATTR_DISPLAY_NAME,
|
||||||
|
ATTR_INTERGRATION,
|
||||||
|
ATTR_MANUFACTURER,
|
||||||
|
ATTR_MODEL,
|
||||||
|
ATTR_SOFTWARE_VERSION,
|
||||||
ATTR_VALUE,
|
ATTR_VALUE,
|
||||||
BRIDGE_NAME,
|
BRIDGE_NAME,
|
||||||
CONF_ADVERTISE_IP,
|
CONF_ADVERTISE_IP,
|
||||||
@ -200,7 +205,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
aid_storage = AccessoryAidStorage(hass, entry.entry_id)
|
aid_storage = AccessoryAidStorage(hass, entry.entry_id)
|
||||||
|
|
||||||
await aid_storage.async_initialize()
|
await aid_storage.async_initialize()
|
||||||
# These are yaml only
|
# ip_address and advertise_ip are yaml only
|
||||||
ip_address = conf.get(CONF_IP_ADDRESS)
|
ip_address = conf.get(CONF_IP_ADDRESS)
|
||||||
advertise_ip = conf.get(CONF_ADVERTISE_IP)
|
advertise_ip = conf.get(CONF_ADVERTISE_IP)
|
||||||
|
|
||||||
@ -494,6 +499,7 @@ class HomeKit:
|
|||||||
self.status = STATUS_WAIT
|
self.status = STATUS_WAIT
|
||||||
|
|
||||||
ent_reg = await entity_registry.async_get_registry(self.hass)
|
ent_reg = await entity_registry.async_get_registry(self.hass)
|
||||||
|
dev_reg = await device_registry.async_get_registry(self.hass)
|
||||||
|
|
||||||
device_lookup = ent_reg.async_get_device_class_lookup(
|
device_lookup = ent_reg.async_get_device_class_lookup(
|
||||||
{
|
{
|
||||||
@ -507,16 +513,24 @@ class HomeKit:
|
|||||||
if not self._filter(state.entity_id):
|
if not self._filter(state.entity_id):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self._async_configure_linked_battery_sensors(ent_reg, device_lookup, state)
|
ent_reg_ent = ent_reg.async_get(state.entity_id)
|
||||||
|
if ent_reg_ent:
|
||||||
|
await self._async_set_device_info_attributes(
|
||||||
|
ent_reg_ent, dev_reg, state.entity_id
|
||||||
|
)
|
||||||
|
self._async_configure_linked_battery_sensors(
|
||||||
|
ent_reg_ent, device_lookup, state
|
||||||
|
)
|
||||||
|
|
||||||
bridged_states.append(state)
|
bridged_states.append(state)
|
||||||
|
|
||||||
|
self._async_register_bridge(dev_reg)
|
||||||
await self.hass.async_add_executor_job(self._start, bridged_states)
|
await self.hass.async_add_executor_job(self._start, bridged_states)
|
||||||
await self._async_register_bridge()
|
|
||||||
|
|
||||||
async def _async_register_bridge(self):
|
@callback
|
||||||
|
def _async_register_bridge(self, dev_reg):
|
||||||
"""Register the bridge as a device so homekit_controller and exclude it from discovery."""
|
"""Register the bridge as a device so homekit_controller and exclude it from discovery."""
|
||||||
registry = await device_registry.async_get_registry(self.hass)
|
dev_reg.async_get_or_create(
|
||||||
registry.async_get_or_create(
|
|
||||||
config_entry_id=self._entry_id,
|
config_entry_id=self._entry_id,
|
||||||
connections={
|
connections={
|
||||||
(device_registry.CONNECTION_NETWORK_MAC, self.driver.state.mac)
|
(device_registry.CONNECTION_NETWORK_MAC, self.driver.state.mac)
|
||||||
@ -567,21 +581,21 @@ class HomeKit:
|
|||||||
self.hass.add_job(self.driver.stop)
|
self.hass.add_job(self.driver.stop)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_configure_linked_battery_sensors(self, ent_reg, device_lookup, state):
|
def _async_configure_linked_battery_sensors(
|
||||||
entry = ent_reg.async_get(state.entity_id)
|
self, ent_reg_ent, device_lookup, state
|
||||||
|
):
|
||||||
if (
|
if (
|
||||||
entry is None
|
ent_reg_ent is None
|
||||||
or entry.device_id is None
|
or ent_reg_ent.device_id is None
|
||||||
or entry.device_id not in device_lookup
|
or ent_reg_ent.device_id not in device_lookup
|
||||||
or entry.device_class
|
or ent_reg_ent.device_class
|
||||||
in (DEVICE_CLASS_BATTERY_CHARGING, DEVICE_CLASS_BATTERY)
|
in (DEVICE_CLASS_BATTERY_CHARGING, DEVICE_CLASS_BATTERY)
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
if ATTR_BATTERY_CHARGING not in state.attributes:
|
if ATTR_BATTERY_CHARGING not in state.attributes:
|
||||||
battery_charging_binary_sensor_entity_id = device_lookup[
|
battery_charging_binary_sensor_entity_id = device_lookup[
|
||||||
entry.device_id
|
ent_reg_ent.device_id
|
||||||
].get(("binary_sensor", DEVICE_CLASS_BATTERY_CHARGING))
|
].get(("binary_sensor", DEVICE_CLASS_BATTERY_CHARGING))
|
||||||
if battery_charging_binary_sensor_entity_id:
|
if battery_charging_binary_sensor_entity_id:
|
||||||
self._config.setdefault(state.entity_id, {}).setdefault(
|
self._config.setdefault(state.entity_id, {}).setdefault(
|
||||||
@ -590,7 +604,7 @@ class HomeKit:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if ATTR_BATTERY_LEVEL not in state.attributes:
|
if ATTR_BATTERY_LEVEL not in state.attributes:
|
||||||
battery_sensor_entity_id = device_lookup[entry.device_id].get(
|
battery_sensor_entity_id = device_lookup[ent_reg_ent.device_id].get(
|
||||||
("sensor", DEVICE_CLASS_BATTERY)
|
("sensor", DEVICE_CLASS_BATTERY)
|
||||||
)
|
)
|
||||||
if battery_sensor_entity_id:
|
if battery_sensor_entity_id:
|
||||||
@ -598,6 +612,21 @@ class HomeKit:
|
|||||||
CONF_LINKED_BATTERY_SENSOR, battery_sensor_entity_id
|
CONF_LINKED_BATTERY_SENSOR, battery_sensor_entity_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def _async_set_device_info_attributes(self, ent_reg_ent, dev_reg, entity_id):
|
||||||
|
"""Set attributes that will be used for homekit device info."""
|
||||||
|
ent_cfg = self._config.setdefault(entity_id, {})
|
||||||
|
if ent_reg_ent.device_id:
|
||||||
|
dev_reg_ent = dev_reg.async_get(ent_reg_ent.device_id)
|
||||||
|
if dev_reg_ent.manufacturer:
|
||||||
|
ent_cfg[ATTR_MANUFACTURER] = dev_reg_ent.manufacturer
|
||||||
|
if dev_reg_ent.model:
|
||||||
|
ent_cfg[ATTR_MODEL] = dev_reg_ent.model
|
||||||
|
if dev_reg_ent.sw_version:
|
||||||
|
ent_cfg[ATTR_SOFTWARE_VERSION] = dev_reg_ent.sw_version
|
||||||
|
if ATTR_MANUFACTURER not in ent_cfg:
|
||||||
|
integration = await async_get_integration(self.hass, ent_reg_ent.platform)
|
||||||
|
ent_cfg[ATTR_INTERGRATION] = integration.name
|
||||||
|
|
||||||
|
|
||||||
class HomeKitPairingQRView(HomeAssistantView):
|
class HomeKitPairingQRView(HomeAssistantView):
|
||||||
"""Display the homekit pairing code at a protected url."""
|
"""Display the homekit pairing code at a protected url."""
|
||||||
|
@ -40,6 +40,10 @@ from homeassistant.util.decorator import Registry
|
|||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_DISPLAY_NAME,
|
ATTR_DISPLAY_NAME,
|
||||||
|
ATTR_INTERGRATION,
|
||||||
|
ATTR_MANUFACTURER,
|
||||||
|
ATTR_MODEL,
|
||||||
|
ATTR_SOFTWARE_VERSION,
|
||||||
ATTR_VALUE,
|
ATTR_VALUE,
|
||||||
BRIDGE_MODEL,
|
BRIDGE_MODEL,
|
||||||
BRIDGE_SERIAL_NUMBER,
|
BRIDGE_SERIAL_NUMBER,
|
||||||
@ -235,15 +239,32 @@ class HomeAccessory(Accessory):
|
|||||||
):
|
):
|
||||||
"""Initialize a Accessory object."""
|
"""Initialize a Accessory object."""
|
||||||
super().__init__(driver=driver, display_name=name, aid=aid, *args, **kwargs)
|
super().__init__(driver=driver, display_name=name, aid=aid, *args, **kwargs)
|
||||||
model = split_entity_id(entity_id)[0].replace("_", " ").title()
|
self.config = config or {}
|
||||||
|
domain = split_entity_id(entity_id)[0].replace("_", " ")
|
||||||
|
|
||||||
|
if ATTR_MANUFACTURER in self.config:
|
||||||
|
manufacturer = self.config[ATTR_MANUFACTURER]
|
||||||
|
elif ATTR_INTERGRATION in self.config:
|
||||||
|
manufacturer = self.config[ATTR_INTERGRATION].replace("_", " ").title()
|
||||||
|
else:
|
||||||
|
manufacturer = f"{MANUFACTURER} {domain}".title()
|
||||||
|
if ATTR_MODEL in self.config:
|
||||||
|
model = self.config[ATTR_MODEL]
|
||||||
|
else:
|
||||||
|
model = domain.title()
|
||||||
|
if ATTR_SOFTWARE_VERSION in self.config:
|
||||||
|
sw_version = self.config[ATTR_SOFTWARE_VERSION]
|
||||||
|
else:
|
||||||
|
sw_version = __version__
|
||||||
|
|
||||||
self.set_info_service(
|
self.set_info_service(
|
||||||
firmware_revision=__version__,
|
manufacturer=manufacturer,
|
||||||
manufacturer=MANUFACTURER,
|
|
||||||
model=model,
|
model=model,
|
||||||
serial_number=entity_id,
|
serial_number=entity_id,
|
||||||
|
firmware_revision=sw_version,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.category = category
|
self.category = category
|
||||||
self.config = config or {}
|
|
||||||
self.entity_id = entity_id
|
self.entity_id = entity_id
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.debounce = {}
|
self.debounce = {}
|
||||||
|
@ -23,6 +23,10 @@ AUDIO_CODEC_COPY = "copy"
|
|||||||
# #### Attributes ####
|
# #### Attributes ####
|
||||||
ATTR_DISPLAY_NAME = "display_name"
|
ATTR_DISPLAY_NAME = "display_name"
|
||||||
ATTR_VALUE = "value"
|
ATTR_VALUE = "value"
|
||||||
|
ATTR_INTERGRATION = "platform"
|
||||||
|
ATTR_MANUFACTURER = "manufacturer"
|
||||||
|
ATTR_MODEL = "model"
|
||||||
|
ATTR_SOFTWARE_VERSION = "sw_version"
|
||||||
|
|
||||||
# #### Config ####
|
# #### Config ####
|
||||||
CONF_ADVERTISE_IP = "advertise_ip"
|
CONF_ADVERTISE_IP = "advertise_ip"
|
||||||
@ -50,6 +54,7 @@ CONF_VIDEO_MAP = "video_map"
|
|||||||
CONF_VIDEO_PACKET_SIZE = "video_packet_size"
|
CONF_VIDEO_PACKET_SIZE = "video_packet_size"
|
||||||
|
|
||||||
# #### Config Defaults ####
|
# #### Config Defaults ####
|
||||||
|
DEFAULT_SUPPORT_AUDIO = False
|
||||||
DEFAULT_AUDIO_CODEC = AUDIO_CODEC_OPUS
|
DEFAULT_AUDIO_CODEC = AUDIO_CODEC_OPUS
|
||||||
DEFAULT_AUDIO_MAP = "0:a:0"
|
DEFAULT_AUDIO_MAP = "0:a:0"
|
||||||
DEFAULT_AUDIO_PACKET_SIZE = 188
|
DEFAULT_AUDIO_PACKET_SIZE = 188
|
||||||
|
@ -33,10 +33,20 @@ from .const import (
|
|||||||
CONF_VIDEO_CODEC,
|
CONF_VIDEO_CODEC,
|
||||||
CONF_VIDEO_MAP,
|
CONF_VIDEO_MAP,
|
||||||
CONF_VIDEO_PACKET_SIZE,
|
CONF_VIDEO_PACKET_SIZE,
|
||||||
|
DEFAULT_AUDIO_CODEC,
|
||||||
|
DEFAULT_AUDIO_MAP,
|
||||||
|
DEFAULT_AUDIO_PACKET_SIZE,
|
||||||
|
DEFAULT_MAX_FPS,
|
||||||
|
DEFAULT_MAX_HEIGHT,
|
||||||
|
DEFAULT_MAX_WIDTH,
|
||||||
|
DEFAULT_SUPPORT_AUDIO,
|
||||||
|
DEFAULT_VIDEO_CODEC,
|
||||||
|
DEFAULT_VIDEO_MAP,
|
||||||
|
DEFAULT_VIDEO_PACKET_SIZE,
|
||||||
SERV_CAMERA_RTP_STREAM_MANAGEMENT,
|
SERV_CAMERA_RTP_STREAM_MANAGEMENT,
|
||||||
)
|
)
|
||||||
from .img_util import scale_jpeg_camera_image
|
from .img_util import scale_jpeg_camera_image
|
||||||
from .util import CAMERA_SCHEMA, pid_is_alive
|
from .util import pid_is_alive
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -94,6 +104,19 @@ FFMPEG_WATCHER = "ffmpeg_watcher"
|
|||||||
FFMPEG_PID = "ffmpeg_pid"
|
FFMPEG_PID = "ffmpeg_pid"
|
||||||
SESSION_ID = "session_id"
|
SESSION_ID = "session_id"
|
||||||
|
|
||||||
|
CONFIG_DEFAULTS = {
|
||||||
|
CONF_SUPPORT_AUDIO: DEFAULT_SUPPORT_AUDIO,
|
||||||
|
CONF_MAX_WIDTH: DEFAULT_MAX_WIDTH,
|
||||||
|
CONF_MAX_HEIGHT: DEFAULT_MAX_HEIGHT,
|
||||||
|
CONF_MAX_FPS: DEFAULT_MAX_FPS,
|
||||||
|
CONF_AUDIO_CODEC: DEFAULT_AUDIO_CODEC,
|
||||||
|
CONF_AUDIO_MAP: DEFAULT_AUDIO_MAP,
|
||||||
|
CONF_VIDEO_MAP: DEFAULT_VIDEO_MAP,
|
||||||
|
CONF_VIDEO_CODEC: DEFAULT_VIDEO_CODEC,
|
||||||
|
CONF_AUDIO_PACKET_SIZE: DEFAULT_AUDIO_PACKET_SIZE,
|
||||||
|
CONF_VIDEO_PACKET_SIZE: DEFAULT_VIDEO_PACKET_SIZE,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@TYPES.register("Camera")
|
@TYPES.register("Camera")
|
||||||
class Camera(HomeAccessory, PyhapCamera):
|
class Camera(HomeAccessory, PyhapCamera):
|
||||||
@ -104,11 +127,13 @@ class Camera(HomeAccessory, PyhapCamera):
|
|||||||
self._ffmpeg = hass.data[DATA_FFMPEG]
|
self._ffmpeg = hass.data[DATA_FFMPEG]
|
||||||
self._cur_session = None
|
self._cur_session = None
|
||||||
self._camera = hass.data[DOMAIN_CAMERA]
|
self._camera = hass.data[DOMAIN_CAMERA]
|
||||||
config_w_defaults = CAMERA_SCHEMA(config)
|
for config_key in CONFIG_DEFAULTS:
|
||||||
|
if config_key not in config:
|
||||||
|
config[config_key] = CONFIG_DEFAULTS[config_key]
|
||||||
|
|
||||||
max_fps = config_w_defaults[CONF_MAX_FPS]
|
max_fps = config[CONF_MAX_FPS]
|
||||||
max_width = config_w_defaults[CONF_MAX_WIDTH]
|
max_width = config[CONF_MAX_WIDTH]
|
||||||
max_height = config_w_defaults[CONF_MAX_HEIGHT]
|
max_height = config[CONF_MAX_HEIGHT]
|
||||||
resolutions = [
|
resolutions = [
|
||||||
(w, h, fps)
|
(w, h, fps)
|
||||||
for w, h, fps in SLOW_RESOLUTIONS
|
for w, h, fps in SLOW_RESOLUTIONS
|
||||||
@ -136,7 +161,7 @@ class Camera(HomeAccessory, PyhapCamera):
|
|||||||
}
|
}
|
||||||
audio_options = {"codecs": [{"type": "OPUS", "samplerate": 24}]}
|
audio_options = {"codecs": [{"type": "OPUS", "samplerate": 24}]}
|
||||||
|
|
||||||
stream_address = config_w_defaults.get(CONF_STREAM_ADDRESS, get_local_ip())
|
stream_address = config.get(CONF_STREAM_ADDRESS, get_local_ip())
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
"video": video_options,
|
"video": video_options,
|
||||||
@ -151,7 +176,7 @@ class Camera(HomeAccessory, PyhapCamera):
|
|||||||
name,
|
name,
|
||||||
entity_id,
|
entity_id,
|
||||||
aid,
|
aid,
|
||||||
config_w_defaults,
|
config,
|
||||||
category=CATEGORY_CAMERA,
|
category=CATEGORY_CAMERA,
|
||||||
options=options,
|
options=options,
|
||||||
)
|
)
|
||||||
|
@ -49,6 +49,7 @@ from .const import (
|
|||||||
DEFAULT_MAX_FPS,
|
DEFAULT_MAX_FPS,
|
||||||
DEFAULT_MAX_HEIGHT,
|
DEFAULT_MAX_HEIGHT,
|
||||||
DEFAULT_MAX_WIDTH,
|
DEFAULT_MAX_WIDTH,
|
||||||
|
DEFAULT_SUPPORT_AUDIO,
|
||||||
DEFAULT_VIDEO_CODEC,
|
DEFAULT_VIDEO_CODEC,
|
||||||
DEFAULT_VIDEO_MAP,
|
DEFAULT_VIDEO_MAP,
|
||||||
DEFAULT_VIDEO_PACKET_SIZE,
|
DEFAULT_VIDEO_PACKET_SIZE,
|
||||||
@ -98,7 +99,7 @@ CAMERA_SCHEMA = BASIC_INFO_SCHEMA.extend(
|
|||||||
vol.Optional(CONF_AUDIO_CODEC, default=DEFAULT_AUDIO_CODEC): vol.In(
|
vol.Optional(CONF_AUDIO_CODEC, default=DEFAULT_AUDIO_CODEC): vol.In(
|
||||||
VALID_AUDIO_CODECS
|
VALID_AUDIO_CODECS
|
||||||
),
|
),
|
||||||
vol.Optional(CONF_SUPPORT_AUDIO, default=False): cv.boolean,
|
vol.Optional(CONF_SUPPORT_AUDIO, default=DEFAULT_SUPPORT_AUDIO): cv.boolean,
|
||||||
vol.Optional(CONF_MAX_WIDTH, default=DEFAULT_MAX_WIDTH): cv.positive_int,
|
vol.Optional(CONF_MAX_WIDTH, default=DEFAULT_MAX_WIDTH): cv.positive_int,
|
||||||
vol.Optional(CONF_MAX_HEIGHT, default=DEFAULT_MAX_HEIGHT): cv.positive_int,
|
vol.Optional(CONF_MAX_HEIGHT, default=DEFAULT_MAX_HEIGHT): cv.positive_int,
|
||||||
vol.Optional(CONF_MAX_FPS, default=DEFAULT_MAX_FPS): cv.positive_int,
|
vol.Optional(CONF_MAX_FPS, default=DEFAULT_MAX_FPS): cv.positive_int,
|
||||||
|
@ -14,6 +14,10 @@ from homeassistant.components.homekit.accessories import (
|
|||||||
)
|
)
|
||||||
from homeassistant.components.homekit.const import (
|
from homeassistant.components.homekit.const import (
|
||||||
ATTR_DISPLAY_NAME,
|
ATTR_DISPLAY_NAME,
|
||||||
|
ATTR_INTERGRATION,
|
||||||
|
ATTR_MANUFACTURER,
|
||||||
|
ATTR_MODEL,
|
||||||
|
ATTR_SOFTWARE_VERSION,
|
||||||
ATTR_VALUE,
|
ATTR_VALUE,
|
||||||
BRIDGE_MODEL,
|
BRIDGE_MODEL,
|
||||||
BRIDGE_NAME,
|
BRIDGE_NAME,
|
||||||
@ -80,11 +84,17 @@ async def test_debounce(hass):
|
|||||||
|
|
||||||
async def test_home_accessory(hass, hk_driver):
|
async def test_home_accessory(hass, hk_driver):
|
||||||
"""Test HomeAccessory class."""
|
"""Test HomeAccessory class."""
|
||||||
entity_id = "homekit.accessory"
|
entity_id = "sensor.accessory"
|
||||||
|
entity_id2 = "light.accessory"
|
||||||
|
|
||||||
hass.states.async_set(entity_id, None)
|
hass.states.async_set(entity_id, None)
|
||||||
|
hass.states.async_set(entity_id2, None)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
acc = HomeAccessory(hass, hk_driver, "Home Accessory", entity_id, 2, None)
|
acc = HomeAccessory(
|
||||||
|
hass, hk_driver, "Home Accessory", entity_id, 2, {"platform": "isy994"}
|
||||||
|
)
|
||||||
assert acc.hass == hass
|
assert acc.hass == hass
|
||||||
assert acc.display_name == "Home Accessory"
|
assert acc.display_name == "Home Accessory"
|
||||||
assert acc.aid == 2
|
assert acc.aid == 2
|
||||||
@ -93,9 +103,35 @@ async def test_home_accessory(hass, hk_driver):
|
|||||||
serv = acc.services[0] # SERV_ACCESSORY_INFO
|
serv = acc.services[0] # SERV_ACCESSORY_INFO
|
||||||
assert serv.display_name == SERV_ACCESSORY_INFO
|
assert serv.display_name == SERV_ACCESSORY_INFO
|
||||||
assert serv.get_characteristic(CHAR_NAME).value == "Home Accessory"
|
assert serv.get_characteristic(CHAR_NAME).value == "Home Accessory"
|
||||||
assert serv.get_characteristic(CHAR_MANUFACTURER).value == MANUFACTURER
|
assert serv.get_characteristic(CHAR_MANUFACTURER).value == "Isy994"
|
||||||
assert serv.get_characteristic(CHAR_MODEL).value == "Homekit"
|
assert serv.get_characteristic(CHAR_MODEL).value == "Sensor"
|
||||||
assert serv.get_characteristic(CHAR_SERIAL_NUMBER).value == "homekit.accessory"
|
assert serv.get_characteristic(CHAR_SERIAL_NUMBER).value == "sensor.accessory"
|
||||||
|
|
||||||
|
acc2 = HomeAccessory(hass, hk_driver, "Home Accessory", entity_id2, 3, {})
|
||||||
|
serv = acc2.services[0] # SERV_ACCESSORY_INFO
|
||||||
|
assert serv.get_characteristic(CHAR_NAME).value == "Home Accessory"
|
||||||
|
assert serv.get_characteristic(CHAR_MANUFACTURER).value == f"{MANUFACTURER} Light"
|
||||||
|
assert serv.get_characteristic(CHAR_MODEL).value == "Light"
|
||||||
|
assert serv.get_characteristic(CHAR_SERIAL_NUMBER).value == "light.accessory"
|
||||||
|
|
||||||
|
acc3 = HomeAccessory(
|
||||||
|
hass,
|
||||||
|
hk_driver,
|
||||||
|
"Home Accessory",
|
||||||
|
entity_id2,
|
||||||
|
3,
|
||||||
|
{
|
||||||
|
ATTR_MODEL: "Awesome",
|
||||||
|
ATTR_MANUFACTURER: "Lux Brands",
|
||||||
|
ATTR_SOFTWARE_VERSION: "0.4.3",
|
||||||
|
ATTR_INTERGRATION: "luxe",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
serv = acc3.services[0] # SERV_ACCESSORY_INFO
|
||||||
|
assert serv.get_characteristic(CHAR_NAME).value == "Home Accessory"
|
||||||
|
assert serv.get_characteristic(CHAR_MANUFACTURER).value == "Lux Brands"
|
||||||
|
assert serv.get_characteristic(CHAR_MODEL).value == "Awesome"
|
||||||
|
assert serv.get_characteristic(CHAR_SERIAL_NUMBER).value == "light.accessory"
|
||||||
|
|
||||||
hass.states.async_set(entity_id, "on")
|
hass.states.async_set(entity_id, "on")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
@ -441,9 +477,7 @@ async def test_battery_appears_after_startup(hass, hk_driver, caplog):
|
|||||||
hass.states.async_set(entity_id, None, {})
|
hass.states.async_set(entity_id, None, {})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
acc = HomeAccessory(
|
acc = HomeAccessory(hass, hk_driver, "Accessory without battery", entity_id, 2, {})
|
||||||
hass, hk_driver, "Accessory without battery", entity_id, 2, None
|
|
||||||
)
|
|
||||||
assert acc._char_battery is None
|
assert acc._char_battery is None
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
@ -469,7 +503,7 @@ async def test_call_service(hass, hk_driver, events):
|
|||||||
hass.states.async_set(entity_id, None)
|
hass.states.async_set(entity_id, None)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
acc = HomeAccessory(hass, hk_driver, "Home Accessory", entity_id, 2, None)
|
acc = HomeAccessory(hass, hk_driver, "Home Accessory", entity_id, 2, {})
|
||||||
call_service = async_mock_service(hass, "cover", "open_cover")
|
call_service = async_mock_service(hass, "cover", "open_cover")
|
||||||
|
|
||||||
test_domain = "cover"
|
test_domain = "cover"
|
||||||
|
@ -5,6 +5,7 @@ import homeassistant.components.climate as climate
|
|||||||
import homeassistant.components.cover as cover
|
import homeassistant.components.cover as cover
|
||||||
from homeassistant.components.homekit.accessories import TYPES, get_accessory
|
from homeassistant.components.homekit.accessories import TYPES, get_accessory
|
||||||
from homeassistant.components.homekit.const import (
|
from homeassistant.components.homekit.const import (
|
||||||
|
ATTR_INTERGRATION,
|
||||||
CONF_FEATURE_LIST,
|
CONF_FEATURE_LIST,
|
||||||
FEATURE_ON_OFF,
|
FEATURE_ON_OFF,
|
||||||
TYPE_FAUCET,
|
TYPE_FAUCET,
|
||||||
@ -61,10 +62,12 @@ def test_not_supported_media_player():
|
|||||||
def test_customize_options(config, name):
|
def test_customize_options(config, name):
|
||||||
"""Test with customized options."""
|
"""Test with customized options."""
|
||||||
mock_type = Mock()
|
mock_type = Mock()
|
||||||
|
conf = config.copy()
|
||||||
|
conf[ATTR_INTERGRATION] = "platform_name"
|
||||||
with patch.dict(TYPES, {"Light": mock_type}):
|
with patch.dict(TYPES, {"Light": mock_type}):
|
||||||
entity_state = State("light.demo", "on")
|
entity_state = State("light.demo", "on")
|
||||||
get_accessory(None, None, entity_state, 2, config)
|
get_accessory(None, None, entity_state, 2, conf)
|
||||||
mock_type.assert_called_with(None, None, name, "light.demo", 2, config)
|
mock_type.assert_called_with(None, None, name, "light.demo", 2, conf)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -254,7 +257,7 @@ def test_type_switches(type_name, entity_id, state, attrs, config):
|
|||||||
| vacuum.SUPPORT_RETURN_HOME
|
| vacuum.SUPPORT_RETURN_HOME
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
("Switch", "vacuum.basic_vacuum", "off", {},),
|
("Switch", "vacuum.basic_vacuum", "off", {}),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_type_vacuum(type_name, entity_id, state, attrs):
|
def test_type_vacuum(type_name, entity_id, state, attrs):
|
||||||
|
@ -703,25 +703,28 @@ async def test_homekit_finds_linked_batteries(
|
|||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
device_entry = device_reg.async_get_or_create(
|
device_entry = device_reg.async_get_or_create(
|
||||||
config_entry_id=config_entry.entry_id,
|
config_entry_id=config_entry.entry_id,
|
||||||
|
sw_version="0.16.0",
|
||||||
|
model="Powerwall 2",
|
||||||
|
manufacturer="Tesla",
|
||||||
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||||
)
|
)
|
||||||
|
|
||||||
binary_charging_sensor = entity_reg.async_get_or_create(
|
binary_charging_sensor = entity_reg.async_get_or_create(
|
||||||
"binary_sensor",
|
"binary_sensor",
|
||||||
"light",
|
"powerwall",
|
||||||
"battery_charging",
|
"battery_charging",
|
||||||
device_id=device_entry.id,
|
device_id=device_entry.id,
|
||||||
device_class=DEVICE_CLASS_BATTERY_CHARGING,
|
device_class=DEVICE_CLASS_BATTERY_CHARGING,
|
||||||
)
|
)
|
||||||
battery_sensor = entity_reg.async_get_or_create(
|
battery_sensor = entity_reg.async_get_or_create(
|
||||||
"sensor",
|
"sensor",
|
||||||
"light",
|
"powerwall",
|
||||||
"battery",
|
"battery",
|
||||||
device_id=device_entry.id,
|
device_id=device_entry.id,
|
||||||
device_class=DEVICE_CLASS_BATTERY,
|
device_class=DEVICE_CLASS_BATTERY,
|
||||||
)
|
)
|
||||||
light = entity_reg.async_get_or_create(
|
light = entity_reg.async_get_or_create(
|
||||||
"light", "light", "demo", device_id=device_entry.id
|
"light", "powerwall", "demo", device_id=device_entry.id
|
||||||
)
|
)
|
||||||
|
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
@ -751,8 +754,11 @@ async def test_homekit_finds_linked_batteries(
|
|||||||
ANY,
|
ANY,
|
||||||
ANY,
|
ANY,
|
||||||
{
|
{
|
||||||
"linked_battery_charging_sensor": "binary_sensor.light_battery_charging",
|
"manufacturer": "Tesla",
|
||||||
"linked_battery_sensor": "sensor.light_battery",
|
"model": "Powerwall 2",
|
||||||
|
"sw_version": "0.16.0",
|
||||||
|
"linked_battery_charging_sensor": "binary_sensor.powerwall_battery_charging",
|
||||||
|
"linked_battery_sensor": "sensor.powerwall_battery",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user