Show device details in homekit accessory service info (#35100)

* Show device info in homekit accessory service info

* fix conflict
This commit is contained in:
J. Nick Koston 2020-05-11 09:09:48 -05:00 committed by GitHub
parent 6d9f561853
commit 0a9b373edb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 168 additions and 44 deletions

View File

@ -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."""

View File

@ -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 = {}

View File

@ -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

View File

@ -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,
) )

View File

@ -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,

View File

@ -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"

View File

@ -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):

View File

@ -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",
}, },
) )