From 878d41a472b4bce7ddf1d567d1f1add2ac21aa18 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 27 Jun 2023 07:43:19 -0500 Subject: [PATCH] Remove senseme integration (#94363) --- .coveragerc | 6 - .strict-typing | 1 - CODEOWNERS | 2 - homeassistant/components/senseme/__init__.py | 36 -- .../components/senseme/binary_sensor.py | 41 -- .../components/senseme/config_flow.py | 148 ------- homeassistant/components/senseme/const.py | 29 -- homeassistant/components/senseme/discovery.py | 64 ---- homeassistant/components/senseme/entity.py | 47 --- homeassistant/components/senseme/fan.py | 113 ------ homeassistant/components/senseme/light.py | 110 ------ .../components/senseme/manifest.json | 18 - homeassistant/components/senseme/select.py | 90 ----- homeassistant/components/senseme/strings.json | 30 -- homeassistant/components/senseme/switch.py | 138 ------- homeassistant/generated/config_flows.py | 1 - homeassistant/generated/dhcp.py | 8 - homeassistant/generated/integrations.json | 6 - mypy.ini | 10 - requirements_all.txt | 3 - requirements_test_all.txt | 3 - tests/components/senseme/__init__.py | 142 ------- tests/components/senseme/test_config_flow.py | 361 ------------------ tests/components/senseme/test_light.py | 115 ------ 24 files changed, 1522 deletions(-) delete mode 100644 homeassistant/components/senseme/__init__.py delete mode 100644 homeassistant/components/senseme/binary_sensor.py delete mode 100644 homeassistant/components/senseme/config_flow.py delete mode 100644 homeassistant/components/senseme/const.py delete mode 100644 homeassistant/components/senseme/discovery.py delete mode 100644 homeassistant/components/senseme/entity.py delete mode 100644 homeassistant/components/senseme/fan.py delete mode 100644 homeassistant/components/senseme/light.py delete mode 100644 homeassistant/components/senseme/manifest.json delete mode 100644 homeassistant/components/senseme/select.py delete mode 100644 homeassistant/components/senseme/strings.json delete mode 100644 homeassistant/components/senseme/switch.py delete mode 100644 tests/components/senseme/__init__.py delete mode 100644 tests/components/senseme/test_config_flow.py delete mode 100644 tests/components/senseme/test_light.py diff --git a/.coveragerc b/.coveragerc index 4cecd5af0fc..c75737b530f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1046,12 +1046,6 @@ omit = homeassistant/components/sense/__init__.py homeassistant/components/sense/binary_sensor.py homeassistant/components/sense/sensor.py - homeassistant/components/senseme/__init__.py - homeassistant/components/senseme/discovery.py - homeassistant/components/senseme/entity.py - homeassistant/components/senseme/fan.py - homeassistant/components/senseme/light.py - homeassistant/components/senseme/switch.py homeassistant/components/senz/__init__.py homeassistant/components/senz/api.py homeassistant/components/senz/climate.py diff --git a/.strict-typing b/.strict-typing index 465892430c1..67ebca7aea7 100644 --- a/.strict-typing +++ b/.strict-typing @@ -277,7 +277,6 @@ homeassistant.components.scene.* homeassistant.components.schedule.* homeassistant.components.scrape.* homeassistant.components.select.* -homeassistant.components.senseme.* homeassistant.components.sensibo.* homeassistant.components.sensirion_ble.* homeassistant.components.sensor.* diff --git a/CODEOWNERS b/CODEOWNERS index c82f249a906..38929301f87 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1079,8 +1079,6 @@ build.json @home-assistant/supervisor /tests/components/select/ @home-assistant/core /homeassistant/components/sense/ @kbickar /tests/components/sense/ @kbickar -/homeassistant/components/senseme/ @mikelawrence @bdraco -/tests/components/senseme/ @mikelawrence @bdraco /homeassistant/components/sensibo/ @andrey-git @gjohansson-ST /tests/components/sensibo/ @andrey-git @gjohansson-ST /homeassistant/components/sensirion_ble/ @akx diff --git a/homeassistant/components/senseme/__init__.py b/homeassistant/components/senseme/__init__.py deleted file mode 100644 index a744dd0d0b8..00000000000 --- a/homeassistant/components/senseme/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -"""The SenseME integration.""" -from __future__ import annotations - -from aiosenseme import async_get_device_by_device_info - -from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryNotReady - -from .const import CONF_INFO, DOMAIN, PLATFORMS, UPDATE_RATE -from .discovery import async_start_discovery - - -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: - """Set up SenseME from a config entry.""" - async_start_discovery(hass) - - status, device = await async_get_device_by_device_info( - info=entry.data[CONF_INFO], start_first=True, refresh_minutes=UPDATE_RATE - ) - if not status: - device.stop() - raise ConfigEntryNotReady(f"Connect to address {device.address} failed") - - await device.async_update(not status) - - hass.data[DOMAIN][entry.entry_id] = device - await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - - return True - - -async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: - """Unload a config entry.""" - hass.data[DOMAIN][entry.entry_id].stop() - return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/senseme/binary_sensor.py b/homeassistant/components/senseme/binary_sensor.py deleted file mode 100644 index d6c9e193ece..00000000000 --- a/homeassistant/components/senseme/binary_sensor.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Support for Big Ass Fans SenseME occupancy sensor.""" -from __future__ import annotations - -from aiosenseme import SensemeDevice - -from homeassistant import config_entries -from homeassistant.components.binary_sensor import ( - BinarySensorDeviceClass, - BinarySensorEntity, -) -from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.entity_platform import AddEntitiesCallback - -from .const import DOMAIN -from .entity import SensemeEntity - - -async def async_setup_entry( - hass: HomeAssistant, - entry: config_entries.ConfigEntry, - async_add_entities: AddEntitiesCallback, -) -> None: - """Set up SenseME occupancy sensors.""" - device = hass.data[DOMAIN][entry.entry_id] - if device.has_sensor: - async_add_entities([HASensemeOccupancySensor(device)]) - - -class HASensemeOccupancySensor(SensemeEntity, BinarySensorEntity): - """Representation of a Big Ass Fans SenseME occupancy sensor.""" - - def __init__(self, device: SensemeDevice) -> None: - """Initialize the entity.""" - super().__init__(device, f"{device.name} Occupancy") - self._attr_unique_id = f"{self._device.uuid}-SENSOR" - self._attr_device_class = BinarySensorDeviceClass.OCCUPANCY - - @callback - def _async_update_attrs(self) -> None: - """Update attrs from device.""" - self._attr_is_on = self._device.motion_detected diff --git a/homeassistant/components/senseme/config_flow.py b/homeassistant/components/senseme/config_flow.py deleted file mode 100644 index 6e2f10c1b36..00000000000 --- a/homeassistant/components/senseme/config_flow.py +++ /dev/null @@ -1,148 +0,0 @@ -"""Config flow for SenseME.""" -from __future__ import annotations - -import ipaddress -from typing import Any - -from aiosenseme import SensemeDevice, async_get_device_by_ip_address -import voluptuous as vol - -from homeassistant import config_entries -from homeassistant.components import dhcp -from homeassistant.const import CONF_DEVICE, CONF_HOST, CONF_ID -from homeassistant.data_entry_flow import FlowResult -from homeassistant.helpers.typing import DiscoveryInfoType - -from .const import CONF_HOST_MANUAL, CONF_INFO, DOMAIN -from .discovery import async_discover, async_get_discovered_device - -DISCOVER_TIMEOUT = 5 - - -class SensemeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): - """Handle SenseME discovery config flow.""" - - VERSION = 1 - - def __init__(self) -> None: - """Initialize the SenseME config flow.""" - self._discovered_devices: list[SensemeDevice] | None = None - self._discovered_device: SensemeDevice | None = None - - async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult: - """Handle discovery via dhcp.""" - # If discovery is already running, it takes precedence since its more efficient - if self._async_current_entries(): - return self.async_abort(reason="already_configured") - if device := await async_get_device_by_ip_address(discovery_info.ip): - device.stop() - if device is None or not device.uuid: - return self.async_abort(reason="cannot_connect") - await self.async_set_unique_id(device.uuid) - self._discovered_device = device - return await self.async_step_discovery_confirm() - - async def async_step_integration_discovery( - self, discovery_info: DiscoveryInfoType - ) -> FlowResult: - """Handle integration discovery.""" - uuid = discovery_info[CONF_ID] - device = async_get_discovered_device(self.hass, discovery_info[CONF_ID]) - host = device.address - await self.async_set_unique_id(uuid) - for entry in self._async_current_entries(include_ignore=False): - if entry.data[CONF_INFO]["address"] == host: - return self.async_abort(reason="already_configured") - if entry.unique_id != uuid: - continue - self.hass.config_entries.async_update_entry( - entry, data={CONF_INFO: {**entry.data[CONF_INFO], "address": host}} - ) - return self.async_abort(reason="already_configured") - self._discovered_device = device - return await self.async_step_discovery_confirm() - - async def async_step_discovery_confirm( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Confirm discovery.""" - device = self._discovered_device - assert device is not None - - if user_input is not None: - return await self._async_entry_for_device(device) - placeholders = { - "name": device.name, - "model": device.model, - "host": device.address, - } - self.context["title_placeholders"] = placeholders - return self.async_show_form( - step_id="discovery_confirm", description_placeholders=placeholders - ) - - async def _async_entry_for_device(self, device: SensemeDevice) -> FlowResult: - """Create a config entry for a device.""" - await self.async_set_unique_id(device.uuid, raise_on_progress=False) - self._abort_if_unique_id_configured() - return self.async_create_entry( - title=device.name, - data={CONF_INFO: device.get_device_info}, - ) - - async def async_step_manual( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Handle manual entry of an ip address.""" - errors = {} - if user_input is not None: - host = user_input[CONF_HOST] - try: - ipaddress.ip_address(host) - except ValueError: - errors[CONF_HOST] = "invalid_host" - else: - if device := await async_get_device_by_ip_address(host): - device.stop() - return await self._async_entry_for_device(device) - - errors[CONF_HOST] = "cannot_connect" - - return self.async_show_form( - step_id="manual", - data_schema=vol.Schema({vol.Required(CONF_HOST): str}), - errors=errors, - ) - - async def async_step_user( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Handle a flow initialized by the user.""" - if self._discovered_devices is None: - self._discovered_devices = await async_discover(self.hass, DISCOVER_TIMEOUT) - current_ids = self._async_current_ids() - device_selection = { - device.uuid: device.name - for device in self._discovered_devices - if device.uuid not in current_ids - } - - if not device_selection: - return await self.async_step_manual(user_input=None) - - device_selection[None] = CONF_HOST_MANUAL - - if user_input is not None: - if user_input[CONF_DEVICE] is None: - return await self.async_step_manual() - - for device in self._discovered_devices: - if device.uuid == user_input[CONF_DEVICE]: - return await self._async_entry_for_device(device) - - return self.async_show_form( - step_id="user", - data_schema=vol.Schema( - {vol.Required(CONF_DEVICE): vol.In(device_selection)} - ), - ) diff --git a/homeassistant/components/senseme/const.py b/homeassistant/components/senseme/const.py deleted file mode 100644 index 5660fc1d177..00000000000 --- a/homeassistant/components/senseme/const.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Constants for the SenseME integration.""" - - -from homeassistant.const import Platform - -DOMAIN = "senseme" - -# Periodic fan update rate in minutes -UPDATE_RATE = 1 - -# data storage -CONF_INFO = "info" -CONF_HOST_MANUAL = "IP Address" -DISCOVERY = "discovery" - -# Fan Preset Modes -PRESET_MODE_WHOOSH = "Whoosh" - -# Fan Directions -SENSEME_DIRECTION_FORWARD = "FWD" -SENSEME_DIRECTION_REVERSE = "REV" - -PLATFORMS = [ - Platform.BINARY_SENSOR, - Platform.FAN, - Platform.LIGHT, - Platform.SELECT, - Platform.SWITCH, -] diff --git a/homeassistant/components/senseme/discovery.py b/homeassistant/components/senseme/discovery.py deleted file mode 100644 index d3924ef16c0..00000000000 --- a/homeassistant/components/senseme/discovery.py +++ /dev/null @@ -1,64 +0,0 @@ -"""The SenseME integration discovery.""" -from __future__ import annotations - -import asyncio - -from aiosenseme import SensemeDevice, SensemeDiscovery - -from homeassistant import config_entries -from homeassistant.const import CONF_ID -from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import discovery_flow - -from .const import DISCOVERY, DOMAIN - - -@callback -def async_start_discovery(hass: HomeAssistant) -> bool: - """Start discovery if its not already running.""" - domain_data = hass.data.setdefault(DOMAIN, {}) - if DISCOVERY in domain_data: - return False # already running - discovery = domain_data[DISCOVERY] = SensemeDiscovery(False) - discovery.add_callback(lambda devices: async_trigger_discovery(hass, devices)) - discovery.start() - return True # started - - -@callback -def async_get_discovered_device(hass: HomeAssistant, uuid: str) -> SensemeDevice: - """Return a discovered device.""" - discovery: SensemeDiscovery = hass.data[DOMAIN][DISCOVERY] - devices: list[SensemeDevice] = discovery.devices - for discovered_device in devices: - if discovered_device.uuid == uuid: - return discovered_device - raise RuntimeError("Discovered device unexpectedly disappeared") - - -async def async_discover(hass: HomeAssistant, timeout: float) -> list[SensemeDevice]: - """Discover devices or restart it if its already running.""" - started = async_start_discovery(hass) - discovery: SensemeDiscovery = hass.data[DOMAIN][DISCOVERY] - if not started: # already running - discovery.stop() - discovery.start() - await asyncio.sleep(timeout) - devices: list[SensemeDevice] = discovery.devices - return devices - - -@callback -def async_trigger_discovery( - hass: HomeAssistant, - discovered_devices: list[SensemeDevice], -) -> None: - """Trigger config flows for discovered devices.""" - for device in discovered_devices: - if device.uuid: - discovery_flow.async_create_flow( - hass, - DOMAIN, - context={"source": config_entries.SOURCE_INTEGRATION_DISCOVERY}, - data={CONF_ID: device.uuid}, - ) diff --git a/homeassistant/components/senseme/entity.py b/homeassistant/components/senseme/entity.py deleted file mode 100644 index 9e0ca412628..00000000000 --- a/homeassistant/components/senseme/entity.py +++ /dev/null @@ -1,47 +0,0 @@ -"""The SenseME integration entities.""" -from __future__ import annotations - -from aiosenseme import SensemeDevice - -from homeassistant.core import callback -from homeassistant.helpers import device_registry as dr -from homeassistant.helpers.entity import DeviceInfo, Entity - - -class SensemeEntity(Entity): - """Base class for senseme entities.""" - - _attr_should_poll = False - - def __init__(self, device: SensemeDevice, name: str) -> None: - """Initialize the entity.""" - self._device = device - self._attr_name = name - self._attr_device_info = DeviceInfo( - connections={(dr.CONNECTION_NETWORK_MAC, self._device.mac)}, - name=self._device.name, - manufacturer="Big Ass Fans", - model=self._device.model, - sw_version=self._device.fw_version, - suggested_area=self._device.room_name, - ) - self._async_update_attrs() - - @callback - def _async_update_attrs(self) -> None: - """Update attrs from device.""" - self._attr_available = self._device.available - - @callback - def _async_update_from_device(self) -> None: - """Process an update from the device.""" - self._async_update_attrs() - self.async_write_ha_state() - - async def async_added_to_hass(self) -> None: - """Add data updated listener after this object has been initialized.""" - self._device.add_callback(self._async_update_from_device) - - async def async_will_remove_from_hass(self) -> None: - """Remove data updated listener after this object has been initialized.""" - self._device.remove_callback(self._async_update_from_device) diff --git a/homeassistant/components/senseme/fan.py b/homeassistant/components/senseme/fan.py deleted file mode 100644 index 63c64a11632..00000000000 --- a/homeassistant/components/senseme/fan.py +++ /dev/null @@ -1,113 +0,0 @@ -"""Support for Big Ass Fans SenseME fan.""" -from __future__ import annotations - -import math -from typing import Any - -from aiosenseme import SensemeFan - -from homeassistant import config_entries -from homeassistant.components.fan import ( - DIRECTION_FORWARD, - DIRECTION_REVERSE, - FanEntity, - FanEntityFeature, -) -from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.util.percentage import ( - percentage_to_ranged_value, - ranged_value_to_percentage, -) - -from .const import ( - DOMAIN, - PRESET_MODE_WHOOSH, - SENSEME_DIRECTION_FORWARD, - SENSEME_DIRECTION_REVERSE, -) -from .entity import SensemeEntity - -SENSEME_DIRECTION_TO_HASS = { - SENSEME_DIRECTION_FORWARD: DIRECTION_FORWARD, - SENSEME_DIRECTION_REVERSE: DIRECTION_REVERSE, -} -HASS_DIRECTION_TO_SENSEME = {v: k for k, v in SENSEME_DIRECTION_TO_HASS.items()} - - -async def async_setup_entry( - hass: HomeAssistant, - entry: config_entries.ConfigEntry, - async_add_entities: AddEntitiesCallback, -) -> None: - """Set up SenseME fans.""" - device = hass.data[DOMAIN][entry.entry_id] - if device.is_fan: - async_add_entities([HASensemeFan(device)]) - - -class HASensemeFan(SensemeEntity, FanEntity): - """SenseME ceiling fan component.""" - - _attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.DIRECTION - _attr_preset_modes = [PRESET_MODE_WHOOSH] - - def __init__(self, device: SensemeFan) -> None: - """Initialize the entity.""" - super().__init__(device, device.name) - self._attr_speed_count = self._device.fan_speed_max - self._attr_unique_id = f"{self._device.uuid}-FAN" # for legacy compat - - @callback - def _async_update_attrs(self) -> None: - """Update attrs from device.""" - self._attr_is_on = self._device.fan_on - self._attr_current_direction = SENSEME_DIRECTION_TO_HASS.get( - self._device.fan_dir, DIRECTION_FORWARD # None also means forward - ) - if self._device.fan_speed is not None: - self._attr_percentage = ranged_value_to_percentage( - self._device.fan_speed_limits, self._device.fan_speed - ) - else: - self._attr_percentage = None - whoosh = self._device.fan_whoosh_mode - self._attr_preset_mode = PRESET_MODE_WHOOSH if whoosh else None - super()._async_update_attrs() - - async def async_set_percentage(self, percentage: int) -> None: - """Set the speed of the fan, as a percentage.""" - self._device.fan_speed = math.ceil( - percentage_to_ranged_value(self._device.fan_speed_limits, percentage) - ) - - async def async_turn_on( - self, - percentage: int | None = None, - preset_mode: str | None = None, - **kwargs: Any, - ) -> None: - """Turn the fan on with a percentage or preset mode.""" - if preset_mode is not None: - await self.async_set_preset_mode(preset_mode) - elif percentage is None: - self._device.fan_on = True - else: - await self.async_set_percentage(percentage) - - async def async_turn_off(self, **kwargs: Any) -> None: - """Turn the fan off.""" - self._device.fan_on = False - - async def async_set_preset_mode(self, preset_mode: str) -> None: - """Set the preset mode of the fan.""" - if preset_mode != PRESET_MODE_WHOOSH: - raise ValueError(f"Invalid preset mode: {preset_mode}") - # Sleep mode must be off for Whoosh to work. - if self._device.sleep_mode: - self._device.sleep_mode = False - self._device.fan_whoosh_mode = True - - async def async_set_direction(self, direction: str) -> None: - """Set the direction of the fan.""" - self._device.fan_dir = HASS_DIRECTION_TO_SENSEME[direction] diff --git a/homeassistant/components/senseme/light.py b/homeassistant/components/senseme/light.py deleted file mode 100644 index f1150260f38..00000000000 --- a/homeassistant/components/senseme/light.py +++ /dev/null @@ -1,110 +0,0 @@ -"""Support for Big Ass Fans SenseME light.""" -from __future__ import annotations - -from typing import Any - -from aiosenseme import SensemeDevice - -from homeassistant import config_entries -from homeassistant.components.light import ( - ATTR_BRIGHTNESS, - ATTR_COLOR_TEMP, - ColorMode, - LightEntity, -) -from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.util.color import ( - color_temperature_kelvin_to_mired, - color_temperature_mired_to_kelvin, -) - -from .const import DOMAIN -from .entity import SensemeEntity - - -async def async_setup_entry( - hass: HomeAssistant, - entry: config_entries.ConfigEntry, - async_add_entities: AddEntitiesCallback, -) -> None: - """Set up SenseME lights.""" - device = hass.data[DOMAIN][entry.entry_id] - if not device.has_light: - return - if device.is_light: - async_add_entities([HASensemeStandaloneLight(device)]) - else: - async_add_entities([HASensemeFanLight(device)]) - - -class HASensemeLight(SensemeEntity, LightEntity): - """Representation of a Big Ass Fans SenseME light.""" - - def __init__(self, device: SensemeDevice, name: str) -> None: - """Initialize the entity.""" - super().__init__(device, name) - self._attr_unique_id = f"{device.uuid}-LIGHT" # for legacy compat - - @callback - def _async_update_attrs(self) -> None: - """Update attrs from device.""" - self._attr_is_on = self._device.light_on - if self._device.light_brightness is not None: - self._attr_brightness = int(min(255, self._device.light_brightness * 16)) - - async def async_turn_on(self, **kwargs: Any) -> None: - """Turn on the light.""" - if (brightness := kwargs.get(ATTR_BRIGHTNESS)) is not None: - # set the brightness, which will also turn on/off light - if brightness == 255: - brightness = 256 # this will end up as 16 which is max - self._device.light_brightness = int(brightness / 16) - else: - self._device.light_on = True - - async def async_turn_off(self, **kwargs: Any) -> None: - """Turn off the light.""" - self._device.light_on = False - - -class HASensemeFanLight(HASensemeLight): - """Representation of a Big Ass Fans SenseME light on a fan.""" - - def __init__(self, device: SensemeDevice) -> None: - """Init a fan light.""" - super().__init__(device, device.name) - self._attr_supported_color_modes = {ColorMode.BRIGHTNESS} - self._attr_color_mode = ColorMode.BRIGHTNESS - - -class HASensemeStandaloneLight(HASensemeLight): - """Representation of a Big Ass Fans SenseME light.""" - - def __init__(self, device: SensemeDevice) -> None: - """Init a standalone light.""" - super().__init__(device, f"{device.name} Light") - self._attr_supported_color_modes = {ColorMode.COLOR_TEMP} - self._attr_color_mode = ColorMode.COLOR_TEMP - self._attr_min_mireds = color_temperature_kelvin_to_mired( - device.light_color_temp_max - ) - self._attr_max_mireds = color_temperature_kelvin_to_mired( - device.light_color_temp_min - ) - - @callback - def _async_update_attrs(self) -> None: - """Update attrs from device.""" - super()._async_update_attrs() - self._attr_color_temp = color_temperature_kelvin_to_mired( - self._device.light_color_temp - ) - - async def async_turn_on(self, **kwargs: Any) -> None: - """Turn on the light.""" - if (color_temp := kwargs.get(ATTR_COLOR_TEMP)) is not None: - self._device.light_color_temp = color_temperature_mired_to_kelvin( - color_temp - ) - await super().async_turn_on(**kwargs) diff --git a/homeassistant/components/senseme/manifest.json b/homeassistant/components/senseme/manifest.json deleted file mode 100644 index b5a36609c6a..00000000000 --- a/homeassistant/components/senseme/manifest.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "domain": "senseme", - "name": "SenseME", - "codeowners": ["@mikelawrence", "@bdraco"], - "config_flow": true, - "dhcp": [ - { - "registered_devices": true - }, - { - "macaddress": "20F85E*" - } - ], - "documentation": "https://www.home-assistant.io/integrations/senseme", - "iot_class": "local_push", - "loggers": ["aiosenseme"], - "requirements": ["aiosenseme==0.6.1"] -} diff --git a/homeassistant/components/senseme/select.py b/homeassistant/components/senseme/select.py deleted file mode 100644 index 251e6c385d8..00000000000 --- a/homeassistant/components/senseme/select.py +++ /dev/null @@ -1,90 +0,0 @@ -"""Support for Big Ass Fans SenseME selects.""" -from __future__ import annotations - -from collections.abc import Callable -from dataclasses import dataclass - -from aiosenseme import SensemeFan -from aiosenseme.device import SensemeDevice - -from homeassistant import config_entries -from homeassistant.components.select import SelectEntity, SelectEntityDescription -from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity_platform import AddEntitiesCallback - -from .const import DOMAIN -from .entity import SensemeEntity - -SMART_MODE_TO_HASS = { - "OFF": "Off", - "COOLING": "Cooling", - "HEATING": "Heating", - "FOLLOWTSTAT": "Follow Thermostat", -} -HASS_TO_SMART_MODE = {v: k for k, v in SMART_MODE_TO_HASS.items()} - - -@dataclass -class SenseMESelectEntityDescriptionMixin: - """Mixin for required keys.""" - - value_fn: Callable[[SensemeFan], str] - set_fn: Callable[[SensemeFan, str], None] - - -@dataclass -class SenseMESelectEntityDescription( - SelectEntityDescription, SenseMESelectEntityDescriptionMixin -): - """Describes SenseME select entity.""" - - -def _set_smart_mode(device: SensemeDevice, value: str) -> None: - device.fan_smartmode = HASS_TO_SMART_MODE[value] - - -FAN_SELECTS = [ - SenseMESelectEntityDescription( - key="smart_mode", - name="Smart Mode", - value_fn=lambda device: SMART_MODE_TO_HASS[device.fan_smartmode], - set_fn=_set_smart_mode, - options=list(SMART_MODE_TO_HASS.values()), - ), -] - - -async def async_setup_entry( - hass: HomeAssistant, - entry: config_entries.ConfigEntry, - async_add_entities: AddEntitiesCallback, -) -> None: - """Set up SenseME fan selects.""" - device = hass.data[DOMAIN][entry.entry_id] - if device.is_fan: - async_add_entities( - HASensemeSelect(device, description) for description in FAN_SELECTS - ) - - -class HASensemeSelect(SensemeEntity, SelectEntity): - """SenseME select component.""" - - entity_description: SenseMESelectEntityDescription - - def __init__( - self, device: SensemeFan, description: SenseMESelectEntityDescription - ) -> None: - """Initialize the entity.""" - self.entity_description = description - super().__init__(device, f"{device.name} {description.name}") - self._attr_unique_id = f"{self._device.uuid}-{description.key}" - - @property - def current_option(self) -> str: - """Return the current value.""" - return self.entity_description.value_fn(self._device) - - async def async_select_option(self, option: str) -> None: - """Set the option.""" - self.entity_description.set_fn(self._device, option) diff --git a/homeassistant/components/senseme/strings.json b/homeassistant/components/senseme/strings.json deleted file mode 100644 index 2de35bc7fed..00000000000 --- a/homeassistant/components/senseme/strings.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "config": { - "flow_title": "{name} - {model} ({host})", - "step": { - "user": { - "description": "Select a device, or choose 'IP Address' to manually enter an IP Address.", - "data": { - "device": "Device" - } - }, - "discovery_confirm": { - "description": "Do you want to set up {name} - {model} ({host})?" - }, - "manual": { - "description": "Enter an IP Address.", - "data": { - "host": "[%key:common::config_flow::data::host%]" - } - } - }, - "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", - "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" - }, - "error": { - "invalid_host": "[%key:common::config_flow::error::invalid_host%]", - "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" - } - } -} diff --git a/homeassistant/components/senseme/switch.py b/homeassistant/components/senseme/switch.py deleted file mode 100644 index 8ffc9075248..00000000000 --- a/homeassistant/components/senseme/switch.py +++ /dev/null @@ -1,138 +0,0 @@ -"""Support for Big Ass Fans SenseME switch.""" -from __future__ import annotations - -from collections.abc import Callable -from dataclasses import dataclass -from typing import Any, cast - -from aiosenseme import SensemeFan -from aiosenseme.device import SensemeDevice - -from homeassistant import config_entries -from homeassistant.components.switch import ( - SwitchDeviceClass, - SwitchEntity, - SwitchEntityDescription, -) -from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.entity_platform import AddEntitiesCallback - -from .const import DOMAIN -from .entity import SensemeEntity - - -@dataclass -class SenseMESwitchEntityDescriptionMixin: - """Mixin for required keys.""" - - value_fn: Callable[[SensemeFan], bool] - set_fn: Callable[[SensemeFan, bool], None] - - -@dataclass -class SenseMESwitchEntityDescription( - SwitchEntityDescription, SenseMESwitchEntityDescriptionMixin -): - """Describes SenseME switch entity.""" - - -def _set_sleep_mode(device: SensemeDevice, value: bool) -> None: - device.sleep_mode = value - - -def _set_motion_fan_auto(device: SensemeDevice, value: bool) -> None: - device.motion_fan_auto = value - - -def _set_motion_light_auto(device: SensemeDevice, value: bool) -> None: - device.motion_light_auto = value - - -FAN_SWITCHES = [ - # Turning on sleep mode will disable Whoosh - SenseMESwitchEntityDescription( - key="sleep_mode", - name="Sleep Mode", - value_fn=lambda device: cast(bool, device.sleep_mode), - set_fn=_set_sleep_mode, - ), - SenseMESwitchEntityDescription( - key="motion_fan_auto", - name="Motion", - value_fn=lambda device: cast(bool, device.motion_fan_auto), - set_fn=_set_motion_fan_auto, - ), -] - -FAN_LIGHT_SWITCHES = [ - SenseMESwitchEntityDescription( - key="motion_light_auto", - name="Light Motion", - value_fn=lambda device: cast(bool, device.motion_light_auto), - set_fn=_set_motion_light_auto, - ), -] - -LIGHT_SWITCHES = [ - SenseMESwitchEntityDescription( - key="sleep_mode", - name="Sleep Mode", - value_fn=lambda device: cast(bool, device.sleep_mode), - set_fn=_set_sleep_mode, - ), - SenseMESwitchEntityDescription( - key="motion_light_auto", - name="Motion", - value_fn=lambda device: cast(bool, device.motion_light_auto), - set_fn=_set_motion_light_auto, - ), -] - - -async def async_setup_entry( - hass: HomeAssistant, - entry: config_entries.ConfigEntry, - async_add_entities: AddEntitiesCallback, -) -> None: - """Set up SenseME fans.""" - device = hass.data[DOMAIN][entry.entry_id] - descriptions: list[SenseMESwitchEntityDescription] = [] - - if device.is_fan: - descriptions.extend(FAN_SWITCHES) - if device.has_light: - descriptions.extend(FAN_LIGHT_SWITCHES) - elif device.is_light: - descriptions.extend(LIGHT_SWITCHES) - - async_add_entities( - HASensemeSwitch(device, description) for description in descriptions - ) - - -class HASensemeSwitch(SensemeEntity, SwitchEntity): - """SenseME switch component.""" - - entity_description: SenseMESwitchEntityDescription - - def __init__( - self, device: SensemeFan, description: SenseMESwitchEntityDescription - ) -> None: - """Initialize the entity.""" - self.entity_description = description - self._attr_device_class = SwitchDeviceClass.SWITCH - super().__init__(device, f"{device.name} {description.name}") - self._attr_unique_id = f"{self._device.uuid}-SWITCH-{description.key}" - - @callback - def _async_update_attrs(self) -> None: - """Update attrs from device.""" - self._attr_is_on = self.entity_description.value_fn(self._device) - - async def async_turn_on(self, **kwargs: Any) -> None: - """Turn on the switch.""" - self.entity_description.set_fn(self._device, True) - - async def async_turn_off(self, **kwargs: Any) -> None: - """Turn off the switch.""" - self.entity_description.set_fn(self._device, False) diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 13c3a385756..77b1c77a914 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -391,7 +391,6 @@ FLOWS = { "screenlogic", "season", "sense", - "senseme", "sensibo", "sensirion_ble", "sensorpro", diff --git a/homeassistant/generated/dhcp.py b/homeassistant/generated/dhcp.py index ce5536e62a4..6c8910cd7f9 100644 --- a/homeassistant/generated/dhcp.py +++ b/homeassistant/generated/dhcp.py @@ -501,14 +501,6 @@ DHCP: list[dict[str, str | bool]] = [ "hostname": "sense-*", "macaddress": "A4D578*", }, - { - "domain": "senseme", - "registered_devices": True, - }, - { - "domain": "senseme", - "macaddress": "20F85E*", - }, { "domain": "sensibo", "hostname": "sensibo*", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index e67f385f4b4..0d080763f55 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -4829,12 +4829,6 @@ "config_flow": true, "iot_class": "cloud_polling" }, - "senseme": { - "name": "SenseME", - "integration_type": "hub", - "config_flow": true, - "iot_class": "local_push" - }, "sensibo": { "name": "Sensibo", "integration_type": "hub", diff --git a/mypy.ini b/mypy.ini index e4c67bfd909..ab8b5a5df89 100644 --- a/mypy.ini +++ b/mypy.ini @@ -2532,16 +2532,6 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true -[mypy-homeassistant.components.senseme.*] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -warn_return_any = true -warn_unreachable = true - [mypy-homeassistant.components.sensibo.*] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/requirements_all.txt b/requirements_all.txt index 0be541111c5..34575b7fc25 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -335,9 +335,6 @@ aioridwell==2023.01.0 # homeassistant.components.ruuvi_gateway aioruuvigateway==0.1.0 -# homeassistant.components.senseme -aiosenseme==0.6.1 - # homeassistant.components.senz aiosenz==1.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7d1cd7db14e..afacd20948a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -307,9 +307,6 @@ aioridwell==2023.01.0 # homeassistant.components.ruuvi_gateway aioruuvigateway==0.1.0 -# homeassistant.components.senseme -aiosenseme==0.6.1 - # homeassistant.components.senz aiosenz==1.0.0 diff --git a/tests/components/senseme/__init__.py b/tests/components/senseme/__init__.py deleted file mode 100644 index 43d586328bb..00000000000 --- a/tests/components/senseme/__init__.py +++ /dev/null @@ -1,142 +0,0 @@ -"""Tests for the SenseME integration.""" - -from contextlib import contextmanager -from unittest.mock import AsyncMock, MagicMock, patch - -from aiosenseme import SensemeDevice, SensemeDiscovery - -from homeassistant.components.senseme import config_flow - -MOCK_NAME = "Haiku Fan" -MOCK_UUID = "77a6b7b3-925d-4695-a415-76d76dca4444" -MOCK_ADDRESS = "127.0.0.1" -MOCK_MAC = "20:F8:5E:92:5A:75" - - -def _mock_device(): - device = MagicMock(auto_spec=SensemeDevice) - device.async_update = AsyncMock() - device.model = "Haiku Fan" - device.fan_speed_max = 7 - device.mac = "aa:bb:cc:dd:ee:ff" - device.fan_dir = "REV" - device.has_light = True - device.is_light = False - device.light_brightness = 50 - device.room_name = "Main" - device.room_type = "Main" - device.fw_version = "1" - device.fan_autocomfort = "COOLING" - device.fan_smartmode = "OFF" - device.fan_whoosh_mode = "on" - device.name = MOCK_NAME - device.uuid = MOCK_UUID - device.address = MOCK_ADDRESS - device.get_device_info = { - "name": MOCK_NAME, - "uuid": MOCK_UUID, - "mac": MOCK_ADDRESS, - "address": MOCK_ADDRESS, - "base_model": "FAN,HAIKU,HSERIES", - "has_light": False, - "has_sensor": True, - "is_fan": True, - "is_light": False, - } - return device - - -device_alternate_ip = MagicMock(auto_spec=SensemeDevice) -device_alternate_ip.async_update = AsyncMock() -device_alternate_ip.model = "Haiku Fan" -device_alternate_ip.fan_speed_max = 7 -device_alternate_ip.mac = "aa:bb:cc:dd:ee:ff" -device_alternate_ip.fan_dir = "REV" -device_alternate_ip.room_name = "Main" -device_alternate_ip.room_type = "Main" -device_alternate_ip.fw_version = "1" -device_alternate_ip.fan_autocomfort = "on" -device_alternate_ip.fan_smartmode = "on" -device_alternate_ip.fan_whoosh_mode = "on" -device_alternate_ip.name = MOCK_NAME -device_alternate_ip.uuid = MOCK_UUID -device_alternate_ip.address = "127.0.0.8" -device_alternate_ip.get_device_info = { - "name": MOCK_NAME, - "uuid": MOCK_UUID, - "mac": "20:F8:5E:92:5A:75", - "address": "127.0.0.8", - "base_model": "FAN,HAIKU,HSERIES", - "has_light": False, - "has_sensor": True, - "is_fan": True, - "is_light": False, -} - - -device2 = MagicMock(auto_spec=SensemeDevice) -device2.async_update = AsyncMock() -device2.model = "Haiku Fan" -device2.fan_speed_max = 7 -device2.mac = "aa:bb:cc:dd:ee:ff" -device2.fan_dir = "FWD" -device2.room_name = "Main" -device2.room_type = "Main" -device2.fw_version = "1" -device2.fan_autocomfort = "on" -device2.fan_smartmode = "on" -device2.fan_whoosh_mode = "on" -device2.name = "Device 2" -device2.uuid = "uuid2" -device2.address = "127.0.0.2" -device2.get_device_info = { - "name": "Device 2", - "uuid": "uuid2", - "mac": "20:F8:5E:92:5A:76", - "address": "127.0.0.2", - "base_model": "FAN,HAIKU,HSERIES", - "has_light": True, - "has_sensor": True, - "is_fan": True, - "is_light": False, -} - -device_no_uuid = MagicMock(auto_spec=SensemeDevice) -device_no_uuid.uuid = None - - -MOCK_DEVICE = _mock_device() -MOCK_DEVICE_ALTERNATE_IP = device_alternate_ip -MOCK_DEVICE2 = device2 -MOCK_DEVICE_NO_UUID = device_no_uuid - - -def _patch_discovery(device=None, no_device=None): - """Patch discovery.""" - mock_senseme_discovery = MagicMock(auto_spec=SensemeDiscovery) - if not no_device: - mock_senseme_discovery.devices = [device or MOCK_DEVICE] - - @contextmanager - def _patcher(): - with patch.object(config_flow, "DISCOVER_TIMEOUT", 0), patch( - "homeassistant.components.senseme.discovery.SensemeDiscovery", - return_value=mock_senseme_discovery, - ): - yield - - return _patcher() - - -def _patch_device(device=None, no_device=False): - async def _device_mocker(*args, **kwargs): - if no_device: - return False, None - if device: - return True, device - return True, _mock_device() - - return patch( - "homeassistant.components.senseme.async_get_device_by_device_info", - new=_device_mocker, - ) diff --git a/tests/components/senseme/test_config_flow.py b/tests/components/senseme/test_config_flow.py deleted file mode 100644 index 63850dff3b5..00000000000 --- a/tests/components/senseme/test_config_flow.py +++ /dev/null @@ -1,361 +0,0 @@ -"""Test the SenseME config flow.""" -from unittest.mock import patch - -from homeassistant import config_entries -from homeassistant.components import dhcp -from homeassistant.components.senseme.const import DOMAIN -from homeassistant.const import CONF_HOST, CONF_ID -from homeassistant.core import HomeAssistant -from homeassistant.data_entry_flow import FlowResultType - -from . import ( - MOCK_ADDRESS, - MOCK_DEVICE, - MOCK_DEVICE2, - MOCK_DEVICE_ALTERNATE_IP, - MOCK_DEVICE_NO_UUID, - MOCK_MAC, - MOCK_UUID, - _patch_discovery, -) - -from tests.common import MockConfigEntry - -DHCP_DISCOVERY = dhcp.DhcpServiceInfo( - hostname="any", - ip=MOCK_ADDRESS, - macaddress=MOCK_MAC, -) - - -async def test_form_user(hass: HomeAssistant) -> None: - """Test we get the form as a user.""" - - with _patch_discovery(), patch( - "homeassistant.components.senseme.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - assert result["type"] == FlowResultType.FORM - assert not result["errors"] - - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "device": MOCK_UUID, - }, - ) - await hass.async_block_till_done() - - assert result2["type"] == FlowResultType.CREATE_ENTRY - assert result2["title"] == "Haiku Fan" - assert result2["data"] == { - "info": MOCK_DEVICE.get_device_info, - } - assert len(mock_setup_entry.mock_calls) == 1 - - -async def test_form_user_manual_entry(hass: HomeAssistant) -> None: - """Test we get the form as a user with a discovery but user chooses manual.""" - - with _patch_discovery(): - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - assert result["type"] == FlowResultType.FORM - assert not result["errors"] - - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "device": None, - }, - ) - await hass.async_block_till_done() - - assert result2["type"] == FlowResultType.FORM - assert result2["step_id"] == "manual" - - with patch( - "homeassistant.components.senseme.config_flow.async_get_device_by_ip_address", - return_value=MOCK_DEVICE, - ), patch( - "homeassistant.components.senseme.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result3 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - CONF_HOST: MOCK_ADDRESS, - }, - ) - await hass.async_block_till_done() - - assert result3["type"] == FlowResultType.CREATE_ENTRY - assert result3["title"] == "Haiku Fan" - assert result3["data"] == { - "info": MOCK_DEVICE.get_device_info, - } - assert len(mock_setup_entry.mock_calls) == 1 - - -async def test_form_user_no_discovery(hass: HomeAssistant) -> None: - """Test we get the form as a user with no discovery.""" - - with _patch_discovery(no_device=True), patch( - "homeassistant.components.senseme.config_flow.async_get_device_by_ip_address", - return_value=MOCK_DEVICE, - ), patch( - "homeassistant.components.senseme.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - assert result["type"] == FlowResultType.FORM - assert not result["errors"] - - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - CONF_HOST: "not a valid address", - }, - ) - await hass.async_block_till_done() - - assert result2["type"] == FlowResultType.FORM - assert result2["step_id"] == "manual" - assert result2["errors"] == {CONF_HOST: "invalid_host"} - - result3 = await hass.config_entries.flow.async_configure( - result2["flow_id"], - { - CONF_HOST: MOCK_ADDRESS, - }, - ) - await hass.async_block_till_done() - - assert result3["type"] == FlowResultType.CREATE_ENTRY - assert result3["title"] == "Haiku Fan" - assert result3["data"] == { - "info": MOCK_DEVICE.get_device_info, - } - assert len(mock_setup_entry.mock_calls) == 1 - - -async def test_form_user_manual_entry_cannot_connect(hass: HomeAssistant) -> None: - """Test we get the form as a user.""" - - with _patch_discovery(): - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - assert result["type"] == FlowResultType.FORM - assert not result["errors"] - - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "device": None, - }, - ) - await hass.async_block_till_done() - - assert result2["type"] == FlowResultType.FORM - assert result2["step_id"] == "manual" - - with patch( - "homeassistant.components.senseme.config_flow.async_get_device_by_ip_address", - return_value=None, - ): - result3 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - CONF_HOST: MOCK_ADDRESS, - }, - ) - await hass.async_block_till_done() - - assert result3["type"] == FlowResultType.FORM - assert result3["step_id"] == "manual" - assert result3["errors"] == {CONF_HOST: "cannot_connect"} - - -async def test_discovery(hass: HomeAssistant) -> None: - """Test we can setup a discovered device.""" - entry = MockConfigEntry( - domain=DOMAIN, - data={ - "info": MOCK_DEVICE2.get_device_info, - }, - unique_id=MOCK_DEVICE2.uuid, - ) - entry.add_to_hass(hass) - - with _patch_discovery(), patch( - "homeassistant.components.senseme.async_get_device_by_device_info", - return_value=(True, MOCK_DEVICE2), - ): - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - with _patch_discovery(), patch( - "homeassistant.components.senseme.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_INTEGRATION_DISCOVERY}, - data={CONF_ID: MOCK_UUID}, - ) - assert result["type"] == FlowResultType.FORM - assert not result["errors"] - - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "device": MOCK_UUID, - }, - ) - await hass.async_block_till_done() - - assert result2["type"] == FlowResultType.CREATE_ENTRY - assert result2["title"] == "Haiku Fan" - assert result2["data"] == { - "info": MOCK_DEVICE.get_device_info, - } - assert len(mock_setup_entry.mock_calls) == 1 - - -async def test_discovery_existing_device_no_ip_change(hass: HomeAssistant) -> None: - """Test we can setup a discovered device.""" - entry = MockConfigEntry( - domain=DOMAIN, - data={ - "info": MOCK_DEVICE.get_device_info, - }, - unique_id=MOCK_DEVICE.uuid, - ) - entry.add_to_hass(hass) - - with _patch_discovery(), patch( - "homeassistant.components.senseme.async_get_device_by_device_info", - return_value=(True, MOCK_DEVICE), - ): - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - with _patch_discovery(): - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_INTEGRATION_DISCOVERY}, - data={CONF_ID: MOCK_UUID}, - ) - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "already_configured" - - -async def test_discovery_existing_device_ip_change(hass: HomeAssistant) -> None: - """Test a config entry ips get updated from discovery.""" - entry = MockConfigEntry( - domain=DOMAIN, - data={ - "info": MOCK_DEVICE.get_device_info, - }, - unique_id=MOCK_DEVICE.uuid, - ) - entry.add_to_hass(hass) - - with _patch_discovery(device=MOCK_DEVICE_ALTERNATE_IP), patch( - "homeassistant.components.senseme.async_get_device_by_device_info", - return_value=(True, MOCK_DEVICE), - ): - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_INTEGRATION_DISCOVERY}, - data={CONF_ID: MOCK_UUID}, - ) - await hass.async_block_till_done() - - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "already_configured" - assert entry.data["info"]["address"] == "127.0.0.8" - - -async def test_dhcp_discovery_existing_config_entry(hass: HomeAssistant) -> None: - """Test dhcp discovery is aborted if there is an existing config entry.""" - entry = MockConfigEntry( - domain=DOMAIN, - data={ - "info": MOCK_DEVICE2.get_device_info, - }, - unique_id=MOCK_DEVICE2.uuid, - ) - entry.add_to_hass(hass) - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=DHCP_DISCOVERY - ) - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "already_configured" - - -async def test_dhcp_discovery(hass: HomeAssistant) -> None: - """Test we can setup a dhcp discovered device.""" - with _patch_discovery(), patch( - "homeassistant.components.senseme.config_flow.async_get_device_by_ip_address", - return_value=MOCK_DEVICE, - ), patch( - "homeassistant.components.senseme.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=DHCP_DISCOVERY - ) - assert result["type"] == FlowResultType.FORM - assert not result["errors"] - - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "device": MOCK_UUID, - }, - ) - await hass.async_block_till_done() - - assert result2["type"] == FlowResultType.CREATE_ENTRY - assert result2["title"] == "Haiku Fan" - assert result2["data"] == { - "info": MOCK_DEVICE.get_device_info, - } - assert len(mock_setup_entry.mock_calls) == 1 - - -async def test_dhcp_discovery_cannot_connect(hass: HomeAssistant) -> None: - """Test we abort if we cannot cannot to a dhcp discovered device.""" - with _patch_discovery(), patch( - "homeassistant.components.senseme.config_flow.async_get_device_by_ip_address", - return_value=None, - ): - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=DHCP_DISCOVERY - ) - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "cannot_connect" - - -async def test_dhcp_discovery_cannot_connect_no_uuid(hass: HomeAssistant) -> None: - """Test we abort if the discovered device has no uuid.""" - with _patch_discovery(), patch( - "homeassistant.components.senseme.config_flow.async_get_device_by_ip_address", - return_value=MOCK_DEVICE_NO_UUID, - ): - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=DHCP_DISCOVERY - ) - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "cannot_connect" diff --git a/tests/components/senseme/test_light.py b/tests/components/senseme/test_light.py deleted file mode 100644 index d4a8eddc91c..00000000000 --- a/tests/components/senseme/test_light.py +++ /dev/null @@ -1,115 +0,0 @@ -"""Tests for senseme light platform.""" -from aiosenseme import SensemeDevice - -from homeassistant.components import senseme -from homeassistant.components.light import ( - ATTR_BRIGHTNESS, - ATTR_COLOR_MODE, - ATTR_COLOR_TEMP, - ATTR_SUPPORTED_COLOR_MODES, - DOMAIN as LIGHT_DOMAIN, - SERVICE_TURN_OFF, - SERVICE_TURN_ON, - ColorMode, -) -from homeassistant.components.senseme.const import DOMAIN -from homeassistant.const import ATTR_ENTITY_ID, STATE_ON -from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er -from homeassistant.setup import async_setup_component - -from . import _mock_device, _patch_device, _patch_discovery - -from tests.common import MockConfigEntry - - -async def _setup_mocked_entry(hass: HomeAssistant, device: SensemeDevice) -> None: - """Set up a mocked entry.""" - entry = MockConfigEntry( - domain=DOMAIN, - data={"info": device.get_device_info}, - unique_id=device.uuid, - ) - entry.add_to_hass(hass) - with _patch_discovery(), _patch_device(device=device): - await async_setup_component(hass, senseme.DOMAIN, {senseme.DOMAIN: {}}) - await hass.async_block_till_done() - - -async def test_light_unique_id(hass: HomeAssistant) -> None: - """Test a light unique id.""" - device = _mock_device() - await _setup_mocked_entry(hass, device) - entity_id = "light.haiku_fan" - entity_registry = er.async_get(hass) - assert entity_registry.async_get(entity_id).unique_id == f"{device.uuid}-LIGHT" - state = hass.states.get(entity_id) - assert state.state == STATE_ON - - -async def test_fan_light(hass: HomeAssistant) -> None: - """Test a fan light.""" - device = _mock_device() - await _setup_mocked_entry(hass, device) - entity_id = "light.haiku_fan" - - state = hass.states.get(entity_id) - assert state.state == STATE_ON - attributes = state.attributes - assert attributes[ATTR_BRIGHTNESS] == 255 - assert attributes[ATTR_COLOR_MODE] == ColorMode.BRIGHTNESS - assert attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS] - - await hass.services.async_call( - LIGHT_DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: entity_id}, blocking=True - ) - assert device.light_on is False - - await hass.services.async_call( - LIGHT_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: entity_id}, blocking=True - ) - assert device.light_on is True - - -async def test_fan_light_no_brightness(hass: HomeAssistant) -> None: - """Test a fan light without brightness.""" - device = _mock_device() - device.brightness = None - await _setup_mocked_entry(hass, device) - entity_id = "light.haiku_fan" - - state = hass.states.get(entity_id) - assert state.state == STATE_ON - attributes = state.attributes - assert attributes[ATTR_BRIGHTNESS] == 255 - assert attributes[ATTR_COLOR_MODE] == ColorMode.BRIGHTNESS - assert attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS] - - -async def test_standalone_light(hass: HomeAssistant) -> None: - """Test a standalone light.""" - device = _mock_device() - device.is_light = True - device.light_color_temp_max = 6500 - device.light_color_temp_min = 2700 - device.light_color_temp = 4000 - await _setup_mocked_entry(hass, device) - entity_id = "light.haiku_fan_light" - - state = hass.states.get(entity_id) - assert state.state == STATE_ON - attributes = state.attributes - assert attributes[ATTR_BRIGHTNESS] == 255 - assert attributes[ATTR_COLOR_MODE] == ColorMode.COLOR_TEMP - assert attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.COLOR_TEMP] - assert attributes[ATTR_COLOR_TEMP] == 250 - - await hass.services.async_call( - LIGHT_DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: entity_id}, blocking=True - ) - assert device.light_on is False - - await hass.services.async_call( - LIGHT_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: entity_id}, blocking=True - ) - assert device.light_on is True