Skip to content

Commit

Permalink
Add Home Assistant cloud controls
Browse files Browse the repository at this point in the history
  • Loading branch information
frenck committed Feb 24, 2023
1 parent ebfd91c commit 94daa6d
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 1 deletion.
7 changes: 7 additions & 0 deletions README.md
Expand Up @@ -85,6 +85,13 @@ of the box.
- **Number of suns**: Answers how godlike you are. _#burn_
- **Number of zones**: How many comfort zones you have on a map. _#zoneing_

## Switches

- **Home Assistant Cloud**: Switch to control behavior of Nabu Casa's Home Assistant Cloud. _#love_
- **Alexa**: Enable/disable Alexa connection. _#amazon_
- **Google Assistant**: Enable/disable Google Assistant connection. _#bigtech_
- **Remote**: Enable/disable remote access to the Home Asistant frontend. _#rdp_

# Services

There are quite a few useless and horrible services available for you to explore
Expand Down
2 changes: 1 addition & 1 deletion custom_components/spook/const.py
Expand Up @@ -4,4 +4,4 @@
from homeassistant.const import Platform

DOMAIN: Final = "spook"
PLATFORMS: Final = [Platform.SENSOR]
PLATFORMS: Final = [Platform.SENSOR, Platform.SWITCH]
27 changes: 27 additions & 0 deletions custom_components/spook/entity.py
Expand Up @@ -3,7 +3,9 @@

from dataclasses import dataclass

from hass_nabucasa import Cloud
from homeassistant.components import homeassistant
from homeassistant.components.cloud.const import DOMAIN as CLOUD_DOMAIN
from homeassistant.const import __version__ as HA_VERSION
from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription

Expand Down Expand Up @@ -44,3 +46,28 @@ def __init__(self, description: SpookEntityDescription) -> None:
sw_version=HA_VERSION,
)
self._attr_unique_id = f"{homeassistant.DOMAIN}_{description.key}"


class HomeAssistantCloudSpookEntity(SpookEntity):
"""Defines an base Spook entity for Home Assistant Cloud related entities."""

_cloud: Cloud

def __init__(self, cloud: Cloud, description: SpookEntityDescription) -> None:
"""Initialize the entity."""
super().__init__(description=description)
self._cloud = cloud
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, CLOUD_DOMAIN)},
manufacturer="Nabu Casa Inc.",
name="Home Assistant Cloud",
configuration_url="https://account.nabucasa.com/",
)
self._attr_unique_id = f"{CLOUD_DOMAIN}_{description.key}"

@property
def available(self) -> bool:
"""Return if cloud services are available."""
return (
super().available and self._cloud.is_logged_in and self._cloud.is_connected
)
1 change: 1 addition & 0 deletions custom_components/spook/manifest.json
Expand Up @@ -4,6 +4,7 @@
"codeowners": ["@frenck"],
"config_flow": true,
"dependencies": ["homeassistant"],
"after_dependencies": ["cloud"],
"documentation": "https://github.com/frenck/spook",
"integration_type": "hub",
"iot_class": "local_push",
Expand Down
129 changes: 129 additions & 0 deletions custom_components/spook/switch.py
@@ -0,0 +1,129 @@
"""Spook - Not your homey."""
from __future__ import annotations

from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from typing import Any

from hass_nabucasa import Cloud
from homeassistant.components.cloud.const import DOMAIN as CLOUD_DOMAIN
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .entity import HomeAssistantCloudSpookEntity, SpookEntityDescription


@dataclass
class HomeAssistantCloudSpookSwitchEntityDescriptionMixin:
"""Mixin values for Home Assistant related sensors."""

is_on_fn: Callable[[Cloud], bool | None]
set_fn: Callable[[Cloud, bool], Awaitable[Any]]


@dataclass
class HomeAssistantCloudSpookSwitchEntityDescription(
SpookEntityDescription,
SwitchEntityDescription,
HomeAssistantCloudSpookSwitchEntityDescriptionMixin,
):
"""Class describing Spook Home Assistant sensor entities."""

icon_off: str | None = None

def __post_init__(self) -> None:
"""Sync icon_off with icon."""
if self.icon_off is None:
self.icon_off = self.icon


SWITCHES: tuple[HomeAssistantCloudSpookSwitchEntityDescription, ...] = (
HomeAssistantCloudSpookSwitchEntityDescription(
key="alexa",
entity_id="switch.cloud_alexa",
name="Alexa",
icon="mdi:account-voice",
entity_category=EntityCategory.CONFIG,
is_on_fn=lambda cloud: cloud.client.prefs.alexa_enabled,
set_fn=lambda cloud, enabled: cloud.client.prefs.async_update(
alexa_enabled=enabled
),
),
HomeAssistantCloudSpookSwitchEntityDescription(
key="google",
entity_id="switch.cloud_google",
name="Google Assistant",
icon="mdi:google-assistant",
entity_category=EntityCategory.CONFIG,
is_on_fn=lambda cloud: cloud.client.prefs.google_enabled,
set_fn=lambda cloud, enabled: cloud.client.prefs.async_update(
google_enabled=enabled
),
),
HomeAssistantCloudSpookSwitchEntityDescription(
key="remote",
entity_id="switch.cloud_remote",
name="Remote",
icon="mdi:remote-desktop",
entity_category=EntityCategory.CONFIG,
is_on_fn=lambda cloud: cloud.client.prefs.remote_enabled,
set_fn=lambda cloud, enabled: cloud.client.prefs.async_update(
remote_enabled=enabled
),
),
)


async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Spook sensor."""
if CLOUD_DOMAIN in hass.config.components:
cloud: Cloud = hass.data[CLOUD_DOMAIN]
async_add_entities(
HomeAssistantCloudSpookSwitchEntity(cloud, description)
for description in SWITCHES
)


class HomeAssistantCloudSpookSwitchEntity(HomeAssistantCloudSpookEntity, SwitchEntity):
"""Spook switch providig Home Asistant Cloud controls."""

entity_description: HomeAssistantCloudSpookSwitchEntityDescription

async def async_added_to_hass(self) -> None:
"""Register for switch updates."""

@callback
def _update_state(_: Any) -> None:
"""Update state."""
self.async_schedule_update_ha_state()

self.async_on_remove(
self._cloud.client.prefs.async_listen_updates(_update_state)
)

@property
def icon(self) -> str | None:
"""Return the icon."""
if self.entity_description.icon_off and self.is_on is False:
return self.entity_description.icon_off
return super().icon

@property
def is_on(self) -> bool | None:
"""Return state of the switch."""
return self.entity_description.is_on_fn(self._cloud)

async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the entity on."""
await self.entity_description.set_fn(self._cloud, True)

async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the entity off."""
await self.entity_description.set_fn(self._cloud, False)

0 comments on commit 94daa6d

Please sign in to comment.