Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alarm volume #250

Merged
merged 25 commits into from
Jun 9, 2021
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
df0ca6b
Added `update_alarm_volume` to `GlocaltokensApiClient`
ArnyminerZ Jun 6, 2021
2c2dfd3
Added `API_ENDPOINT_ALARM_VOLUME`.
ArnyminerZ Jun 6, 2021
5c16b5e
Added `set_alarm_volume` and `get_alarm_volume` to `GoogleHomeDevice`.
ArnyminerZ Jun 6, 2021
795eda8
Added `_get_alarm_volume` to `GoogleHomeDeviceSensor`
ArnyminerZ Jun 6, 2021
790a26e
Added `ICON_ALARM_VOLUME_LOW`, `ICON_ALARM_VOLUME_MID`, `ICON_ALARM_V…
ArnyminerZ Jun 6, 2021
ac6103f
Added `AlarmVolumeNumber`
ArnyminerZ Jun 6, 2021
b2a5751
Added `AlarmVolumeAttributes`
ArnyminerZ Jun 6, 2021
7739320
Fixed issues with typing
ArnyminerZ Jun 6, 2021
4b44f4f
Fixed lint issues
ArnyminerZ Jun 6, 2021
9a625db
Fixed linting
ArnyminerZ Jun 6, 2021
21cf61b
Fixed linting
ArnyminerZ Jun 6, 2021
459c841
Added check for volume answer type
ArnyminerZ Jun 6, 2021
27b7ebe
Fixed lint
ArnyminerZ Jun 6, 2021
540cd56
Undone typing changes in favor of python-typing-update
ArnyminerZ Jun 6, 2021
7d1369a
Added .idea dir
ArnyminerZ Jun 6, 2021
31d6c29
Added alarms volume attr to alarms sensor
ArnyminerZ Jun 6, 2021
1fd1bea
Fixed suggestions
ArnyminerZ Jun 7, 2021
93d1aec
Simplified check
ArnyminerZ Jun 7, 2021
fdfd5ce
Removed unnecessary attributes
ArnyminerZ Jun 8, 2021
e1c9239
Merge branch 'master' into alarm_volume
ArnyminerZ Jun 8, 2021
357422b
Disabled `duplicate-code`
ArnyminerZ Jun 8, 2021
a9ba6dd
Update api.py
ArnyminerZ Jun 9, 2021
bcf2734
Update README.md
ArnyminerZ Jun 9, 2021
b2eed2f
Merge branch 'master' into alarm_volume
ArnyminerZ Jun 9, 2021
36a1f78
Update README.md
ArnyminerZ Jun 9, 2021
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ __pycache__
venv*
.python-version
.tox/
/.idea
57 changes: 55 additions & 2 deletions custom_components/google_home/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
from homeassistant.core import HomeAssistant

from .const import (
API_ENDPOINT_ALARM_DELETE,
API_ENDPOINT_ALARM_VOLUME,
API_ENDPOINT_ALARMS,
API_ENDPOINT_DELETE,
API_ENDPOINT_DO_NOT_DISTURB,
API_ENDPOINT_REBOOT,
HEADER_CAST_LOCAL_AUTH,
HEADER_CONTENT_TYPE,
JSON_ALARM,
JSON_ALARM_VOLUME,
JSON_NOTIFICATIONS_ENABLED,
JSON_TIMER,
PORT,
Expand Down Expand Up @@ -195,7 +197,7 @@ async def delete_alarm_or_timer(
)

response = await self.request(
method="POST", endpoint=API_ENDPOINT_DELETE, device=device, data=data
method="POST", endpoint=API_ENDPOINT_ALARM_DELETE, device=device, data=data
)

if response is not None:
Expand Down Expand Up @@ -297,6 +299,57 @@ async def update_do_not_disturb(

return device

async def update_alarm_volume(
self, device: GoogleHomeDevice, volume: float | None = None
) -> GoogleHomeDevice:
"""Gets or sets the alarm volume setting on a Google Home device."""

data: JsonDict | None = None
polling = False

if volume is not None:
# Setting is inverted on device
data = {JSON_ALARM_VOLUME: volume}
_LOGGER.debug(
"Setting Alarm Volume setting to %f on Google Home device %s",
volume,
device.name,
)
else:
polling = True
_LOGGER.debug(
"Getting Alarm Volume setting from Google Home device %s",
device.name,
)

response = await self.request(
method="POST",
endpoint=API_ENDPOINT_ALARM_VOLUME,
device=device,
data=data,
polling=polling,
)
if response is not None:
ArnyminerZ marked this conversation as resolved.
Show resolved Hide resolved
if JSON_NOTIFICATIONS_ENABLED in response:
volume_raw: str = str(response[JSON_ALARM_VOLUME])
loaded_volume: float = float(volume_raw)
ArnyminerZ marked this conversation as resolved.
Show resolved Hide resolved
_LOGGER.debug(
"Received Alarm Volume setting from Google Home device %s"
" - Volume: %f",
device.name,
loaded_volume,
)

device.set_alarm_volume(loaded_volume)
else:
_LOGGER.debug(
"Response not expected from Google Home device %s - %s",
device.name,
response,
)

return device

async def request(
self,
method: Literal["GET", "POST"],
Expand Down
3 changes: 2 additions & 1 deletion custom_components/google_home/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ async def _show_config_form(
errors=self._errors,
)

async def _test_credentials(self, client: GlocaltokensApiClient) -> str | None:
@staticmethod
async def _test_credentials(client: GlocaltokensApiClient) -> str | None:
"""Returns true and master token if credentials are valid."""
try:
master_token = await client.async_get_master_token()
Expand Down
12 changes: 11 additions & 1 deletion custom_components/google_home/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
ICON_ALARMS: Final[str] = "mdi:alarm-multiple"
ICON_TIMERS: Final[str] = "mdi:timer-sand"
ICON_DO_NOT_DISTURB: Final[str] = "mdi:minus-circle"
ICON_ALARM_VOLUME_LOW: Final[str] = "mdi:volume-low"
ICON_ALARM_VOLUME_MID: Final[str] = "mdi:volume-medium"
ICON_ALARM_VOLUME_HIGH: Final[str] = "mdi:volume-high"
ICON_ALARM_VOLUME_OFF: Final[str] = "mdi:volume-off"

# Device classes
BINARY_SENSOR_DEVICE_CLASS: Final[str] = "connectivity"
Expand All @@ -51,6 +55,7 @@
DEFAULT_NAME: Final[str] = "Google Home"

LABEL_ALARMS: Final[str] = "alarms"
LABEL_ALARM_VOLUME: Final[str] = "alarm_volume"
LABEL_AVAILABLE: Final[str] = "available"
LABEL_TIMERS: Final[str] = "timers"
LABEL_DEVICE: Final[str] = "device"
Expand All @@ -61,7 +66,8 @@

# API
API_ENDPOINT_ALARMS: Final[str] = "setup/assistant/alarms"
API_ENDPOINT_DELETE: Final[str] = "setup/assistant/alarms/delete"
API_ENDPOINT_ALARM_DELETE: Final[str] = "setup/assistant/alarms/delete"
API_ENDPOINT_ALARM_VOLUME: Final[str] = "setup/assistant/alarms/volume"
API_ENDPOINT_REBOOT: Final[str] = "setup/reboot"
API_ENDPOINT_DO_NOT_DISTURB: Final[str] = "setup/assistant/notifications"

Expand All @@ -83,6 +89,10 @@
JSON_ALARM: Final[str] = "alarm"
JSON_TIMER: Final[str] = "timer"
JSON_NOTIFICATIONS_ENABLED: Final[str] = "notifications_enabled"
JSON_ALARM_VOLUME: Final[str] = "volume"

# Device class names
DEVICE_CLASS_ALARM_VOLUME = "google_home__alarm_volume"
ArnyminerZ marked this conversation as resolved.
Show resolved Hide resolved

STARTUP_MESSAGE: Final[
str
Expand Down
13 changes: 13 additions & 0 deletions custom_components/google_home/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from datetime import timedelta
from enum import Enum
import sys
from typing import Final

from homeassistant.util.dt import as_local, utc_from_timestamp

Expand Down Expand Up @@ -37,6 +38,7 @@ def __init__(
self.hardware = hardware
self.available = True
self._do_not_disturb = False
self._alarm_volume = GOOGLE_HOME_ALARM_DEFAULT_VALUE
self._timers: list[GoogleHomeTimer] = []
self._alarms: list[GoogleHomeAlarm] = []

Expand Down Expand Up @@ -100,6 +102,14 @@ def get_do_not_disturb(self) -> bool:
"""Return Do Not Disturb status."""
return self._do_not_disturb

def set_alarm_volume(self, volume: float) -> None:
"""Set Alarm Volume status."""
self._alarm_volume = volume

def get_alarm_volume(self) -> float:
"""Return Alarm Volume status."""
return self._alarm_volume


class GoogleHomeTimer:
"""Local representation of Google Home timer"""
Expand Down Expand Up @@ -193,3 +203,6 @@ class GoogleHomeTimerStatus(Enum):
SET = 1
PAUSED = 2
RINGING = 3


GOOGLE_HOME_ALARM_DEFAULT_VALUE: Final[float] = 1
ArnyminerZ marked this conversation as resolved.
Show resolved Hide resolved
125 changes: 125 additions & 0 deletions custom_components/google_home/number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"""Number Platform for Google Home"""
from __future__ import annotations

import logging
from typing import Callable, Iterable

from homeassistant.components.number import NumberEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant

from .const import (
DATA_CLIENT,
DATA_COORDINATOR,
DEVICE_CLASS_ALARM_VOLUME,
DOMAIN,
ICON_ALARM_VOLUME_HIGH,
ICON_ALARM_VOLUME_LOW,
ICON_ALARM_VOLUME_MID,
ICON_ALARM_VOLUME_OFF,
LABEL_ALARM_VOLUME,
)
from .entity import GoogleHomeBaseEntity
from .models import GOOGLE_HOME_ALARM_DEFAULT_VALUE
from .types import AlarmVolumeAttributes

_LOGGER: logging.Logger = logging.getLogger(__package__)


async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_devices: Callable[[Iterable[NumberEntity]], None],
) -> bool:
"""Setup switch platform."""
client = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT]
coordinator = hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR]
ArnyminerZ marked this conversation as resolved.
Show resolved Hide resolved

numbers: list[NumberEntity] = []
for device in coordinator.data:
if device.auth_token and device.available:
numbers.append(
AlarmVolumeNumber(
coordinator,
client,
device.name,
)
)

if numbers:
async_add_devices(numbers)

return True


class AlarmVolumeNumber(GoogleHomeBaseEntity, NumberEntity):
"""Google Home Alarm Volume Number entity."""

@property
def label(self) -> str:
"""Label to use for name and unique id."""
return LABEL_ALARM_VOLUME

@property
def unit_of_measurement(self) -> str | None:
"""The unit of measurement for the entity"""
return PERCENTAGE

@property
def icon(self) -> str:
"""Return the icon of the sensor."""
device = self.get_device()
if device is None:
return ICON_ALARM_VOLUME_HIGH
ArnyminerZ marked this conversation as resolved.
Show resolved Hide resolved
volume = device.get_alarm_volume()
if volume == 0:
return ICON_ALARM_VOLUME_OFF
if volume <= 0.3:
return ICON_ALARM_VOLUME_LOW
if volume <= 0.6:
return ICON_ALARM_VOLUME_MID
return ICON_ALARM_VOLUME_HIGH

@property
def min_value(self) -> float:
"""Return the minimum value for the volume"""
return 0

@property
def max_value(self) -> float:
"""Return the minimum value for the volume"""
return 1

@property
def step(self) -> float:
"""Return the step value for the volume"""
return 0.01

@property
def value(self) -> float:
"""Return the current volume value"""
device = self.get_device()

if device is None:
return GOOGLE_HOME_ALARM_DEFAULT_VALUE

volume = device.get_alarm_volume()
return volume

@property
def device_state_attributes(self) -> AlarmVolumeAttributes:
ArnyminerZ marked this conversation as resolved.
Show resolved Hide resolved
"""Return the state attributes."""
return {
"device_class": DEVICE_CLASS_ALARM_VOLUME,
"integration": DOMAIN,
ArnyminerZ marked this conversation as resolved.
Show resolved Hide resolved
}

async def async_set_value(self, value: float) -> None:
"""Sets the alarm volume"""
device = self.get_device()
if device is None:
_LOGGER.error("Device %s not found.", self.device_name)
return

await self.client.update_alarm_volume(device=device, volume=value)
14 changes: 13 additions & 1 deletion custom_components/google_home/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@
SERVICE_REBOOT,
)
from .entity import GoogleHomeBaseEntity
from .models import GoogleHomeAlarmStatus, GoogleHomeDevice, GoogleHomeTimerStatus
from .models import (
GOOGLE_HOME_ALARM_DEFAULT_VALUE,
GoogleHomeAlarmStatus,
GoogleHomeDevice,
GoogleHomeTimerStatus,
)
from .types import (
AlarmsAttributes,
DeviceAttributes,
Expand Down Expand Up @@ -188,6 +193,7 @@ def device_state_attributes(self) -> AlarmsAttributes:
"""Return the state attributes."""
return {
"next_alarm_status": self._get_next_alarm_status(),
"alarm_volume": self._get_alarm_volume(),
"alarms": self._get_alarms_data(),
"integration": DOMAIN,
}
Expand All @@ -202,6 +208,12 @@ def _get_next_alarm_status(self) -> str:
else GoogleHomeAlarmStatus.NONE.name.lower()
)

def _get_alarm_volume(self) -> float:
"""Update alarm volume status from coordinator"""
device = self.get_device()
alarm_volume = device.get_alarm_volume() if device else None
return alarm_volume if alarm_volume else GOOGLE_HOME_ALARM_DEFAULT_VALUE

def _get_alarms_data(self) -> list[GoogleHomeAlarmDict]:
"""Update alarms data extracting it from coordinator"""
device = self.get_device()
Expand Down
11 changes: 10 additions & 1 deletion custom_components/google_home/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,18 @@ class AlarmsAttributes(TypedDict):
"""Typed dict for alarms attributes"""

next_alarm_status: str
alarm_volume: float
alarms: list[GoogleHomeAlarmDict]
integration: str


class AlarmVolumeAttributes(TypedDict):
"""Typed dict for alarm volume attributes"""

device_class: str
integration: str


class TimersAttributes(TypedDict):
"""Typed dict for timers attributes"""

Expand All @@ -90,5 +98,6 @@ class OptionsFlowDict(TypedDict):


JsonDict = Mapping[
str, Union[bool, int, str, List[str], List[AlarmJsonDict], List[TimerJsonDict]]
str,
Union[bool, float, int, str, List[str], List[AlarmJsonDict], List[TimerJsonDict]],
]