mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add scene platform to Qbus integration (#144032)
* Add scene platform * Remove updating last_activated * Simplify device info * Move _attr_name to specific classes * Refactor device info --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
54dce53628
commit
486535c189
@ -57,6 +57,7 @@ async def async_setup_entry(
|
|||||||
class QbusClimate(QbusEntity, ClimateEntity):
|
class QbusClimate(QbusEntity, ClimateEntity):
|
||||||
"""Representation of a Qbus climate entity."""
|
"""Representation of a Qbus climate entity."""
|
||||||
|
|
||||||
|
_attr_name = None
|
||||||
_attr_hvac_modes = [HVACMode.HEAT]
|
_attr_hvac_modes = [HVACMode.HEAT]
|
||||||
_attr_supported_features = (
|
_attr_supported_features = (
|
||||||
ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE
|
ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE
|
||||||
|
@ -8,6 +8,7 @@ DOMAIN: Final = "qbus"
|
|||||||
PLATFORMS: list[Platform] = [
|
PLATFORMS: list[Platform] = [
|
||||||
Platform.CLIMATE,
|
Platform.CLIMATE,
|
||||||
Platform.LIGHT,
|
Platform.LIGHT,
|
||||||
|
Platform.SCENE,
|
||||||
Platform.SWITCH,
|
Platform.SWITCH,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -105,6 +105,7 @@ class QbusControllerCoordinator(DataUpdateCoordinator[list[QbusMqttOutput]]):
|
|||||||
device_registry = dr.async_get(self.hass)
|
device_registry = dr.async_get(self.hass)
|
||||||
device_registry.async_get_or_create(
|
device_registry.async_get_or_create(
|
||||||
config_entry_id=self.config_entry.entry_id,
|
config_entry_id=self.config_entry.entry_id,
|
||||||
|
connections={(dr.CONNECTION_NETWORK_MAC, self._controller.mac)},
|
||||||
identifiers={(DOMAIN, format_mac(self._controller.mac))},
|
identifiers={(DOMAIN, format_mac(self._controller.mac))},
|
||||||
manufacturer=MANUFACTURER,
|
manufacturer=MANUFACTURER,
|
||||||
model="CTD3.x",
|
model="CTD3.x",
|
||||||
|
@ -54,34 +54,39 @@ def format_ref_id(ref_id: str) -> str | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def create_main_device_identifier(mqtt_output: QbusMqttOutput) -> tuple[str, str]:
|
||||||
|
"""Create the identifier referring to the main device this output belongs to."""
|
||||||
|
return (DOMAIN, format_mac(mqtt_output.device.mac))
|
||||||
|
|
||||||
|
|
||||||
class QbusEntity(Entity, ABC):
|
class QbusEntity(Entity, ABC):
|
||||||
"""Representation of a Qbus entity."""
|
"""Representation of a Qbus entity."""
|
||||||
|
|
||||||
_attr_has_entity_name = True
|
_attr_has_entity_name = True
|
||||||
_attr_name = None
|
|
||||||
_attr_should_poll = False
|
_attr_should_poll = False
|
||||||
|
|
||||||
def __init__(self, mqtt_output: QbusMqttOutput) -> None:
|
def __init__(self, mqtt_output: QbusMqttOutput) -> None:
|
||||||
"""Initialize the Qbus entity."""
|
"""Initialize the Qbus entity."""
|
||||||
|
|
||||||
|
self._mqtt_output = mqtt_output
|
||||||
|
|
||||||
self._topic_factory = QbusMqttTopicFactory()
|
self._topic_factory = QbusMqttTopicFactory()
|
||||||
self._message_factory = QbusMqttMessageFactory()
|
self._message_factory = QbusMqttMessageFactory()
|
||||||
|
self._state_topic = self._topic_factory.get_output_state_topic(
|
||||||
|
mqtt_output.device.id, mqtt_output.id
|
||||||
|
)
|
||||||
|
|
||||||
ref_id = format_ref_id(mqtt_output.ref_id)
|
ref_id = format_ref_id(mqtt_output.ref_id)
|
||||||
|
|
||||||
self._attr_unique_id = f"ctd_{mqtt_output.device.serial_number}_{ref_id}"
|
self._attr_unique_id = f"ctd_{mqtt_output.device.serial_number}_{ref_id}"
|
||||||
|
|
||||||
|
# Create linked device
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
name=mqtt_output.name.title(),
|
name=mqtt_output.name.title(),
|
||||||
manufacturer=MANUFACTURER,
|
manufacturer=MANUFACTURER,
|
||||||
identifiers={(DOMAIN, f"{mqtt_output.device.serial_number}_{ref_id}")},
|
identifiers={(DOMAIN, f"{mqtt_output.device.serial_number}_{ref_id}")},
|
||||||
suggested_area=mqtt_output.location.title(),
|
suggested_area=mqtt_output.location.title(),
|
||||||
via_device=(DOMAIN, format_mac(mqtt_output.device.mac)),
|
via_device=create_main_device_identifier(mqtt_output),
|
||||||
)
|
|
||||||
|
|
||||||
self._mqtt_output = mqtt_output
|
|
||||||
self._state_topic = self._topic_factory.get_output_state_topic(
|
|
||||||
mqtt_output.device.id, mqtt_output.id
|
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
|
@ -43,6 +43,7 @@ async def async_setup_entry(
|
|||||||
class QbusLight(QbusEntity, LightEntity):
|
class QbusLight(QbusEntity, LightEntity):
|
||||||
"""Representation of a Qbus light entity."""
|
"""Representation of a Qbus light entity."""
|
||||||
|
|
||||||
|
_attr_name = None
|
||||||
_attr_supported_color_modes = {ColorMode.BRIGHTNESS}
|
_attr_supported_color_modes = {ColorMode.BRIGHTNESS}
|
||||||
_attr_color_mode = ColorMode.BRIGHTNESS
|
_attr_color_mode = ColorMode.BRIGHTNESS
|
||||||
|
|
||||||
|
66
homeassistant/components/qbus/scene.py
Normal file
66
homeassistant/components/qbus/scene.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
"""Support for Qbus scene."""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from qbusmqttapi.discovery import QbusMqttOutput
|
||||||
|
from qbusmqttapi.state import QbusMqttState, StateAction, StateType
|
||||||
|
|
||||||
|
from homeassistant.components.mqtt import ReceiveMessage
|
||||||
|
from homeassistant.components.scene import Scene
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||||
|
|
||||||
|
from .coordinator import QbusConfigEntry
|
||||||
|
from .entity import QbusEntity, add_new_outputs, create_main_device_identifier
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: QbusConfigEntry,
|
||||||
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up scene entities."""
|
||||||
|
|
||||||
|
coordinator = entry.runtime_data
|
||||||
|
added_outputs: list[QbusMqttOutput] = []
|
||||||
|
|
||||||
|
def _check_outputs() -> None:
|
||||||
|
add_new_outputs(
|
||||||
|
coordinator,
|
||||||
|
added_outputs,
|
||||||
|
lambda output: output.type == "scene",
|
||||||
|
QbusScene,
|
||||||
|
async_add_entities,
|
||||||
|
)
|
||||||
|
|
||||||
|
_check_outputs()
|
||||||
|
entry.async_on_unload(coordinator.async_add_listener(_check_outputs))
|
||||||
|
|
||||||
|
|
||||||
|
class QbusScene(QbusEntity, Scene):
|
||||||
|
"""Representation of a Qbus scene entity."""
|
||||||
|
|
||||||
|
def __init__(self, mqtt_output: QbusMqttOutput) -> None:
|
||||||
|
"""Initialize scene entity."""
|
||||||
|
|
||||||
|
super().__init__(mqtt_output)
|
||||||
|
|
||||||
|
# Add to main controller device
|
||||||
|
self._attr_device_info = DeviceInfo(
|
||||||
|
identifiers={create_main_device_identifier(mqtt_output)}
|
||||||
|
)
|
||||||
|
self._attr_name = mqtt_output.name.title()
|
||||||
|
|
||||||
|
async def async_activate(self, **kwargs: Any) -> None:
|
||||||
|
"""Activate scene."""
|
||||||
|
state = QbusMqttState(
|
||||||
|
id=self._mqtt_output.id, type=StateType.ACTION, action=StateAction.ACTIVE
|
||||||
|
)
|
||||||
|
await self._async_publish_output_state(state)
|
||||||
|
|
||||||
|
async def _state_received(self, msg: ReceiveMessage) -> None:
|
||||||
|
# Nothing to do
|
||||||
|
pass
|
@ -42,6 +42,7 @@ async def async_setup_entry(
|
|||||||
class QbusSwitch(QbusEntity, SwitchEntity):
|
class QbusSwitch(QbusEntity, SwitchEntity):
|
||||||
"""Representation of a Qbus switch entity."""
|
"""Representation of a Qbus switch entity."""
|
||||||
|
|
||||||
|
_attr_name = None
|
||||||
_attr_device_class = SwitchDeviceClass.SWITCH
|
_attr_device_class = SwitchDeviceClass.SWITCH
|
||||||
|
|
||||||
def __init__(self, mqtt_output: QbusMqttOutput) -> None:
|
def __init__(self, mqtt_output: QbusMqttOutput) -> None:
|
||||||
|
@ -99,6 +99,19 @@
|
|||||||
"write": true
|
"write": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "UL25",
|
||||||
|
"location": "Living",
|
||||||
|
"locationId": 0,
|
||||||
|
"name": "Watching TV",
|
||||||
|
"originalName": "Watching TV",
|
||||||
|
"refId": "000001/105/3",
|
||||||
|
"type": "scene",
|
||||||
|
"actions": {
|
||||||
|
"active": null
|
||||||
|
},
|
||||||
|
"properties": {}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
45
tests/components/qbus/test_scene.py
Normal file
45
tests/components/qbus/test_scene.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
"""Test Qbus scene entities."""
|
||||||
|
|
||||||
|
from homeassistant.components.scene import DOMAIN as SCENE_DOMAIN, SERVICE_TURN_ON
|
||||||
|
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from tests.common import async_fire_mqtt_message
|
||||||
|
from tests.typing import MqttMockHAClient
|
||||||
|
|
||||||
|
_PAYLOAD_SCENE_STATE = '{"id":"UL25","properties":{"value":true},"type":"state"}'
|
||||||
|
_PAYLOAD_SCENE_ACTIVATE = '{"id": "UL25", "type": "action", "action": "active"}'
|
||||||
|
|
||||||
|
_TOPIC_SCENE_STATE = "cloudapp/QBUSMQTTGW/UL1/UL25/state"
|
||||||
|
_TOPIC_SCENE_SET_STATE = "cloudapp/QBUSMQTTGW/UL1/UL25/setState"
|
||||||
|
|
||||||
|
_SCENE_ENTITY_ID = "scene.ctd_000001_watching_tv"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_scene(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mqtt_mock: MqttMockHAClient,
|
||||||
|
setup_integration: None,
|
||||||
|
) -> None:
|
||||||
|
"""Test scene."""
|
||||||
|
|
||||||
|
assert hass.states.get(_SCENE_ENTITY_ID).state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
# Activate scene
|
||||||
|
mqtt_mock.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
SCENE_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: _SCENE_ENTITY_ID},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
|
_TOPIC_SCENE_SET_STATE, _PAYLOAD_SCENE_ACTIVATE, 0, False
|
||||||
|
)
|
||||||
|
|
||||||
|
# Simulate response
|
||||||
|
async_fire_mqtt_message(hass, _TOPIC_SCENE_STATE, _PAYLOAD_SCENE_STATE)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert hass.states.get(_SCENE_ENTITY_ID).state != STATE_UNKNOWN
|
Loading…
x
Reference in New Issue
Block a user