Collection of code styling tweaks (#87381)
This commit is contained in:
parent
e55f11296e
commit
3d557b5583
52 changed files with 274 additions and 156 deletions
|
@ -129,8 +129,8 @@ async def async_setup_entry(
|
||||||
hass, calendar_item.dict(exclude_unset=True)
|
hass, calendar_item.dict(exclude_unset=True)
|
||||||
)
|
)
|
||||||
new_calendars.append(calendar_info)
|
new_calendars.append(calendar_info)
|
||||||
# Yaml calendar config may map one calendar to multiple entities with extra options like
|
# Yaml calendar config may map one calendar to multiple entities
|
||||||
# offsets or search criteria.
|
# with extra options like offsets or search criteria.
|
||||||
num_entities = len(calendar_info[CONF_ENTITIES])
|
num_entities = len(calendar_info[CONF_ENTITIES])
|
||||||
for data in calendar_info[CONF_ENTITIES]:
|
for data in calendar_info[CONF_ENTITIES]:
|
||||||
entity_enabled = data.get(CONF_TRACK, True)
|
entity_enabled = data.get(CONF_TRACK, True)
|
||||||
|
@ -141,15 +141,17 @@ async def async_setup_entry(
|
||||||
" removed from google_calendars.yaml"
|
" removed from google_calendars.yaml"
|
||||||
)
|
)
|
||||||
entity_name = data[CONF_DEVICE_ID]
|
entity_name = data[CONF_DEVICE_ID]
|
||||||
# The unique id is based on the config entry and calendar id since multiple accounts
|
# The unique id is based on the config entry and calendar id since
|
||||||
# can have a common calendar id (e.g. `en.usa#holiday@group.v.calendar.google.com`).
|
# multiple accounts can have a common calendar id
|
||||||
# When using google_calendars.yaml with multiple entities for a single calendar, we
|
# (e.g. `en.usa#holiday@group.v.calendar.google.com`).
|
||||||
# have no way to set a unique id.
|
# When using google_calendars.yaml with multiple entities for a
|
||||||
|
# single calendar, we have no way to set a unique id.
|
||||||
if num_entities > 1:
|
if num_entities > 1:
|
||||||
unique_id = None
|
unique_id = None
|
||||||
else:
|
else:
|
||||||
unique_id = f"{config_entry.unique_id}-{calendar_id}"
|
unique_id = f"{config_entry.unique_id}-{calendar_id}"
|
||||||
# Migrate to new unique_id format which supports multiple config entries as of 2022.7
|
# Migrate to new unique_id format which supports
|
||||||
|
# multiple config entries as of 2022.7
|
||||||
for old_unique_id in (calendar_id, f"{calendar_id}-{entity_name}"):
|
for old_unique_id in (calendar_id, f"{calendar_id}-{entity_name}"):
|
||||||
if not (entity_entry := entity_entry_map.get(old_unique_id)):
|
if not (entity_entry := entity_entry_map.get(old_unique_id)):
|
||||||
continue
|
continue
|
||||||
|
@ -173,9 +175,9 @@ async def async_setup_entry(
|
||||||
entity_entry.entity_id,
|
entity_entry.entity_id,
|
||||||
)
|
)
|
||||||
coordinator: CalendarSyncUpdateCoordinator | CalendarQueryUpdateCoordinator
|
coordinator: CalendarSyncUpdateCoordinator | CalendarQueryUpdateCoordinator
|
||||||
# Prefer calendar sync down of resources when possible. However, sync does not work
|
# Prefer calendar sync down of resources when possible. However,
|
||||||
# for search. Also free-busy calendars denormalize recurring events as individual
|
# sync does not work for search. Also free-busy calendars denormalize
|
||||||
# events which is not efficient for sync
|
# recurring events as individual events which is not efficient for sync
|
||||||
support_write = (
|
support_write = (
|
||||||
calendar_item.access_role.is_writer
|
calendar_item.access_role.is_writer
|
||||||
and get_feature_access(hass, config_entry) is FeatureAccess.read_write
|
and get_feature_access(hass, config_entry) is FeatureAccess.read_write
|
||||||
|
|
|
@ -134,7 +134,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
DATA_GROUP_MANAGER: group_manager,
|
DATA_GROUP_MANAGER: group_manager,
|
||||||
DATA_SOURCE_MANAGER: source_manager,
|
DATA_SOURCE_MANAGER: source_manager,
|
||||||
Platform.MEDIA_PLAYER: players,
|
Platform.MEDIA_PLAYER: players,
|
||||||
# Maps player_id to entity_id. Populated by the individual HeosMediaPlayer entities.
|
# Maps player_id to entity_id. Populated by the individual
|
||||||
|
# HeosMediaPlayer entities.
|
||||||
DATA_ENTITY_ID_MAP: {},
|
DATA_ENTITY_ID_MAP: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,11 +252,11 @@ class GroupManager:
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
|
|
||||||
def _get_entity_id_to_player_id_map(self) -> dict:
|
def _get_entity_id_to_player_id_map(self) -> dict:
|
||||||
"""Return a dictionary which maps all HeosMediaPlayer entity_ids to player_ids."""
|
"""Return mapping of all HeosMediaPlayer entity_ids to player_ids."""
|
||||||
return {v: k for k, v in self._hass.data[DOMAIN][DATA_ENTITY_ID_MAP].items()}
|
return {v: k for k, v in self._hass.data[DOMAIN][DATA_ENTITY_ID_MAP].items()}
|
||||||
|
|
||||||
async def async_get_group_membership(self):
|
async def async_get_group_membership(self):
|
||||||
"""Return a dictionary which contains all group members for each player as entity_ids."""
|
"""Return all group members for each player as entity_ids."""
|
||||||
group_info_by_entity_id = {
|
group_info_by_entity_id = {
|
||||||
player_entity_id: []
|
player_entity_id: []
|
||||||
for player_entity_id in self._get_entity_id_to_player_id_map()
|
for player_entity_id in self._get_entity_id_to_player_id_map()
|
||||||
|
@ -287,7 +288,7 @@ class GroupManager:
|
||||||
async def async_join_players(
|
async def async_join_players(
|
||||||
self, leader_entity_id: str, member_entity_ids: list[str]
|
self, leader_entity_id: str, member_entity_ids: list[str]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Create a group with `leader_entity_id` as group leader and `member_entity_ids` as member players."""
|
"""Create a group a group leader and member players."""
|
||||||
entity_id_to_player_id_map = self._get_entity_id_to_player_id_map()
|
entity_id_to_player_id_map = self._get_entity_id_to_player_id_map()
|
||||||
leader_id = entity_id_to_player_id_map.get(leader_entity_id)
|
leader_id = entity_id_to_player_id_map.get(leader_entity_id)
|
||||||
if not leader_id:
|
if not leader_id:
|
||||||
|
|
|
@ -93,7 +93,8 @@ def find_existing_host(
|
||||||
def ensure_pin_format(pin: str, allow_insecure_setup_codes: Any = None) -> str:
|
def ensure_pin_format(pin: str, allow_insecure_setup_codes: Any = None) -> str:
|
||||||
"""Ensure a pin code is correctly formatted.
|
"""Ensure a pin code is correctly formatted.
|
||||||
|
|
||||||
Ensures a pin code is in the format 111-11-111. Handles codes with and without dashes.
|
Ensures a pin code is in the format 111-11-111.
|
||||||
|
Handles codes with and without dashes.
|
||||||
|
|
||||||
If incorrect code is entered, an exception is raised.
|
If incorrect code is entered, an exception is raised.
|
||||||
"""
|
"""
|
||||||
|
@ -284,7 +285,8 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
if self.controller is None:
|
if self.controller is None:
|
||||||
await self._async_setup_controller()
|
await self._async_setup_controller()
|
||||||
|
|
||||||
# mypy can't see that self._async_setup_controller() always sets self.controller or throws
|
# mypy can't see that self._async_setup_controller() always
|
||||||
|
# sets self.controller or throws
|
||||||
assert self.controller
|
assert self.controller
|
||||||
|
|
||||||
pairing = self.controller.load_pairing(
|
pairing = self.controller.load_pairing(
|
||||||
|
@ -344,7 +346,8 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
if model in HOMEKIT_IGNORE:
|
if model in HOMEKIT_IGNORE:
|
||||||
return self.async_abort(reason="ignored_model")
|
return self.async_abort(reason="ignored_model")
|
||||||
|
|
||||||
# If this is a HomeKit bridge/accessory exported by *this* HA instance ignore it.
|
# If this is a HomeKit bridge/accessory exported
|
||||||
|
# by *this* HA instance ignore it.
|
||||||
if await self._hkid_is_homekit(hkid):
|
if await self._hkid_is_homekit(hkid):
|
||||||
return self.async_abort(reason="ignored_model")
|
return self.async_abort(reason="ignored_model")
|
||||||
|
|
||||||
|
@ -366,12 +369,11 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
return self.async_abort(reason="ignored_model")
|
return self.async_abort(reason="ignored_model")
|
||||||
|
|
||||||
# Late imports in case BLE is not available
|
# Late imports in case BLE is not available
|
||||||
from aiohomekit.controller.ble.discovery import ( # pylint: disable=import-outside-toplevel
|
# pylint: disable-next=import-outside-toplevel
|
||||||
BleDiscovery,
|
from aiohomekit.controller.ble.discovery import BleDiscovery
|
||||||
)
|
|
||||||
from aiohomekit.controller.ble.manufacturer_data import ( # pylint: disable=import-outside-toplevel
|
# pylint: disable-next=import-outside-toplevel
|
||||||
HomeKitAdvertisement,
|
from aiohomekit.controller.ble.manufacturer_data import HomeKitAdvertisement
|
||||||
)
|
|
||||||
|
|
||||||
await self.async_set_unique_id(discovery_info.address)
|
await self.async_set_unique_id(discovery_info.address)
|
||||||
self._abort_if_unique_id_configured()
|
self._abort_if_unique_id_configured()
|
||||||
|
|
|
@ -817,8 +817,8 @@ class PowerViewShadeDualOverlappedCombined(PowerViewShadeDualOverlappedBase):
|
||||||
@callback
|
@callback
|
||||||
def _get_shade_move(self, target_hass_position: int) -> PowerviewShadeMove:
|
def _get_shade_move(self, target_hass_position: int) -> PowerviewShadeMove:
|
||||||
position_shade = hass_position_to_hd(target_hass_position, MAX_POSITION)
|
position_shade = hass_position_to_hd(target_hass_position, MAX_POSITION)
|
||||||
# note we set POS_KIND_VANE: MIN_POSITION here even with shades without tilt so no additional
|
# note we set POS_KIND_VANE: MIN_POSITION here even with shades without
|
||||||
# override is required for differences between type 8/9/10
|
# tilt so no additional override is required for differences between type 8/9/10
|
||||||
# this just stores the value in the coordinator for future reference
|
# this just stores the value in the coordinator for future reference
|
||||||
if target_hass_position <= 50:
|
if target_hass_position <= 50:
|
||||||
target_hass_position = target_hass_position * 2
|
target_hass_position = target_hass_position * 2
|
||||||
|
@ -846,11 +846,14 @@ class PowerViewShadeDualOverlappedFront(PowerViewShadeDualOverlappedBase):
|
||||||
|
|
||||||
This equates to two shades being controlled by one motor.
|
This equates to two shades being controlled by one motor.
|
||||||
The front shade must be completely down before the rear shade will move.
|
The front shade must be completely down before the rear shade will move.
|
||||||
Sibling Class: PowerViewShadeDualOverlappedCombined, PowerViewShadeDualOverlappedRear
|
Sibling Class:
|
||||||
API Class: ShadeDualOverlapped + ShadeDualOverlappedTilt90 + ShadeDualOverlappedTilt180
|
PowerViewShadeDualOverlappedCombined, PowerViewShadeDualOverlappedRear
|
||||||
|
API Class:
|
||||||
|
ShadeDualOverlapped + ShadeDualOverlappedTilt90 + ShadeDualOverlappedTilt180
|
||||||
|
|
||||||
Type 8 - Duolite (front and rear shades)
|
Type 8 - Duolite (front and rear shades)
|
||||||
Type 9 - Duolite with 90° Tilt (front bottom up shade that also tilts plus a rear opaque (non-tilting) shade)
|
Type 9 - Duolite with 90° Tilt (front bottom up shade that also tilts
|
||||||
|
plus a rear opaque (non-tilting) shade)
|
||||||
Type 10 - Duolite with 180° Tilt
|
Type 10 - Duolite with 180° Tilt
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -907,11 +910,14 @@ class PowerViewShadeDualOverlappedRear(PowerViewShadeDualOverlappedBase):
|
||||||
|
|
||||||
This equates to two shades being controlled by one motor.
|
This equates to two shades being controlled by one motor.
|
||||||
The front shade must be completely down before the rear shade will move.
|
The front shade must be completely down before the rear shade will move.
|
||||||
Sibling Class: PowerViewShadeDualOverlappedCombined, PowerViewShadeDualOverlappedFront
|
Sibling Class:
|
||||||
API Class: ShadeDualOverlapped + ShadeDualOverlappedTilt90 + ShadeDualOverlappedTilt180
|
PowerViewShadeDualOverlappedCombined, PowerViewShadeDualOverlappedFront
|
||||||
|
API Class:
|
||||||
|
ShadeDualOverlapped + ShadeDualOverlappedTilt90 + ShadeDualOverlappedTilt180
|
||||||
|
|
||||||
Type 8 - Duolite (front and rear shades)
|
Type 8 - Duolite (front and rear shades)
|
||||||
Type 9 - Duolite with 90° Tilt (front bottom up shade that also tilts plus a rear opaque (non-tilting) shade)
|
Type 9 - Duolite with 90° Tilt (front bottom up shade that also tilts plus
|
||||||
|
a rear opaque (non-tilting) shade)
|
||||||
Type 10 - Duolite with 180° Tilt
|
Type 10 - Duolite with 180° Tilt
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,8 @@ OPTIONS = "options"
|
||||||
|
|
||||||
async def async_get_device_config(hass, config_entry):
|
async def async_get_device_config(hass, config_entry):
|
||||||
"""Initiate the connection and services."""
|
"""Initiate the connection and services."""
|
||||||
# Make a copy of addresses due to edge case where the list of devices could change during status update
|
# Make a copy of addresses due to edge case where the list of devices could
|
||||||
|
# change during status update
|
||||||
# Cannot be done concurrently due to issues with the underlying protocol.
|
# Cannot be done concurrently due to issues with the underlying protocol.
|
||||||
for address in list(devices):
|
for address in list(devices):
|
||||||
if devices[address].is_battery:
|
if devices[address].is_battery:
|
||||||
|
|
|
@ -72,7 +72,12 @@ async def async_setup_entry(
|
||||||
child_nodes: list[
|
child_nodes: list[
|
||||||
tuple[Node, BinarySensorDeviceClass | None, str | None, DeviceInfo | None]
|
tuple[Node, BinarySensorDeviceClass | None, str | None, DeviceInfo | None]
|
||||||
] = []
|
] = []
|
||||||
entity: ISYInsteonBinarySensorEntity | ISYBinarySensorEntity | ISYBinarySensorHeartbeat | ISYBinarySensorProgramEntity
|
entity: (
|
||||||
|
ISYInsteonBinarySensorEntity
|
||||||
|
| ISYBinarySensorEntity
|
||||||
|
| ISYBinarySensorHeartbeat
|
||||||
|
| ISYBinarySensorProgramEntity
|
||||||
|
)
|
||||||
|
|
||||||
isy_data = hass.data[DOMAIN][entry.entry_id]
|
isy_data = hass.data[DOMAIN][entry.entry_id]
|
||||||
devices: dict[str, DeviceInfo] = isy_data.devices
|
devices: dict[str, DeviceInfo] = isy_data.devices
|
||||||
|
|
|
@ -51,8 +51,8 @@ async def async_setup_entry(
|
||||||
entities.append(ISYSwitchProgramEntity(name, status, actions))
|
entities.append(ISYSwitchProgramEntity(name, status, actions))
|
||||||
|
|
||||||
for node, control in isy_data.aux_properties[Platform.SWITCH]:
|
for node, control in isy_data.aux_properties[Platform.SWITCH]:
|
||||||
# Currently only used for enable switches, will need to be updated for NS support
|
# Currently only used for enable switches, will need to be updated for
|
||||||
# by making sure control == TAG_ENABLED
|
# NS support by making sure control == TAG_ENABLED
|
||||||
description = SwitchEntityDescription(
|
description = SwitchEntityDescription(
|
||||||
key=control,
|
key=control,
|
||||||
device_class=SwitchDeviceClass.SWITCH,
|
device_class=SwitchDeviceClass.SWITCH,
|
||||||
|
|
|
@ -273,8 +273,9 @@ class ControllerDevice(ClimateEntity):
|
||||||
),
|
),
|
||||||
"control_zone": self._controller.zone_ctrl,
|
"control_zone": self._controller.zone_ctrl,
|
||||||
"control_zone_name": self.control_zone_name,
|
"control_zone_name": self.control_zone_name,
|
||||||
# Feature ClimateEntityFeature.TARGET_TEMPERATURE controls both displaying target temp & setting it
|
# Feature ClimateEntityFeature.TARGET_TEMPERATURE controls both displaying
|
||||||
# As the feature is turned off for zone control, report target temp as extra state attribute
|
# target temp & setting it as the feature is turned off for zone control,
|
||||||
|
# report target temp as extra state attribute
|
||||||
"control_zone_setpoint": show_temp(
|
"control_zone_setpoint": show_temp(
|
||||||
self.hass,
|
self.hass,
|
||||||
self.control_zone_setpoint,
|
self.control_zone_setpoint,
|
||||||
|
|
|
@ -224,7 +224,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Load a config entry."""
|
"""Load a config entry."""
|
||||||
# `config` is None when reloading the integration or no `knx` key in configuration.yaml
|
# `config` is None when reloading the integration
|
||||||
|
# or no `knx` key in configuration.yaml
|
||||||
if (config := hass.data.get(DATA_KNX_CONFIG)) is None:
|
if (config := hass.data.get(DATA_KNX_CONFIG)) is None:
|
||||||
_conf = await async_integration_yaml_config(hass, DOMAIN)
|
_conf = await async_integration_yaml_config(hass, DOMAIN)
|
||||||
if not _conf or DOMAIN not in _conf:
|
if not _conf or DOMAIN not in _conf:
|
||||||
|
@ -526,7 +527,10 @@ class KNXModule:
|
||||||
transcoder := DPTBase.parse_transcoder(dpt)
|
transcoder := DPTBase.parse_transcoder(dpt)
|
||||||
):
|
):
|
||||||
self._address_filter_transcoder.update(
|
self._address_filter_transcoder.update(
|
||||||
{_filter: transcoder for _filter in _filters} # type: ignore[type-abstract]
|
{
|
||||||
|
_filter: transcoder # type: ignore[type-abstract]
|
||||||
|
for _filter in _filters
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.xknx.telegram_queue.register_telegram_received_cb(
|
return self.xknx.telegram_queue.register_telegram_received_cb(
|
||||||
|
@ -558,7 +562,10 @@ class KNXModule:
|
||||||
transcoder := DPTBase.parse_transcoder(dpt)
|
transcoder := DPTBase.parse_transcoder(dpt)
|
||||||
):
|
):
|
||||||
self._group_address_transcoder.update(
|
self._group_address_transcoder.update(
|
||||||
{_address: transcoder for _address in group_addresses} # type: ignore[type-abstract]
|
{
|
||||||
|
_address: transcoder # type: ignore[type-abstract]
|
||||||
|
for _address in group_addresses
|
||||||
|
}
|
||||||
)
|
)
|
||||||
for group_address in group_addresses:
|
for group_address in group_addresses:
|
||||||
if group_address in self._knx_event_callback.group_addresses:
|
if group_address in self._knx_event_callback.group_addresses:
|
||||||
|
|
|
@ -9,8 +9,8 @@ def get_tradable_asset_pairs(kraken_api: KrakenAPI) -> dict[str, str]:
|
||||||
tradable_asset_pairs = {}
|
tradable_asset_pairs = {}
|
||||||
asset_pairs_df = kraken_api.get_tradable_asset_pairs()
|
asset_pairs_df = kraken_api.get_tradable_asset_pairs()
|
||||||
for pair in zip(asset_pairs_df.index.values, asset_pairs_df["wsname"]):
|
for pair in zip(asset_pairs_df.index.values, asset_pairs_df["wsname"]):
|
||||||
if not pair[0].endswith(
|
# Remove darkpools
|
||||||
".d"
|
# https://support.kraken.com/hc/en-us/articles/360001391906-Introducing-the-Kraken-Dark-Pool
|
||||||
): # Remove darkpools https://support.kraken.com/hc/en-us/articles/360001391906-Introducing-the-Kraken-Dark-Pool
|
if not pair[0].endswith(".d"):
|
||||||
tradable_asset_pairs[pair[1]] = pair[0]
|
tradable_asset_pairs[pair[1]] = pair[0]
|
||||||
return tradable_asset_pairs
|
return tradable_asset_pairs
|
||||||
|
|
|
@ -122,7 +122,8 @@ async def get_usb_ports(hass: HomeAssistant) -> dict[str, str]:
|
||||||
ports = await hass.async_add_executor_job(list_ports.comports)
|
ports = await hass.async_add_executor_job(list_ports.comports)
|
||||||
port_descriptions = {}
|
port_descriptions = {}
|
||||||
for port in ports:
|
for port in ports:
|
||||||
# this prevents an issue with usb_device_from_port not working for ports without vid on RPi
|
# this prevents an issue with usb_device_from_port
|
||||||
|
# not working for ports without vid on RPi
|
||||||
if port.vid:
|
if port.vid:
|
||||||
usb_device = usb.usb_device_from_port(port)
|
usb_device = usb.usb_device_from_port(port)
|
||||||
dev_path = usb.get_serial_by_id(usb_device.device)
|
dev_path = usb.get_serial_by_id(usb_device.device)
|
||||||
|
|
|
@ -537,7 +537,12 @@ class LutronCasetaDevice(Entity):
|
||||||
# here. Since it would be a breaking change to change the identifier
|
# here. Since it would be a breaking change to change the identifier
|
||||||
# we are ignoring the type error here until it can be migrated to
|
# we are ignoring the type error here until it can be migrated to
|
||||||
# a string in a future release.
|
# a string in a future release.
|
||||||
identifiers={(DOMAIN, self._handle_none_serial(self.serial))}, # type: ignore[arg-type]
|
identifiers={
|
||||||
|
(
|
||||||
|
DOMAIN,
|
||||||
|
self._handle_none_serial(self.serial), # type: ignore[arg-type]
|
||||||
|
)
|
||||||
|
},
|
||||||
manufacturer=MANUFACTURER,
|
manufacturer=MANUFACTURER,
|
||||||
model=f"{device['model']} ({device['type']})",
|
model=f"{device['model']} ({device['type']})",
|
||||||
name=full_name,
|
name=full_name,
|
||||||
|
|
|
@ -52,7 +52,8 @@ async def async_setup_entry(
|
||||||
.title()
|
.title()
|
||||||
)
|
)
|
||||||
|
|
||||||
# Append the child device name to the end of the parent keypad name to create the entity name
|
# Append the child device name to the end of the parent keypad
|
||||||
|
# name to create the entity name
|
||||||
full_name = f'{parent_device_info.get("name")} {device_name}'
|
full_name = f'{parent_device_info.get("name")} {device_name}'
|
||||||
# Set the device_info to the same as the Parent Keypad
|
# Set the device_info to the same as the Parent Keypad
|
||||||
# The entities will be nested inside the keypad device
|
# The entities will be nested inside the keypad device
|
||||||
|
|
|
@ -314,10 +314,11 @@ class LyricClimate(LyricDeviceEntity, ClimateEntity):
|
||||||
_LOGGER.debug("HVAC mode: %s", hvac_mode)
|
_LOGGER.debug("HVAC mode: %s", hvac_mode)
|
||||||
try:
|
try:
|
||||||
if LYRIC_HVAC_MODES[hvac_mode] == LYRIC_HVAC_MODE_HEAT_COOL:
|
if LYRIC_HVAC_MODES[hvac_mode] == LYRIC_HVAC_MODE_HEAT_COOL:
|
||||||
# If the system is off, turn it to Heat first then to Auto, otherwise it turns to
|
# If the system is off, turn it to Heat first then to Auto,
|
||||||
# Auto briefly and then reverts to Off (perhaps related to heatCoolMode). This is the
|
# otherwise it turns to.
|
||||||
# behavior that happens with the native app as well, so likely a bug in the api itself
|
# Auto briefly and then reverts to Off (perhaps related to
|
||||||
|
# heatCoolMode). This is the behavior that happens with the
|
||||||
|
# native app as well, so likely a bug in the api itself
|
||||||
if HVAC_MODES[self.device.changeableValues.mode] == HVACMode.OFF:
|
if HVAC_MODES[self.device.changeableValues.mode] == HVACMode.OFF:
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"HVAC mode passed to lyric: %s",
|
"HVAC mode passed to lyric: %s",
|
||||||
|
|
|
@ -64,7 +64,8 @@ async def handle_refresh_vehicle_status(
|
||||||
class MazdaButtonEntityDescription(ButtonEntityDescription):
|
class MazdaButtonEntityDescription(ButtonEntityDescription):
|
||||||
"""Describes a Mazda button entity."""
|
"""Describes a Mazda button entity."""
|
||||||
|
|
||||||
# Function to determine whether the vehicle supports this button, given the coordinator data
|
# Function to determine whether the vehicle supports this button,
|
||||||
|
# given the coordinator data
|
||||||
is_supported: Callable[[dict[str, Any]], bool] = lambda data: True
|
is_supported: Callable[[dict[str, Any]], bool] = lambda data: True
|
||||||
|
|
||||||
async_press: Callable[
|
async_press: Callable[
|
||||||
|
|
|
@ -25,7 +25,8 @@ from .const import DATA_CLIENT, DATA_COORDINATOR, DOMAIN
|
||||||
class MazdaSensorRequiredKeysMixin:
|
class MazdaSensorRequiredKeysMixin:
|
||||||
"""Mixin for required keys."""
|
"""Mixin for required keys."""
|
||||||
|
|
||||||
# Function to determine the value for this sensor, given the coordinator data and the configured unit system
|
# Function to determine the value for this sensor, given the coordinator data
|
||||||
|
# and the configured unit system
|
||||||
value: Callable[[dict[str, Any]], StateType]
|
value: Callable[[dict[str, Any]], StateType]
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,7 +36,8 @@ class MazdaSensorEntityDescription(
|
||||||
):
|
):
|
||||||
"""Describes a Mazda sensor entity."""
|
"""Describes a Mazda sensor entity."""
|
||||||
|
|
||||||
# Function to determine whether the vehicle supports this sensor, given the coordinator data
|
# Function to determine whether the vehicle supports this sensor,
|
||||||
|
# given the coordinator data
|
||||||
is_supported: Callable[[dict[str, Any]], bool] = lambda data: True
|
is_supported: Callable[[dict[str, Any]], bool] = lambda data: True
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -144,10 +144,14 @@ def setup_mysensors_platform(
|
||||||
) -> list[MySensorsDevice] | None:
|
) -> list[MySensorsDevice] | None:
|
||||||
"""Set up a MySensors platform.
|
"""Set up a MySensors platform.
|
||||||
|
|
||||||
Sets up a bunch of instances of a single platform that is supported by this integration.
|
Sets up a bunch of instances of a single platform that is supported by this
|
||||||
The function is given a list of device ids, each one describing an instance to set up.
|
integration.
|
||||||
The function is also given a class.
|
|
||||||
A new instance of the class is created for every device id, and the device id is given to the constructor of the class
|
The function is given a list of device ids, each one describing an instance
|
||||||
|
to set up. The function is also given a class.
|
||||||
|
|
||||||
|
A new instance of the class is created for every device id, and the device
|
||||||
|
id is given to the constructor of the class.
|
||||||
"""
|
"""
|
||||||
if device_args is None:
|
if device_args is None:
|
||||||
device_args = ()
|
device_args = ()
|
||||||
|
|
|
@ -56,12 +56,15 @@ GatewayId = str
|
||||||
# a unique id generated by config_flow.py and stored in the ConfigEntry as the entry id.
|
# a unique id generated by config_flow.py and stored in the ConfigEntry as the entry id.
|
||||||
|
|
||||||
DevId = tuple[GatewayId, int, int, int]
|
DevId = tuple[GatewayId, int, int, int]
|
||||||
# describes the backend of a hass entity. Contents are: GatewayId, node_id, child_id, v_type as int
|
# describes the backend of a hass entity.
|
||||||
|
# Contents are: GatewayId, node_id, child_id, v_type as int
|
||||||
#
|
#
|
||||||
# The string version of v_type can be looked up in the enum gateway.const.SetReq of the appropriate BaseAsyncGateway
|
# The string version of v_type can be looked up in the enum gateway.const.SetReq
|
||||||
|
# of the appropriate BaseAsyncGateway
|
||||||
# Home Assistant Entities are quite limited and only ever do one thing.
|
# Home Assistant Entities are quite limited and only ever do one thing.
|
||||||
# MySensors Nodes have multiple child_ids each with a s_type several associated v_types
|
# MySensors Nodes have multiple child_ids each with a s_type several associated v_types
|
||||||
# The MySensors integration brings these together by creating an entity for every v_type of every child_id of every node.
|
# The MySensors integration brings these together by creating an entity for every v_type
|
||||||
|
# of every child_id of every node.
|
||||||
# The DevId tuple perfectly captures this.
|
# The DevId tuple perfectly captures this.
|
||||||
|
|
||||||
BINARY_SENSOR_TYPES: dict[SensorType, set[ValueType]] = {
|
BINARY_SENSOR_TYPES: dict[SensorType, set[ValueType]] = {
|
||||||
|
|
|
@ -53,7 +53,8 @@ class MySensorsDevice(ABC):
|
||||||
self.gateway: BaseAsyncGateway = gateway
|
self.gateway: BaseAsyncGateway = gateway
|
||||||
self.node_id: int = node_id
|
self.node_id: int = node_id
|
||||||
self.child_id: int = child_id
|
self.child_id: int = child_id
|
||||||
self.value_type: int = value_type # value_type as int. string variant can be looked up in gateway consts
|
# value_type as int. string variant can be looked up in gateway consts
|
||||||
|
self.value_type: int = value_type
|
||||||
self.child_type = self._child.type
|
self.child_type = self._child.type
|
||||||
self._values: dict[int, Any] = {}
|
self._values: dict[int, Any] = {}
|
||||||
self._debouncer: Debouncer | None = None
|
self._debouncer: Debouncer | None = None
|
||||||
|
|
|
@ -28,7 +28,7 @@ class ConfigEntryAuth(pybotvac.OAuthSession): # type: ignore[misc]
|
||||||
super().__init__(self.session.token, vendor=pybotvac.Neato())
|
super().__init__(self.session.token, vendor=pybotvac.Neato())
|
||||||
|
|
||||||
def refresh_tokens(self) -> str:
|
def refresh_tokens(self) -> str:
|
||||||
"""Refresh and return new Neato Botvac tokens using Home Assistant OAuth2 session."""
|
"""Refresh and return new Neato Botvac tokens."""
|
||||||
run_coroutine_threadsafe(
|
run_coroutine_threadsafe(
|
||||||
self.session.async_ensure_token_valid(), self.hass.loop
|
self.session.async_ensure_token_valid(), self.hass.loop
|
||||||
).result()
|
).result()
|
||||||
|
@ -39,7 +39,8 @@ class ConfigEntryAuth(pybotvac.OAuthSession): # type: ignore[misc]
|
||||||
class NeatoImplementation(AuthImplementation):
|
class NeatoImplementation(AuthImplementation):
|
||||||
"""Neato implementation of LocalOAuth2Implementation.
|
"""Neato implementation of LocalOAuth2Implementation.
|
||||||
|
|
||||||
We need this class because we have to add client_secret and scope to the authorization request.
|
We need this class because we have to add client_secret
|
||||||
|
and scope to the authorization request.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -229,7 +229,8 @@ class NSDepartureSensor(SensorEntity):
|
||||||
self._trips = None
|
self._trips = None
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set the search parameter to search from a specific trip time or to just search for next trip.
|
# Set the search parameter to search from a specific trip time
|
||||||
|
# or to just search for next trip.
|
||||||
if self._time:
|
if self._time:
|
||||||
trip_time = (
|
trip_time = (
|
||||||
datetime.today()
|
datetime.today()
|
||||||
|
|
|
@ -115,10 +115,11 @@ class NestCamera(Camera):
|
||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
"""Return True if entity is available."""
|
"""Return True if entity is available."""
|
||||||
# Cameras are marked unavailable on stream errors in #54659 however nest streams have
|
# Cameras are marked unavailable on stream errors in #54659 however nest
|
||||||
# a high error rate (#60353). Given nest streams are so flaky, marking the stream
|
# streams have a high error rate (#60353). Given nest streams are so flaky,
|
||||||
# unavailable has other side effects like not showing the camera image which sometimes
|
# marking the stream unavailable has other side effects like not showing
|
||||||
# are still able to work. Until the streams are fixed, just leave the streams as available.
|
# the camera image which sometimes are still able to work. Until the
|
||||||
|
# streams are fixed, just leave the streams as available.
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def stream_source(self) -> str | None:
|
async def stream_source(self) -> str | None:
|
||||||
|
|
|
@ -363,8 +363,8 @@ class NestFlowHandler(
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Verify any last pre-requisites before sending user through OAuth flow."""
|
"""Verify any last pre-requisites before sending user through OAuth flow."""
|
||||||
if user_input is None and self._upgrade:
|
if user_input is None and self._upgrade:
|
||||||
# During app auth upgrade we need the user to update their device access project
|
# During app auth upgrade we need the user to update their device
|
||||||
# before we redirect to the authentication flow.
|
# access project before we redirect to the authentication flow.
|
||||||
return await self.async_step_device_project_upgrade()
|
return await self.async_step_device_project_upgrade()
|
||||||
return await super().async_step_auth(user_input)
|
return await super().async_step_auth(user_input)
|
||||||
|
|
||||||
|
|
|
@ -357,10 +357,11 @@ class OnkyoDevice(MediaPlayerEntity):
|
||||||
def set_volume_level(self, volume: float) -> None:
|
def set_volume_level(self, volume: float) -> None:
|
||||||
"""Set volume level, input is range 0..1.
|
"""Set volume level, input is range 0..1.
|
||||||
|
|
||||||
However full volume on the amp is usually far too loud so allow the user to specify the upper range
|
However full volume on the amp is usually far too loud so allow the user to
|
||||||
with CONF_MAX_VOLUME. we change as per max_volume set by user. This means that if max volume is 80 then full
|
specify the upper range with CONF_MAX_VOLUME. We change as per max_volume
|
||||||
volume in HA will give 80% volume on the receiver. Then we convert
|
set by user. This means that if max volume is 80 then full volume in HA will
|
||||||
that to the correct scale for the receiver.
|
give 80% volume on the receiver. Then we convert that to the correct scale
|
||||||
|
for the receiver.
|
||||||
"""
|
"""
|
||||||
# HA_VOL * (MAX VOL / 100) * MAX_RECEIVER_VOL
|
# HA_VOL * (MAX VOL / 100) * MAX_RECEIVER_VOL
|
||||||
self.command(
|
self.command(
|
||||||
|
@ -524,10 +525,11 @@ class OnkyoDeviceZone(OnkyoDevice):
|
||||||
def set_volume_level(self, volume: float) -> None:
|
def set_volume_level(self, volume: float) -> None:
|
||||||
"""Set volume level, input is range 0..1.
|
"""Set volume level, input is range 0..1.
|
||||||
|
|
||||||
However full volume on the amp is usually far too loud so allow the user to specify the upper range
|
However full volume on the amp is usually far too loud so allow the user to
|
||||||
with CONF_MAX_VOLUME. we change as per max_volume set by user. This means that if max volume is 80 then full
|
specify the upper range with CONF_MAX_VOLUME. We change as per max_volume
|
||||||
volume in HA will give 80% volume on the receiver. Then we convert
|
set by user. This means that if max volume is 80 then full volume in HA
|
||||||
that to the correct scale for the receiver.
|
will give 80% volume on the receiver. Then we convert that to the correct
|
||||||
|
scale for the receiver.
|
||||||
"""
|
"""
|
||||||
# HA_VOL * (MAX VOL / 100) * MAX_RECEIVER_VOL
|
# HA_VOL * (MAX VOL / 100) * MAX_RECEIVER_VOL
|
||||||
self.command(
|
self.command(
|
||||||
|
|
|
@ -53,11 +53,12 @@ class OverkizEntity(CoordinatorEntity[OverkizDataUpdateCoordinator]):
|
||||||
|
|
||||||
def generate_device_info(self) -> DeviceInfo:
|
def generate_device_info(self) -> DeviceInfo:
|
||||||
"""Return device registry information for this entity."""
|
"""Return device registry information for this entity."""
|
||||||
# Some devices, such as the Smart Thermostat have several devices in one physical device,
|
# Some devices, such as the Smart Thermostat have several devices
|
||||||
# with same device url, terminated by '#' and a number.
|
# in one physical device, with same device url, terminated by '#' and a number.
|
||||||
# In this case, we use the base device url as the device identifier.
|
# In this case, we use the base device url as the device identifier.
|
||||||
if self.is_sub_device:
|
if self.is_sub_device:
|
||||||
# Only return the url of the base device, to inherit device name and model from parent device.
|
# Only return the url of the base device, to inherit device name
|
||||||
|
# and model from parent device.
|
||||||
return {
|
return {
|
||||||
"identifiers": {(DOMAIN, self.executor.base_device_url)},
|
"identifiers": {(DOMAIN, self.executor.base_device_url)},
|
||||||
}
|
}
|
||||||
|
@ -114,5 +115,6 @@ class OverkizDescriptiveEntity(OverkizEntity):
|
||||||
self._attr_unique_id = f"{super().unique_id}-{self.entity_description.key}"
|
self._attr_unique_id = f"{super().unique_id}-{self.entity_description.key}"
|
||||||
|
|
||||||
if self.is_sub_device:
|
if self.is_sub_device:
|
||||||
# In case of sub device, use the provided label and append the name of the type of entity
|
# In case of sub device, use the provided label
|
||||||
|
# and append the name of the type of entity
|
||||||
self._attr_name = f"{self.device.label} {description.name}"
|
self._attr_name = f"{self.device.label} {description.name}"
|
||||||
|
|
|
@ -86,7 +86,9 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
|
||||||
icon="mdi:shower-head",
|
icon="mdi:shower-head",
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
# V40 is measured in litres (L) and shows the amount of warm (mixed) water with a temperature of 40 C, which can be drained from a switched off electric water heater.
|
# V40 is measured in litres (L) and shows the amount of warm (mixed) water
|
||||||
|
# with a temperature of 40 C, which can be drained from
|
||||||
|
# a switched off electric water heater.
|
||||||
OverkizSensorDescription(
|
OverkizSensorDescription(
|
||||||
key=OverkizState.CORE_V40_WATER_VOLUME_ESTIMATION,
|
key=OverkizState.CORE_V40_WATER_VOLUME_ESTIMATION,
|
||||||
name="Water volume estimation at 40 °C",
|
name="Water volume estimation at 40 °C",
|
||||||
|
@ -150,7 +152,8 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
|
||||||
key=OverkizState.CORE_LUMINANCE,
|
key=OverkizState.CORE_LUMINANCE,
|
||||||
name="Luminance",
|
name="Luminance",
|
||||||
device_class=SensorDeviceClass.ILLUMINANCE,
|
device_class=SensorDeviceClass.ILLUMINANCE,
|
||||||
native_unit_of_measurement=LIGHT_LUX, # core:MeasuredValueType = core:LuminanceInLux
|
# core:MeasuredValueType = core:LuminanceInLux
|
||||||
|
native_unit_of_measurement=LIGHT_LUX,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
# ElectricitySensor/CumulativeElectricPowerConsumptionSensor
|
# ElectricitySensor/CumulativeElectricPowerConsumptionSensor
|
||||||
|
@ -158,21 +161,27 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
|
||||||
key=OverkizState.CORE_ELECTRIC_ENERGY_CONSUMPTION,
|
key=OverkizState.CORE_ELECTRIC_ENERGY_CONSUMPTION,
|
||||||
name="Electric energy consumption",
|
name="Electric energy consumption",
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh (not for modbus:YutakiV2DHWElectricalEnergyConsumptionComponent)
|
# core:MeasuredValueType = core:ElectricalEnergyInWh
|
||||||
state_class=SensorStateClass.TOTAL_INCREASING, # core:MeasurementCategory attribute = electric/overall
|
# (not for modbus:YutakiV2DHWElectricalEnergyConsumptionComponent)
|
||||||
|
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||||
|
# core:MeasurementCategory attribute = electric/overall
|
||||||
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
),
|
),
|
||||||
OverkizSensorDescription(
|
OverkizSensorDescription(
|
||||||
key=OverkizState.CORE_ELECTRIC_POWER_CONSUMPTION,
|
key=OverkizState.CORE_ELECTRIC_POWER_CONSUMPTION,
|
||||||
name="Electric power consumption",
|
name="Electric power consumption",
|
||||||
device_class=SensorDeviceClass.POWER,
|
device_class=SensorDeviceClass.POWER,
|
||||||
native_unit_of_measurement=UnitOfPower.WATT, # core:MeasuredValueType = core:ElectricalEnergyInWh (not for modbus:YutakiV2DHWElectricalEnergyConsumptionComponent)
|
# core:MeasuredValueType = core:ElectricalEnergyInWh
|
||||||
|
# (not for modbus:YutakiV2DHWElectricalEnergyConsumptionComponent)
|
||||||
|
native_unit_of_measurement=UnitOfPower.WATT,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
OverkizSensorDescription(
|
OverkizSensorDescription(
|
||||||
key=OverkizState.CORE_CONSUMPTION_TARIFF1,
|
key=OverkizState.CORE_CONSUMPTION_TARIFF1,
|
||||||
name="Consumption tariff 1",
|
name="Consumption tariff 1",
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh
|
# core:MeasuredValueType = core:ElectricalEnergyInWh
|
||||||
|
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
|
@ -180,7 +189,8 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
|
||||||
key=OverkizState.CORE_CONSUMPTION_TARIFF2,
|
key=OverkizState.CORE_CONSUMPTION_TARIFF2,
|
||||||
name="Consumption tariff 2",
|
name="Consumption tariff 2",
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh
|
# core:MeasuredValueType = core:ElectricalEnergyInWh
|
||||||
|
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
|
@ -188,7 +198,8 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
|
||||||
key=OverkizState.CORE_CONSUMPTION_TARIFF3,
|
key=OverkizState.CORE_CONSUMPTION_TARIFF3,
|
||||||
name="Consumption tariff 3",
|
name="Consumption tariff 3",
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh
|
# core:MeasuredValueType = core:ElectricalEnergyInWh
|
||||||
|
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
|
@ -196,7 +207,8 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
|
||||||
key=OverkizState.CORE_CONSUMPTION_TARIFF4,
|
key=OverkizState.CORE_CONSUMPTION_TARIFF4,
|
||||||
name="Consumption tariff 4",
|
name="Consumption tariff 4",
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh
|
# core:MeasuredValueType = core:ElectricalEnergyInWh
|
||||||
|
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
|
@ -204,7 +216,8 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
|
||||||
key=OverkizState.CORE_CONSUMPTION_TARIFF5,
|
key=OverkizState.CORE_CONSUMPTION_TARIFF5,
|
||||||
name="Consumption tariff 5",
|
name="Consumption tariff 5",
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh
|
# core:MeasuredValueType = core:ElectricalEnergyInWh
|
||||||
|
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
|
@ -212,7 +225,8 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
|
||||||
key=OverkizState.CORE_CONSUMPTION_TARIFF6,
|
key=OverkizState.CORE_CONSUMPTION_TARIFF6,
|
||||||
name="Consumption tariff 6",
|
name="Consumption tariff 6",
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh
|
# core:MeasuredValueType = core:ElectricalEnergyInWh
|
||||||
|
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
|
@ -220,7 +234,8 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
|
||||||
key=OverkizState.CORE_CONSUMPTION_TARIFF7,
|
key=OverkizState.CORE_CONSUMPTION_TARIFF7,
|
||||||
name="Consumption tariff 7",
|
name="Consumption tariff 7",
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh
|
# core:MeasuredValueType = core:ElectricalEnergyInWh
|
||||||
|
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
|
@ -228,7 +243,8 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
|
||||||
key=OverkizState.CORE_CONSUMPTION_TARIFF8,
|
key=OverkizState.CORE_CONSUMPTION_TARIFF8,
|
||||||
name="Consumption tariff 8",
|
name="Consumption tariff 8",
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh
|
# core:MeasuredValueType = core:ElectricalEnergyInWh
|
||||||
|
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
|
@ -236,7 +252,8 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
|
||||||
key=OverkizState.CORE_CONSUMPTION_TARIFF9,
|
key=OverkizState.CORE_CONSUMPTION_TARIFF9,
|
||||||
name="Consumption tariff 9",
|
name="Consumption tariff 9",
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh
|
# core:MeasuredValueType = core:ElectricalEnergyInWh
|
||||||
|
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
|
@ -246,7 +263,8 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
|
||||||
name="Relative humidity",
|
name="Relative humidity",
|
||||||
native_value=lambda value: round(cast(float, value), 2),
|
native_value=lambda value: round(cast(float, value), 2),
|
||||||
device_class=SensorDeviceClass.HUMIDITY,
|
device_class=SensorDeviceClass.HUMIDITY,
|
||||||
native_unit_of_measurement=PERCENTAGE, # core:MeasuredValueType = core:RelativeValueInPercentage
|
# core:MeasuredValueType = core:RelativeValueInPercentage
|
||||||
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
# TemperatureSensor/TemperatureSensor
|
# TemperatureSensor/TemperatureSensor
|
||||||
|
@ -255,7 +273,8 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
|
||||||
name="Temperature",
|
name="Temperature",
|
||||||
native_value=lambda value: round(cast(float, value), 2),
|
native_value=lambda value: round(cast(float, value), 2),
|
||||||
device_class=SensorDeviceClass.TEMPERATURE,
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS, # core:MeasuredValueType = core:TemperatureInCelcius
|
# core:MeasuredValueType = core:TemperatureInCelcius
|
||||||
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
# WeatherSensor/WeatherForecastSensor
|
# WeatherSensor/WeatherForecastSensor
|
||||||
|
@ -478,7 +497,8 @@ class OverkizHomeKitSetupCodeSensor(OverkizEntity, SensorEntity):
|
||||||
def device_info(self) -> DeviceInfo:
|
def device_info(self) -> DeviceInfo:
|
||||||
"""Return device registry information for this entity."""
|
"""Return device registry information for this entity."""
|
||||||
# By default this sensor will be listed at a virtual HomekitStack device,
|
# By default this sensor will be listed at a virtual HomekitStack device,
|
||||||
# but it makes more sense to show this at the gateway device in the entity registry.
|
# but it makes more sense to show this at the gateway device
|
||||||
|
# in the entity registry.
|
||||||
return {
|
return {
|
||||||
"identifiers": {(DOMAIN, self.executor.get_gateway_id())},
|
"identifiers": {(DOMAIN, self.executor.get_gateway_id())},
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,8 +68,8 @@ class PandoraMediaPlayer(MediaPlayerEntity):
|
||||||
"""A media player that uses the Pianobar interface to Pandora."""
|
"""A media player that uses the Pianobar interface to Pandora."""
|
||||||
|
|
||||||
_attr_media_content_type = MediaType.MUSIC
|
_attr_media_content_type = MediaType.MUSIC
|
||||||
# MediaPlayerEntityFeature.VOLUME_SET is close to available but we need volume up/down
|
# MediaPlayerEntityFeature.VOLUME_SET is close to available
|
||||||
# controls in the GUI.
|
# but we need volume up/down controls in the GUI.
|
||||||
_attr_supported_features = (
|
_attr_supported_features = (
|
||||||
MediaPlayerEntityFeature.PAUSE
|
MediaPlayerEntityFeature.PAUSE
|
||||||
| MediaPlayerEntityFeature.TURN_ON
|
| MediaPlayerEntityFeature.TURN_ON
|
||||||
|
|
|
@ -77,7 +77,8 @@ class PilightLight(PilightBaseDevice, LightEntity):
|
||||||
# Calculate pilight brightness (as a range of 0 to 15)
|
# Calculate pilight brightness (as a range of 0 to 15)
|
||||||
# By creating a percentage
|
# By creating a percentage
|
||||||
percentage = self._brightness / 255
|
percentage = self._brightness / 255
|
||||||
# Then calculate the dimmer range (aka amount of available brightness steps).
|
# Then calculate the dimmer range (aka amount
|
||||||
|
# of available brightness steps).
|
||||||
dimrange = self._dimlevel_max - self._dimlevel_min
|
dimrange = self._dimlevel_max - self._dimlevel_min
|
||||||
# Finally calculate the pilight brightness.
|
# Finally calculate the pilight brightness.
|
||||||
# We add dimlevel_min back in to ensure the minimum is always reached.
|
# We add dimlevel_min back in to ensure the minimum is always reached.
|
||||||
|
|
|
@ -122,7 +122,8 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):
|
||||||
# When control_state is present, prefer this data
|
# When control_state is present, prefer this data
|
||||||
if (control_state := self.device.get("control_state")) == "cooling":
|
if (control_state := self.device.get("control_state")) == "cooling":
|
||||||
return HVACAction.COOLING
|
return HVACAction.COOLING
|
||||||
# Support preheating state as heating, until preheating is added as a separate state
|
# Support preheating state as heating,
|
||||||
|
# until preheating is added as a separate state
|
||||||
if control_state in ["heating", "preheating"]:
|
if control_state in ["heating", "preheating"]:
|
||||||
return HVACAction.HEATING
|
return HVACAction.HEATING
|
||||||
if control_state == "off":
|
if control_state == "off":
|
||||||
|
|
|
@ -65,7 +65,8 @@ def migrate_sensor_entities(
|
||||||
"""Migrate Sensors if needed."""
|
"""Migrate Sensors if needed."""
|
||||||
ent_reg = er.async_get(hass)
|
ent_reg = er.async_get(hass)
|
||||||
|
|
||||||
# Migrating opentherm_outdoor_temperature to opentherm_outdoor_air_temperature sensor
|
# Migrating opentherm_outdoor_temperature
|
||||||
|
# to opentherm_outdoor_air_temperature sensor
|
||||||
for device_id, device in coordinator.data.devices.items():
|
for device_id, device in coordinator.data.devices.items():
|
||||||
if device.get("dev_class") != "heater_central":
|
if device.get("dev_class") != "heater_central":
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -96,7 +96,8 @@ class PowerwallDataManager:
|
||||||
raise UpdateFailed("Unable to fetch data from powerwall") from err
|
raise UpdateFailed("Unable to fetch data from powerwall") from err
|
||||||
except MissingAttributeError as err:
|
except MissingAttributeError as err:
|
||||||
_LOGGER.error("The powerwall api has changed: %s", str(err))
|
_LOGGER.error("The powerwall api has changed: %s", str(err))
|
||||||
# The error might include some important information about what exactly changed.
|
# The error might include some important information
|
||||||
|
# about what exactly changed.
|
||||||
persistent_notification.create(
|
persistent_notification.create(
|
||||||
self.hass, API_CHANGED_ERROR_BODY, API_CHANGED_TITLE
|
self.hass, API_CHANGED_ERROR_BODY, API_CHANGED_TITLE
|
||||||
)
|
)
|
||||||
|
@ -109,7 +110,8 @@ class PowerwallDataManager:
|
||||||
if self.password is None:
|
if self.password is None:
|
||||||
raise ConfigEntryAuthFailed from err
|
raise ConfigEntryAuthFailed from err
|
||||||
_LOGGER.debug("Access denied, trying to reauthenticate")
|
_LOGGER.debug("Access denied, trying to reauthenticate")
|
||||||
# there is still an attempt left to authenticate, so we continue in the loop
|
# there is still an attempt left to authenticate,
|
||||||
|
# so we continue in the loop
|
||||||
except APIError as err:
|
except APIError as err:
|
||||||
raise UpdateFailed(f"Updated failed due to {err}, will retry") from err
|
raise UpdateFailed(f"Updated failed due to {err}, will retry") from err
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -262,4 +262,4 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
|
|
||||||
|
|
||||||
class WrongVersion(exceptions.HomeAssistantError):
|
class WrongVersion(exceptions.HomeAssistantError):
|
||||||
"""Error to indicate the powerwall uses a software version we cannot interact with."""
|
"""Error indicating we cannot interact with the powerwall software version."""
|
||||||
|
|
|
@ -309,7 +309,10 @@ class ProxmoxClient:
|
||||||
self._connection_start_time = None
|
self._connection_start_time = None
|
||||||
|
|
||||||
def build_client(self):
|
def build_client(self):
|
||||||
"""Construct the ProxmoxAPI client. Allows inserting the realm within the `user` value."""
|
"""Construct the ProxmoxAPI client.
|
||||||
|
|
||||||
|
Allows inserting the realm within the `user` value.
|
||||||
|
"""
|
||||||
|
|
||||||
if "@" in self._user:
|
if "@" in self._user:
|
||||||
user_id = self._user
|
user_id = self._user
|
||||||
|
|
|
@ -54,7 +54,8 @@ COUNTRYCODE_NAMES = {
|
||||||
"LU": "Luxembourg",
|
"LU": "Luxembourg",
|
||||||
"MT": "Malta",
|
"MT": "Malta",
|
||||||
"MX": "Mexico",
|
"MX": "Mexico",
|
||||||
"MY": "Maylasia", # spelling error compatibility with pyps4_2ndscreen.media_art.COUNTRIES
|
# spelling error compatibility with pyps4_2ndscreen.media_art.COUNTRIES
|
||||||
|
"MY": "Maylasia",
|
||||||
"NI": "Nicaragua",
|
"NI": "Nicaragua",
|
||||||
"NL": "Nederland",
|
"NL": "Nederland",
|
||||||
"NO": "Norway",
|
"NO": "Norway",
|
||||||
|
|
|
@ -142,7 +142,10 @@ class QswBinarySensor(QswSensorEntity, BinarySensorEntity):
|
||||||
super().__init__(coordinator, entry, type_id)
|
super().__init__(coordinator, entry, type_id)
|
||||||
|
|
||||||
self._attr_name = f"{self.product} {description.name}"
|
self._attr_name = f"{self.product} {description.name}"
|
||||||
self._attr_unique_id = f"{entry.unique_id}_{description.key}{description.sep_key}{description.subkey}"
|
self._attr_unique_id = (
|
||||||
|
f"{entry.unique_id}_{description.key}"
|
||||||
|
f"{description.sep_key}{description.subkey}"
|
||||||
|
)
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._async_update_attrs()
|
self._async_update_attrs()
|
||||||
|
|
||||||
|
|
|
@ -364,7 +364,10 @@ class QswSensor(QswSensorEntity, SensorEntity):
|
||||||
super().__init__(coordinator, entry, type_id)
|
super().__init__(coordinator, entry, type_id)
|
||||||
|
|
||||||
self._attr_name = f"{self.product} {description.name}"
|
self._attr_name = f"{self.product} {description.name}"
|
||||||
self._attr_unique_id = f"{entry.unique_id}_{description.key}{description.sep_key}{description.subkey}"
|
self._attr_unique_id = (
|
||||||
|
f"{entry.unique_id}_{description.key}"
|
||||||
|
f"{description.sep_key}{description.subkey}"
|
||||||
|
)
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._async_update_attrs()
|
self._async_update_attrs()
|
||||||
|
|
||||||
|
|
|
@ -533,7 +533,8 @@ def _apply_update( # noqa: C901
|
||||||
_drop_index(session_maker, "states", "states__state_changes")
|
_drop_index(session_maker, "states", "states__state_changes")
|
||||||
_drop_index(session_maker, "states", "states__significant_changes")
|
_drop_index(session_maker, "states", "states__significant_changes")
|
||||||
_drop_index(session_maker, "states", "ix_states_entity_id_created")
|
_drop_index(session_maker, "states", "ix_states_entity_id_created")
|
||||||
# This used to create ix_states_entity_id_last_updated, but it was removed in version 32
|
# This used to create ix_states_entity_id_last_updated,
|
||||||
|
# but it was removed in version 32
|
||||||
elif new_version == 5:
|
elif new_version == 5:
|
||||||
# Create supporting index for States.event_id foreign key
|
# Create supporting index for States.event_id foreign key
|
||||||
_create_index(session_maker, "states", "ix_states_event_id")
|
_create_index(session_maker, "states", "ix_states_event_id")
|
||||||
|
@ -544,21 +545,25 @@ def _apply_update( # noqa: C901
|
||||||
["context_id CHARACTER(36)", "context_user_id CHARACTER(36)"],
|
["context_id CHARACTER(36)", "context_user_id CHARACTER(36)"],
|
||||||
)
|
)
|
||||||
_create_index(session_maker, "events", "ix_events_context_id")
|
_create_index(session_maker, "events", "ix_events_context_id")
|
||||||
# This used to create ix_events_context_user_id, but it was removed in version 28
|
# This used to create ix_events_context_user_id,
|
||||||
|
# but it was removed in version 28
|
||||||
_add_columns(
|
_add_columns(
|
||||||
session_maker,
|
session_maker,
|
||||||
"states",
|
"states",
|
||||||
["context_id CHARACTER(36)", "context_user_id CHARACTER(36)"],
|
["context_id CHARACTER(36)", "context_user_id CHARACTER(36)"],
|
||||||
)
|
)
|
||||||
_create_index(session_maker, "states", "ix_states_context_id")
|
_create_index(session_maker, "states", "ix_states_context_id")
|
||||||
# This used to create ix_states_context_user_id, but it was removed in version 28
|
# This used to create ix_states_context_user_id,
|
||||||
|
# but it was removed in version 28
|
||||||
elif new_version == 7:
|
elif new_version == 7:
|
||||||
# There used to be a ix_states_entity_id index here, but it was removed in later schema
|
# There used to be a ix_states_entity_id index here,
|
||||||
|
# but it was removed in later schema
|
||||||
pass
|
pass
|
||||||
elif new_version == 8:
|
elif new_version == 8:
|
||||||
_add_columns(session_maker, "events", ["context_parent_id CHARACTER(36)"])
|
_add_columns(session_maker, "events", ["context_parent_id CHARACTER(36)"])
|
||||||
_add_columns(session_maker, "states", ["old_state_id INTEGER"])
|
_add_columns(session_maker, "states", ["old_state_id INTEGER"])
|
||||||
# This used to create ix_events_context_parent_id, but it was removed in version 28
|
# This used to create ix_events_context_parent_id,
|
||||||
|
# but it was removed in version 28
|
||||||
elif new_version == 9:
|
elif new_version == 9:
|
||||||
# We now get the context from events with a join
|
# We now get the context from events with a join
|
||||||
# since its always there on state_changed events
|
# since its always there on state_changed events
|
||||||
|
@ -576,7 +581,8 @@ def _apply_update( # noqa: C901
|
||||||
# Redundant keys on composite index:
|
# Redundant keys on composite index:
|
||||||
# We already have ix_states_entity_id_last_updated
|
# We already have ix_states_entity_id_last_updated
|
||||||
_drop_index(session_maker, "states", "ix_states_entity_id")
|
_drop_index(session_maker, "states", "ix_states_entity_id")
|
||||||
# This used to create ix_events_event_type_time_fired, but it was removed in version 32
|
# This used to create ix_events_event_type_time_fired,
|
||||||
|
# but it was removed in version 32
|
||||||
_drop_index(session_maker, "events", "ix_events_event_type")
|
_drop_index(session_maker, "events", "ix_events_event_type")
|
||||||
elif new_version == 10:
|
elif new_version == 10:
|
||||||
# Now done in step 11
|
# Now done in step 11
|
||||||
|
|
|
@ -280,7 +280,8 @@ class SamsungTVLegacyBridge(SamsungTVBridge):
|
||||||
CONF_HOST: self.host,
|
CONF_HOST: self.host,
|
||||||
CONF_METHOD: self.method,
|
CONF_METHOD: self.method,
|
||||||
CONF_PORT: None,
|
CONF_PORT: None,
|
||||||
# We need this high timeout because waiting for auth popup is just an open socket
|
# We need this high timeout because waiting for auth popup
|
||||||
|
# is just an open socket
|
||||||
CONF_TIMEOUT: TIMEOUT_REQUEST,
|
CONF_TIMEOUT: TIMEOUT_REQUEST,
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
|
@ -310,7 +311,8 @@ class SamsungTVLegacyBridge(SamsungTVBridge):
|
||||||
LOGGER.debug("Create SamsungTVLegacyBridge for %s", self.host)
|
LOGGER.debug("Create SamsungTVLegacyBridge for %s", self.host)
|
||||||
self._remote = Remote(self.config.copy())
|
self._remote = Remote(self.config.copy())
|
||||||
# This is only happening when the auth was switched to DENY
|
# This is only happening when the auth was switched to DENY
|
||||||
# A removed auth will lead to socket timeout because waiting for auth popup is just an open socket
|
# A removed auth will lead to socket timeout because waiting
|
||||||
|
# for auth popup is just an open socket
|
||||||
except AccessDenied:
|
except AccessDenied:
|
||||||
self._notify_reauth_callback()
|
self._notify_reauth_callback()
|
||||||
raise
|
raise
|
||||||
|
@ -483,7 +485,8 @@ class SamsungTVWSBridge(
|
||||||
CONF_HOST: self.host,
|
CONF_HOST: self.host,
|
||||||
CONF_METHOD: self.method,
|
CONF_METHOD: self.method,
|
||||||
CONF_PORT: self.port,
|
CONF_PORT: self.port,
|
||||||
# We need this high timeout because waiting for auth popup is just an open socket
|
# We need this high timeout because waiting for auth popup
|
||||||
|
# is just an open socket
|
||||||
CONF_TIMEOUT: TIMEOUT_REQUEST,
|
CONF_TIMEOUT: TIMEOUT_REQUEST,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -494,7 +494,7 @@ class ScriptEntity(ToggleEntity, RestoreEntity):
|
||||||
self.script.last_triggered = parse_datetime(last_triggered)
|
self.script.last_triggered = parse_datetime(last_triggered)
|
||||||
|
|
||||||
async def async_will_remove_from_hass(self):
|
async def async_will_remove_from_hass(self):
|
||||||
"""Stop script and remove service when it will be removed from Home Assistant."""
|
"""Stop script and remove service when it will be removed from HA."""
|
||||||
await self.script.async_stop()
|
await self.script.async_stop()
|
||||||
|
|
||||||
# remove service
|
# remove service
|
||||||
|
|
|
@ -117,9 +117,13 @@ class SIABaseEntity(RestoreEntity):
|
||||||
def async_handle_event(self, sia_event: SIAEvent) -> None:
|
def async_handle_event(self, sia_event: SIAEvent) -> None:
|
||||||
"""Listen to dispatcher events for this port and account and update state and attributes.
|
"""Listen to dispatcher events for this port and account and update state and attributes.
|
||||||
|
|
||||||
If the event is for either the zone or the 0 zone (hub zone), then handle it further.
|
If the event is for either the zone or the 0 zone (hub zone),
|
||||||
If the event had a code that was relevant for the entity, then update the attributes.
|
then handle it further.
|
||||||
If the event had a code that was relevant or it was a availability event then update the availability and schedule the next unavailability check.
|
|
||||||
|
If the event had a code that was relevant for the entity,
|
||||||
|
then update the attributes.
|
||||||
|
If the event had a code that was relevant or it was a availability event
|
||||||
|
then update the availability and schedule the next unavailability check.
|
||||||
"""
|
"""
|
||||||
_LOGGER.debug("Received event: %s", sia_event)
|
_LOGGER.debug("Received event: %s", sia_event)
|
||||||
if int(sia_event.ri) not in (self.zone, SIA_HUB_ZONE):
|
if int(sia_event.ri) not in (self.zone, SIA_HUB_ZONE):
|
||||||
|
|
|
@ -224,7 +224,9 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity):
|
||||||
self._attr_extra_state_attributes.update(
|
self._attr_extra_state_attributes.update(
|
||||||
{
|
{
|
||||||
ATTR_ALARM_DURATION: self._system.alarm_duration,
|
ATTR_ALARM_DURATION: self._system.alarm_duration,
|
||||||
ATTR_BATTERY_BACKUP_POWER_LEVEL: self._system.battery_backup_power_level,
|
ATTR_BATTERY_BACKUP_POWER_LEVEL: (
|
||||||
|
self._system.battery_backup_power_level
|
||||||
|
),
|
||||||
ATTR_ENTRY_DELAY_AWAY: self._system.entry_delay_away,
|
ATTR_ENTRY_DELAY_AWAY: self._system.entry_delay_away,
|
||||||
ATTR_ENTRY_DELAY_HOME: self._system.entry_delay_home,
|
ATTR_ENTRY_DELAY_HOME: self._system.entry_delay_home,
|
||||||
ATTR_EXIT_DELAY_AWAY: self._system.exit_delay_away,
|
ATTR_EXIT_DELAY_AWAY: self._system.exit_delay_away,
|
||||||
|
|
|
@ -44,9 +44,9 @@ async def async_setup_entry(
|
||||||
async def async_add_player(player: SlimClient) -> None:
|
async def async_add_player(player: SlimClient) -> None:
|
||||||
"""Add MediaPlayerEntity from SlimClient."""
|
"""Add MediaPlayerEntity from SlimClient."""
|
||||||
# we delay adding the player a small bit because the player name may be received
|
# we delay adding the player a small bit because the player name may be received
|
||||||
# just a bit after connect. This way we can create a device reg entry with the correct name
|
# just a bit after connect. This way we can create a device reg entry with the
|
||||||
# the name will either be available within a few milliseconds after connect or not at all
|
# correct name the name will either be available within a few milliseconds after
|
||||||
# (its an optional data packet)
|
# connect or not at all (its an optional data packet)
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
if player.player_id not in player.name:
|
if player.player_id not in player.name:
|
||||||
break
|
break
|
||||||
|
|
|
@ -54,7 +54,8 @@ class SmartTubConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
|
|
||||||
# this is a reauth attempt
|
# this is a reauth attempt
|
||||||
if self._reauth_entry.unique_id != self.unique_id:
|
if self._reauth_entry.unique_id != self.unique_id:
|
||||||
# there is a config entry matching this account, but it is not the one we were trying to reauth
|
# there is a config entry matching this account,
|
||||||
|
# but it is not the one we were trying to reauth
|
||||||
return self.async_abort(reason="already_configured")
|
return self.async_abort(reason="already_configured")
|
||||||
self.hass.config_entries.async_update_entry(
|
self.hass.config_entries.async_update_entry(
|
||||||
self._reauth_entry, data=user_input
|
self._reauth_entry, data=user_input
|
||||||
|
|
|
@ -102,7 +102,7 @@ class SonosMedia:
|
||||||
|
|
||||||
@soco_error()
|
@soco_error()
|
||||||
def poll_track_info(self) -> dict[str, Any]:
|
def poll_track_info(self) -> dict[str, Any]:
|
||||||
"""Poll the speaker for current track info, add converted position values, and return."""
|
"""Poll the speaker for current track info, add converted position values."""
|
||||||
track_info: dict[str, Any] = self.soco.get_current_track_info()
|
track_info: dict[str, Any] = self.soco.get_current_track_info()
|
||||||
track_info[DURATION_SECONDS] = _timespan_secs(track_info.get("duration"))
|
track_info[DURATION_SECONDS] = _timespan_secs(track_info.get("duration"))
|
||||||
track_info[POSITION_SECONDS] = _timespan_secs(track_info.get("position"))
|
track_info[POSITION_SECONDS] = _timespan_secs(track_info.get("position"))
|
||||||
|
|
|
@ -261,7 +261,8 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
|
||||||
"STOPPED",
|
"STOPPED",
|
||||||
):
|
):
|
||||||
# Sonos can consider itself "paused" but without having media loaded
|
# Sonos can consider itself "paused" but without having media loaded
|
||||||
# (happens if playing Spotify and via Spotify app you pick another device to play on)
|
# (happens if playing Spotify and via Spotify app
|
||||||
|
# you pick another device to play on)
|
||||||
if self.media.title is None:
|
if self.media.title is None:
|
||||||
return MediaPlayerState.IDLE
|
return MediaPlayerState.IDLE
|
||||||
return MediaPlayerState.PAUSED
|
return MediaPlayerState.PAUSED
|
||||||
|
|
|
@ -359,7 +359,8 @@ class SonosSpeaker:
|
||||||
if any(isinstance(result, Exception) for result in results):
|
if any(isinstance(result, Exception) for result in results):
|
||||||
raise SonosSubscriptionsFailed
|
raise SonosSubscriptionsFailed
|
||||||
|
|
||||||
# Create a polling task in case subscriptions fail or callback events do not arrive
|
# Create a polling task in case subscriptions fail
|
||||||
|
# or callback events do not arrive
|
||||||
if not self._poll_timer:
|
if not self._poll_timer:
|
||||||
self._poll_timer = async_track_time_interval(
|
self._poll_timer = async_track_time_interval(
|
||||||
self.hass,
|
self.hass,
|
||||||
|
|
|
@ -407,16 +407,19 @@ class SoundTouchMediaPlayer(MediaPlayerEntity):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Client devices do NOT return their siblings as part of the "slaves" list.
|
# Client devices do NOT return their siblings as part of the "slaves" list.
|
||||||
# Only the master has the full list of slaves. To compensate for this shortcoming
|
# Only the master has the full list of slaves. To compensate for this
|
||||||
# we have to fetch the zone info from the master when the current device is a slave.
|
# shortcoming we have to fetch the zone info from the master when the current
|
||||||
|
# device is a slave.
|
||||||
# In addition to this shortcoming, libsoundtouch seems to report the "is_master"
|
# In addition to this shortcoming, libsoundtouch seems to report the "is_master"
|
||||||
# property wrong on some slaves, so the only reliable way to detect if the current
|
# property wrong on some slaves, so the only reliable way to detect
|
||||||
# devices is the master, is by comparing the master_id of the zone with the device_id.
|
# if the current devices is the master, is by comparing the master_id
|
||||||
|
# of the zone with the device_id.
|
||||||
if zone_status.master_id == self._device.config.device_id:
|
if zone_status.master_id == self._device.config.device_id:
|
||||||
return self._build_zone_info(self.entity_id, zone_status.slaves)
|
return self._build_zone_info(self.entity_id, zone_status.slaves)
|
||||||
|
|
||||||
# The master device has to be searched by it's ID and not IP since libsoundtouch / BOSE API
|
# The master device has to be searched by it's ID and not IP since
|
||||||
# do not return the IP of the master for some slave objects/responses
|
# libsoundtouch / BOSE API do not return the IP of the master
|
||||||
|
# for some slave objects/responses
|
||||||
master_instance = self._get_instance_by_id(zone_status.master_id)
|
master_instance = self._get_instance_by_id(zone_status.master_id)
|
||||||
if master_instance is not None:
|
if master_instance is not None:
|
||||||
master_zone_status = master_instance.device.zone_status()
|
master_zone_status = master_instance.device.zone_status()
|
||||||
|
@ -424,8 +427,9 @@ class SoundTouchMediaPlayer(MediaPlayerEntity):
|
||||||
master_instance.entity_id, master_zone_status.slaves
|
master_instance.entity_id, master_zone_status.slaves
|
||||||
)
|
)
|
||||||
|
|
||||||
# We should never end up here since this means we haven't found a master device to get the
|
# We should never end up here since this means we haven't found a master
|
||||||
# correct zone info from. In this case, assume current device is master
|
# device to get the correct zone info from. In this case,
|
||||||
|
# assume current device is master
|
||||||
return self._build_zone_info(self.entity_id, zone_status.slaves)
|
return self._build_zone_info(self.entity_id, zone_status.slaves)
|
||||||
|
|
||||||
def _get_instance_by_ip(self, ip_address):
|
def _get_instance_by_ip(self, ip_address):
|
||||||
|
|
|
@ -146,8 +146,9 @@ class Segment:
|
||||||
"""Render the HLS playlist section for the Segment.
|
"""Render the HLS playlist section for the Segment.
|
||||||
|
|
||||||
The Segment may still be in progress.
|
The Segment may still be in progress.
|
||||||
This method stores intermediate data in hls_playlist_parts, hls_num_parts_rendered,
|
This method stores intermediate data in hls_playlist_parts,
|
||||||
and hls_playlist_complete to avoid redoing work on subsequent calls.
|
hls_num_parts_rendered, and hls_playlist_complete to avoid redoing
|
||||||
|
work on subsequent calls.
|
||||||
"""
|
"""
|
||||||
if self.hls_playlist_complete:
|
if self.hls_playlist_complete:
|
||||||
return self.hls_playlist_template[0]
|
return self.hls_playlist_template[0]
|
||||||
|
@ -164,12 +165,14 @@ class Segment:
|
||||||
):
|
):
|
||||||
self.hls_playlist_parts.append(
|
self.hls_playlist_parts.append(
|
||||||
f"#EXT-X-PART:DURATION={part.duration:.3f},URI="
|
f"#EXT-X-PART:DURATION={part.duration:.3f},URI="
|
||||||
f'"./segment/{self.sequence}.{part_num}.m4s"{",INDEPENDENT=YES" if part.has_keyframe else ""}'
|
f'"./segment/{self.sequence}.{part_num}.m4s"'
|
||||||
|
f'{",INDEPENDENT=YES" if part.has_keyframe else ""}'
|
||||||
)
|
)
|
||||||
if self.complete:
|
if self.complete:
|
||||||
# Construct the final playlist_template. The placeholder will share a line with
|
# Construct the final playlist_template. The placeholder will share a
|
||||||
# the first element to avoid an extra newline when we don't render any parts.
|
# line with the first element to avoid an extra newline when we don't
|
||||||
# Append an empty string to create a trailing newline when we do render parts
|
# render any parts. Append an empty string to create a trailing newline
|
||||||
|
# when we do render parts
|
||||||
self.hls_playlist_parts.append("")
|
self.hls_playlist_parts.append("")
|
||||||
self.hls_playlist_template = (
|
self.hls_playlist_template = (
|
||||||
[] if last_stream_id == self.stream_id else ["#EXT-X-DISCONTINUITY"]
|
[] if last_stream_id == self.stream_id else ["#EXT-X-DISCONTINUITY"]
|
||||||
|
@ -204,9 +207,9 @@ class Segment:
|
||||||
)
|
)
|
||||||
if not add_hint:
|
if not add_hint:
|
||||||
return playlist
|
return playlist
|
||||||
# Preload hints help save round trips by informing the client about the next part.
|
# Preload hints help save round trips by informing the client about the
|
||||||
# The next part will usually be in this segment but will be first part of the next
|
# next part. The next part will usually be in this segment but will be
|
||||||
# segment if this segment is already complete.
|
# first part of the next segment if this segment is already complete.
|
||||||
if self.complete: # Next part belongs to next segment
|
if self.complete: # Next part belongs to next segment
|
||||||
sequence = self.sequence + 1
|
sequence = self.sequence + 1
|
||||||
part_num = 0
|
part_num = 0
|
||||||
|
@ -434,7 +437,8 @@ class KeyFrameConverter:
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
|
|
||||||
# Keep import here so that we can import stream integration without installing reqs
|
# Keep import here so that we can import stream integration
|
||||||
|
# without installingreqs
|
||||||
# pylint: disable-next=import-outside-toplevel
|
# pylint: disable-next=import-outside-toplevel
|
||||||
from homeassistant.components.camera.img_util import TurboJPEGSingleton
|
from homeassistant.components.camera.img_util import TurboJPEGSingleton
|
||||||
|
|
||||||
|
@ -456,7 +460,8 @@ class KeyFrameConverter:
|
||||||
if self._codec_context:
|
if self._codec_context:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Keep import here so that we can import stream integration without installing reqs
|
# Keep import here so that we can import stream integration without
|
||||||
|
# installing reqs
|
||||||
# pylint: disable-next=import-outside-toplevel
|
# pylint: disable-next=import-outside-toplevel
|
||||||
from av import CodecContext
|
from av import CodecContext
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,8 @@ class TriggerInstance:
|
||||||
event_config = event_trigger.TRIGGER_SCHEMA(event_config)
|
event_config = event_trigger.TRIGGER_SCHEMA(event_config)
|
||||||
if self.remove:
|
if self.remove:
|
||||||
self.remove()
|
self.remove()
|
||||||
# Note: No lock needed, event_trigger.async_attach_trigger is an synchronous function
|
# Note: No lock needed, event_trigger.async_attach_trigger
|
||||||
|
# is an synchronous function
|
||||||
self.remove = await event_trigger.async_attach_trigger(
|
self.remove = await event_trigger.async_attach_trigger(
|
||||||
self.trigger.hass,
|
self.trigger.hass,
|
||||||
event_config,
|
event_config,
|
||||||
|
|
|
@ -124,8 +124,9 @@ class TasmotaLight(
|
||||||
light_type = self._tasmota_entity.light_type
|
light_type = self._tasmota_entity.light_type
|
||||||
|
|
||||||
if light_type in [LIGHT_TYPE_RGB, LIGHT_TYPE_RGBW, LIGHT_TYPE_RGBCW]:
|
if light_type in [LIGHT_TYPE_RGB, LIGHT_TYPE_RGBW, LIGHT_TYPE_RGBCW]:
|
||||||
# Mark HS support for RGBW light because we don't have direct control over the
|
# Mark HS support for RGBW light because we don't have direct
|
||||||
# white channel, so the base component's RGB->RGBW translation does not work
|
# control over the white channel, so the base component's RGB->RGBW
|
||||||
|
# translation does not work
|
||||||
self._supported_color_modes.add(ColorMode.HS)
|
self._supported_color_modes.add(ColorMode.HS)
|
||||||
self._color_mode = ColorMode.HS
|
self._color_mode = ColorMode.HS
|
||||||
|
|
||||||
|
|
|
@ -174,7 +174,8 @@ class TasmotaDiscoveryUpdate(TasmotaEntity):
|
||||||
# Unchanged payload: Ignore to avoid changing states
|
# Unchanged payload: Ignore to avoid changing states
|
||||||
_LOGGER.debug("Ignoring unchanged update for: %s", self.entity_id)
|
_LOGGER.debug("Ignoring unchanged update for: %s", self.entity_id)
|
||||||
|
|
||||||
# Set in case the entity has been removed and is re-added, for example when changing entity_id
|
# Set in case the entity has been removed and is re-added,
|
||||||
|
# for example when changing entity_id
|
||||||
set_discovery_hash(self.hass, self._discovery_hash)
|
set_discovery_hash(self.hass, self._discovery_hash)
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
|
|
|
@ -207,7 +207,9 @@ SENSOR_DEVICE_CLASS_ICON_MAP: dict[str, dict[str, Any]] = {
|
||||||
}
|
}
|
||||||
|
|
||||||
SENSOR_UNIT_MAP = {
|
SENSOR_UNIT_MAP = {
|
||||||
hc.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
hc.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: (
|
||||||
|
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||||
|
),
|
||||||
hc.CONCENTRATION_PARTS_PER_BILLION: CONCENTRATION_PARTS_PER_BILLION,
|
hc.CONCENTRATION_PARTS_PER_BILLION: CONCENTRATION_PARTS_PER_BILLION,
|
||||||
hc.CONCENTRATION_PARTS_PER_MILLION: CONCENTRATION_PARTS_PER_MILLION,
|
hc.CONCENTRATION_PARTS_PER_MILLION: CONCENTRATION_PARTS_PER_MILLION,
|
||||||
hc.ELECTRICAL_CURRENT_AMPERE: UnitOfElectricCurrent.AMPERE,
|
hc.ELECTRICAL_CURRENT_AMPERE: UnitOfElectricCurrent.AMPERE,
|
||||||
|
|
Loading…
Add table
Reference in a new issue