Skip to content

Commit

Permalink
Add power strip with 2 outlets to kitchen_sink (home-assistant#110346)
Browse files Browse the repository at this point in the history
  • Loading branch information
emontnemery authored Feb 12, 2024
1 parent 3086d24 commit e27e799
Show file tree
Hide file tree
Showing 9 changed files with 625 additions and 29 deletions.
2 changes: 2 additions & 0 deletions homeassistant/components/kitchen_sink/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@


COMPONENTS_WITH_DEMO_PLATFORM = [
Platform.BUTTON,
Platform.IMAGE,
Platform.LAWN_MOWER,
Platform.LOCK,
Platform.SENSOR,
Platform.SWITCH,
Platform.WEATHER,
]

Expand Down
56 changes: 56 additions & 0 deletions homeassistant/components/kitchen_sink/button.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""Demo platform that offers a fake button entity."""
from __future__ import annotations

from homeassistant.components import persistent_notification
from homeassistant.components.button import ButtonEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import DOMAIN


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the demo button platform."""
async_add_entities(
[
DemoButton(
unique_id="2_ch_power_strip",
device_name="2CH Power strip",
entity_name="Restart",
),
]
)


class DemoButton(ButtonEntity):
"""Representation of a demo button entity."""

_attr_has_entity_name = True
_attr_should_poll = False

def __init__(
self,
unique_id: str,
device_name: str,
entity_name: str | None,
) -> None:
"""Initialize the Demo button entity."""
self._attr_unique_id = unique_id
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, unique_id)},
name=device_name,
)
self._attr_name = entity_name

async def async_press(self) -> None:
"""Send out a persistent notification."""
persistent_notification.async_create(
self.hass, "Button pressed", title="Button"
)
self.hass.bus.async_fire("demo_button_pressed")
23 changes: 23 additions & 0 deletions homeassistant/components/kitchen_sink/device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Create device without entities."""

from __future__ import annotations

from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr

from . import DOMAIN


def async_create_device(
hass: HomeAssistant,
config_entry_id: str,
device_name: str | None,
unique_id: str,
) -> dr.DeviceEntry:
"""Create a device."""
device_registry = dr.async_get(hass)
return device_registry.async_get_or_create(
config_entry_id=config_entry_id,
name=device_name,
identifiers={(DOMAIN, unique_id)},
)
87 changes: 64 additions & 23 deletions homeassistant/components/kitchen_sink/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.typing import UNDEFINED, StateType, UndefinedType

from . import DOMAIN
from .device import async_create_device


async def async_setup_entry(
Expand All @@ -22,31 +23,63 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Everything but the Kitchen Sink config entry."""
async_create_device(
hass, config_entry.entry_id, "2CH Power strip", "2_ch_power_strip"
)

async_add_entities(
[
DemoSensor(
"statistics_issue_1",
"Statistics issue 1",
100,
None,
SensorStateClass.MEASUREMENT,
UnitOfPower.WATT, # Not a volume unit
device_unique_id="outlet_1",
unique_id="outlet_1_power",
device_name="Outlet 1",
entity_name=UNDEFINED,
state=50,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
unit_of_measurement=UnitOfPower.WATT,
via_device="2_ch_power_strip",
),
DemoSensor(
device_unique_id="outlet_2",
unique_id="outlet_2_power",
device_name="Outlet 2",
entity_name=UNDEFINED,
state=1500,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
unit_of_measurement=UnitOfPower.WATT,
via_device="2_ch_power_strip",
),
DemoSensor(
device_unique_id="statistics_issues",
unique_id="statistics_issue_1",
device_name="Statistics issues",
entity_name="Issue 1",
state=100,
device_class=None,
state_class=SensorStateClass.MEASUREMENT,
unit_of_measurement=UnitOfPower.WATT,
),
DemoSensor(
"statistics_issue_2",
"Statistics issue 2",
100,
None,
SensorStateClass.MEASUREMENT,
"dogs", # Can't be converted to cats
device_unique_id="statistics_issues",
unique_id="statistics_issue_2",
device_name="Statistics issues",
entity_name="Issue 2",
state=100,
device_class=None,
state_class=SensorStateClass.MEASUREMENT,
unit_of_measurement="dogs",
),
DemoSensor(
"statistics_issue_3",
"Statistics issue 3",
100,
None,
None, # Wrong state class
UnitOfPower.WATT,
device_unique_id="statistics_issues",
unique_id="statistics_issue_3",
device_name="Statistics issues",
entity_name="Issue 3",
state=100,
device_class=None,
state_class=None,
unit_of_measurement=UnitOfPower.WATT,
),
]
)
Expand All @@ -55,26 +88,34 @@ async def async_setup_entry(
class DemoSensor(SensorEntity):
"""Representation of a Demo sensor."""

_attr_has_entity_name = True
_attr_should_poll = False

def __init__(
self,
*,
device_unique_id: str,
unique_id: str,
name: str,
device_name: str,
entity_name: str | None | UndefinedType,
state: StateType,
device_class: SensorDeviceClass | None,
state_class: SensorStateClass | None,
unit_of_measurement: str | None,
via_device: str | None = None,
) -> None:
"""Initialize the sensor."""
self._attr_device_class = device_class
self._attr_name = name
if entity_name is not UNDEFINED:
self._attr_name = entity_name
self._attr_native_unit_of_measurement = unit_of_measurement
self._attr_native_value = state
self._attr_state_class = state_class
self._attr_unique_id = unique_id

self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, unique_id)},
name=name,
identifiers={(DOMAIN, device_unique_id)},
name=device_name,
)
if via_device:
self._attr_device_info["via_device"] = (DOMAIN, via_device)
88 changes: 88 additions & 0 deletions homeassistant/components/kitchen_sink/switch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""Demo platform that has some fake switches."""
from __future__ import annotations

from typing import Any

from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import DOMAIN
from .device import async_create_device


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the demo switch platform."""
async_create_device(
hass, config_entry.entry_id, "2CH Power strip", "2_ch_power_strip"
)

async_add_entities(
[
DemoSwitch(
unique_id="outlet_1",
device_name="Outlet 1",
entity_name=None,
state=False,
assumed=False,
via_device="2_ch_power_strip",
),
DemoSwitch(
unique_id="outlet_2",
device_name="Outlet 2",
entity_name=None,
state=True,
assumed=False,
via_device="2_ch_power_strip",
),
]
)


class DemoSwitch(SwitchEntity):
"""Representation of a demo switch."""

_attr_has_entity_name = True
_attr_should_poll = False

def __init__(
self,
*,
unique_id: str,
device_name: str,
entity_name: str | None,
state: bool,
assumed: bool,
translation_key: str | None = None,
device_class: SwitchDeviceClass | None = None,
via_device: str | None = None,
) -> None:
"""Initialize the Demo switch."""
self._attr_assumed_state = assumed
self._attr_device_class = device_class
self._attr_translation_key = translation_key
self._attr_is_on = state
self._attr_unique_id = unique_id
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, unique_id)},
name=device_name,
)
if via_device:
self._attr_device_info["via_device"] = (DOMAIN, via_device)
self._attr_name = entity_name

def turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
self._attr_is_on = True
self.schedule_update_ha_state()

def turn_off(self, **kwargs: Any) -> None:
"""Turn the device off."""
self._attr_is_on = False
self.schedule_update_ha_state()
38 changes: 32 additions & 6 deletions tests/components/kitchen_sink/snapshots/test_sensor.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,61 @@
set({
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Statistics issue 1',
'device_class': 'power',
'friendly_name': 'Outlet 1 Power',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
}),
'context': <ANY>,
'entity_id': 'sensor.statistics_issue_1',
'entity_id': 'sensor.outlet_1_power',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '50',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'power',
'friendly_name': 'Outlet 2 Power',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
}),
'context': <ANY>,
'entity_id': 'sensor.outlet_2_power',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '1500',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Statistics issues Issue 1',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
}),
'context': <ANY>,
'entity_id': 'sensor.statistics_issues_issue_1',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '100',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Statistics issue 2',
'friendly_name': 'Statistics issues Issue 2',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'dogs',
}),
'context': <ANY>,
'entity_id': 'sensor.statistics_issue_2',
'entity_id': 'sensor.statistics_issues_issue_2',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '100',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Statistics issue 3',
'friendly_name': 'Statistics issues Issue 3',
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
}),
'context': <ANY>,
'entity_id': 'sensor.statistics_issue_3',
'entity_id': 'sensor.statistics_issues_issue_3',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '100',
Expand Down
Loading

0 comments on commit e27e799

Please sign in to comment.