Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .strict-typing
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@ homeassistant.components.vacuum.*
homeassistant.components.vallox.*
homeassistant.components.valve.*
homeassistant.components.velbus.*
homeassistant.components.vivotek.*
homeassistant.components.vlc_telnet.*
homeassistant.components.vodafone_station.*
homeassistant.components.volvo.*
Expand Down
2 changes: 0 additions & 2 deletions CODEOWNERS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion homeassistant/components/kraken/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,9 @@ async def async_setup(self) -> None:

def _get_websocket_name_asset_pairs(self) -> str:
return ",".join(
self.tradable_asset_pairs[tracked_pair]
pair
for tracked_pair in self._config_entry.options[CONF_TRACKED_ASSET_PAIRS]
if (pair := self.tradable_asset_pairs.get(tracked_pair)) is not None
)

def set_update_interval(self, update_interval: int) -> None:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/kraken/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def _async_add_kraken_sensors(tracked_asset_pairs: list[str]) -> None:
for description in SENSOR_TYPES
]
)
async_add_entities(entities, True)
async_add_entities(entities)

_async_add_kraken_sensors(config_entry.options[CONF_TRACKED_ASSET_PAIRS])

Expand Down
61 changes: 61 additions & 0 deletions homeassistant/components/oralb/icons.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"entity": {
"sensor": {
"pressure": {
"default": "mdi:tooth-outline",
"state": {
"high": "mdi:tooth",
"low": "mdi:alert",
"power_button_pressed": "mdi:power",
"button_pressed": "mdi:radiobox-marked"
}
},
"sector": {
"default": "mdi:circle-outline",
"state": {
"sector_1": "mdi:circle-slice-2",
"sector_2": "mdi:circle-slice-4",
"sector_3": "mdi:circle-slice-6",
"sector_4": "mdi:circle-slice-8",
"success": "mdi:check-circle-outline"
}
},
"toothbrush_state": {
"default": "mdi:toothbrush-electric",
"state": {
"initializing": "mdi:sync",
"idle": "mdi:toothbrush-electric",
"running": "mdi:waveform",
"charging": "mdi:battery-charging",
"setup": "mdi:wrench",
"flight_menu": "mdi:airplane",
"selection_menu": "mdi:menu",
"off": "mdi:power",
"sleeping": "mdi:sleep",
"transport": "mdi:dolly"
}
},
"number_of_sectors": {
"default": "mdi:chart-pie"
},
"mode": {
"default": "mdi:toothbrush-paste",
"state": {
"daily_clean": "mdi:repeat-once",
"sensitive": "mdi:feather",
"gum_care": "mdi:tooth-outline",
"intense": "mdi:shape-circle-plus",
"whitening": "mdi:shimmer",
"whiten": "mdi:shimmer",
"tongue_cleaning": "mdi:gate-and",
"super_sensitive": "mdi:feather",
"massage": "mdi:spa",
"deep_clean": "mdi:water",
"turbo": "mdi:car-turbocharger",
"off": "mdi:power",
"settings": "mdi:cog-outline"
}
}
}
}
}
29 changes: 28 additions & 1 deletion homeassistant/components/oralb/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
from __future__ import annotations

from oralb_ble import OralBSensor, SensorUpdate
from oralb_ble.parser import (
IO_SERIES_MODES,
PRESSURE,
SECTOR_MAP,
SMART_SERIES_MODES,
STATES,
)

from homeassistant.components.bluetooth.passive_update_processor import (
PassiveBluetoothDataProcessor,
Expand Down Expand Up @@ -39,6 +46,8 @@
key=OralBSensor.SECTOR,
translation_key="sector",
entity_category=EntityCategory.DIAGNOSTIC,
options=[v.replace(" ", "_") for v in set(SECTOR_MAP.values()) | {"no_sector"}],
device_class=SensorDeviceClass.ENUM,
),
OralBSensor.NUMBER_OF_SECTORS: SensorEntityDescription(
key=OralBSensor.NUMBER_OF_SECTORS,
Expand All @@ -53,16 +62,26 @@
),
OralBSensor.TOOTHBRUSH_STATE: SensorEntityDescription(
key=OralBSensor.TOOTHBRUSH_STATE,
translation_key="toothbrush_state",
options=[v.replace(" ", "_") for v in set(STATES.values())],
device_class=SensorDeviceClass.ENUM,
name=None,
),
OralBSensor.PRESSURE: SensorEntityDescription(
key=OralBSensor.PRESSURE,
translation_key="pressure",
options=[v.replace(" ", "_") for v in set(PRESSURE.values()) | {"low"}],
device_class=SensorDeviceClass.ENUM,
),
OralBSensor.MODE: SensorEntityDescription(
key=OralBSensor.MODE,
translation_key="mode",
entity_category=EntityCategory.DIAGNOSTIC,
options=[
v.replace(" ", "_")
for v in set(IO_SERIES_MODES.values()) | set(SMART_SERIES_MODES.values())
],
device_class=SensorDeviceClass.ENUM,
),
OralBSensor.SIGNAL_STRENGTH: SensorEntityDescription(
key=OralBSensor.SIGNAL_STRENGTH,
Expand Down Expand Up @@ -134,7 +153,15 @@ class OralBBluetoothSensorEntity(
@property
def native_value(self) -> str | int | None:
"""Return the native value."""
return self.processor.entity_data.get(self.entity_key)
value = self.processor.entity_data.get(self.entity_key)
if isinstance(value, str):
value = value.replace(" ", "_")
if (
self.entity_description.options is not None
and value not in self.entity_description.options
): # append unknown values to enum
self.entity_description.options.append(value)
return value

@property
def available(self) -> bool:
Expand Down
52 changes: 49 additions & 3 deletions homeassistant/components/oralb/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@
"entity": {
"sensor": {
"sector": {
"name": "Sector"
"name": "Sector",
"state": {
"no_sector": "No sector",
"sector_1": "Sector 1",
"sector_2": "Sector 2",
"sector_3": "Sector 3",
"sector_4": "Sector 4",
"success": "Success"
}
},
"number_of_sectors": {
"name": "Number of sectors"
Expand All @@ -31,10 +39,48 @@
"name": "Sector timer"
},
"pressure": {
"name": "Pressure"
"name": "Pressure",
"state": {
"normal": "[%key:common::state::normal%]",
"high": "[%key:common::state::high%]",
"low": "[%key:common::state::low%]",
"power_button_pressed": "Power button pressed",
"button_pressed": "Button pressed"
}
},
"mode": {
"name": "Brushing mode"
"name": "Brushing mode",
"state": {
"daily_clean": "Daily clean",
"sensitive": "Sensitive",
"gum_care": "Gum care",
"intense": "Intense",
"whitening": "Whiten",
"whiten": "[%key:component::oralb::entity::sensor::mode::state::whitening%]",
"tongue_cleaning": "Tongue clean",
"super_sensitive": "Super sensitive",
"massage": "Massage",
"deep_clean": "Deep clean",
"turbo": "Turbo",
"off": "[%key:common::state::off%]",
"settings": "Settings"
}
},
"toothbrush_state": {
"state": {
"initializing": "Initializing",
"idle": "[%key:common::state::idle%]",
"running": "Running",
"charging": "[%key:common::state::charging%]",
"setup": "Setup",
"flight_menu": "Flight menu",
"selection_menu": "Selection menu",
"off": "[%key:common::state::off%]",
"sleeping": "Sleeping",
"transport": "Transport",
"final_test": "Final test",
"pcb_test": "PCB test"
}
}
}
}
Expand Down
64 changes: 24 additions & 40 deletions homeassistant/components/plum_lightpad/__init__.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,36 @@
"""Support for Plum Lightpad devices."""

import logging

from aiohttp import ContentTypeError
from requests.exceptions import ConnectTimeout, HTTPError

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_PASSWORD,
CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP,
Platform,
)
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import issue_registry as ir

from .const import DOMAIN
from .utils import load_plum
DOMAIN = "plum_lightpad"

_LOGGER = logging.getLogger(__name__)

PLATFORMS = [Platform.LIGHT]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_setup_entry(hass: HomeAssistant, _: ConfigEntry) -> bool:
"""Set up Plum Lightpad from a config entry."""
_LOGGER.debug("Setting up config entry with ID = %s", entry.unique_id)

username = entry.data[CONF_USERNAME]
password = entry.data[CONF_PASSWORD]
ir.async_create_issue(
hass,
DOMAIN,
DOMAIN,
is_fixable=False,
severity=ir.IssueSeverity.ERROR,
translation_key="integration_removed",
translation_placeholders={
"entries": "/config/integrations/integration/plum_lightpad",
},
)

try:
plum = await load_plum(username, password, hass)
except ContentTypeError as ex:
_LOGGER.error("Unable to authenticate to Plum cloud: %s", ex)
return False
except (ConnectTimeout, HTTPError) as ex:
_LOGGER.error("Unable to connect to Plum cloud: %s", ex)
raise ConfigEntryNotReady from ex

hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = plum
return True

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

def cleanup(event):
"""Clean up resources."""
plum.cleanup()
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload config entry."""
if all(
config_entry.state is ConfigEntryState.NOT_LOADED
for config_entry in hass.config_entries.async_entries(DOMAIN)
if config_entry.entry_id != entry.entry_id
):
ir.async_delete_issue(hass, DOMAIN, DOMAIN)

entry.async_on_unload(hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, cleanup))
return True
51 changes: 2 additions & 49 deletions homeassistant/components/plum_lightpad/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,12 @@

from __future__ import annotations

import logging
from typing import Any
from homeassistant.config_entries import ConfigFlow

from aiohttp import ContentTypeError
from requests.exceptions import ConnectTimeout, HTTPError
import voluptuous as vol

from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME

from .const import DOMAIN
from .utils import load_plum

_LOGGER = logging.getLogger(__name__)
from . import DOMAIN


class PlumLightpadConfigFlow(ConfigFlow, domain=DOMAIN):
"""Config flow for Plum Lightpad integration."""

VERSION = 1

def _show_form(self, errors=None):
schema = {
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
}

return self.async_show_form(
step_id="user",
data_schema=vol.Schema(schema),
errors=errors or {},
)

async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initialized by the user or redirected to by import."""
if not user_input:
return self._show_form()

username = user_input[CONF_USERNAME]
password = user_input[CONF_PASSWORD]

# load Plum just so we know username/password work
try:
await load_plum(username, password, self.hass)
except (ContentTypeError, ConnectTimeout, HTTPError) as ex:
_LOGGER.error("Unable to connect/authenticate to Plum cloud: %s", str(ex))
return self._show_form({"base": "cannot_connect"})

await self.async_set_unique_id(username)
self._abort_if_unique_id_configured()

return self.async_create_entry(
title=username, data={CONF_USERNAME: username, CONF_PASSWORD: password}
)
3 changes: 0 additions & 3 deletions homeassistant/components/plum_lightpad/const.py

This file was deleted.

9 changes: 0 additions & 9 deletions homeassistant/components/plum_lightpad/icons.json

This file was deleted.

Loading
Loading