add home assistant integration

This commit is contained in:
Nitwel 2024-03-16 01:19:20 +01:00
parent 2a575c456c
commit dece26ef3d
8 changed files with 307 additions and 0 deletions

View File

@ -0,0 +1,55 @@
"""The Immersive Home integration."""
from __future__ import annotations
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.typing import ConfigType
from . import hub, websocket_api
from .const import DOMAIN
PLATFORMS: list[Platform] = [Platform.SENSOR]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the ImmersiveHome component."""
hass.data[DOMAIN] = {
"hub": hub.Hub(hass),
}
websocket_api.async_setup_commands(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up ImmersiveHome from a config entry."""
registration = entry.data
hass.data.setdefault(DOMAIN, {})
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, registration["device_id"])},
manufacturer="Someone",
model=registration["platform"],
name=registration["name"],
sw_version=registration["version"],
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok

View File

@ -0,0 +1,29 @@
"""Config flow for Immersive Home."""
from homeassistant.config_entries import ConfigFlow
from .const import DOMAIN
class ImmersiveHomeFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a Mobile App config flow."""
VERSION = 1
async def async_step_user(self, user_input=None):
"""Handle a flow initialized by the user."""
placeholders = {
"apps_url": "https://www.home-assistant.io/integrations/immersive_home/#apps"
}
return self.async_abort(
reason="install_app", description_placeholders=placeholders
)
async def async_step_registration(self, user_input=None):
"""Handle a flow initialized during registration."""
if "device_id" in user_input:
await self.async_set_unique_id(f"{user_input['device_id']}")
return self.async_create_entry(title=user_input["name"], data=user_input)

View File

@ -0,0 +1,3 @@
"""Constants for the Immersive Home integration."""
DOMAIN = "immersive_home"

View File

@ -0,0 +1,48 @@
from __future__ import annotations
import asyncio
from homeassistant.core import HomeAssistant
class Hub:
manufacturer = "Immersive Home"
def __init__(self, hass: HomeAssistant) -> None:
self._hass = hass
self.devices = {}
def add_device(self, device: Device) -> None:
self.devices[device.id] = device
def get_device(self, device_id: str) -> Device:
return self.devices.get(device_id)
class Device:
def __init__(self, device_id: str, name: str, version: str, platform: str) -> None:
self.id = device_id
self.name = name
self.version = version
self.platform = platform
self.room = None
self._callbacks = set()
def set_room(self, room: str) -> None:
self.room = room
asyncio.create_task(self.publish_updates())
def add_callback(self, callback: callable[[], None]) -> None:
"""Register callback, called when Roller changes state."""
self._callbacks.add(callback)
def remove_callback(self, callback: callable[[], None]) -> None:
"""Remove previously registered callback."""
self._callbacks.discard(callback)
async def publish_updates(self) -> None:
"""Schedule call all registered callbacks."""
for callback in self._callbacks:
callback()

View File

@ -0,0 +1,14 @@
{
"domain": "immersive_home",
"name": "ImmersiveHome",
"codeowners": ["@nitwel"],
"config_flow": true,
"dependencies": ["http", "websocket_api"],
"documentation": "https://www.home-assistant.io/integrations/immersive_home",
"homekit": {},
"iot_class": "local_push",
"requirements": [],
"ssdp": [],
"zeroconf": [],
"version": "0.0.1"
}

View File

@ -0,0 +1,59 @@
"""Platform for sensor integration."""
from __future__ import annotations
from homeassistant.components.sensor import SensorEntity
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import DOMAIN
from .hub import Device, Hub
async def async_setup_entry(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the sensor platform."""
hub: Hub = hass.data[DOMAIN]["hub"]
for device in hub.devices.values():
async_add_entities([RoomSensor(device)])
class RoomSensor(SensorEntity):
"""Representation of a Sensor."""
def __init__(self, device: Device) -> None:
"""Initialize the sensor."""
self._attr_name = f"{device.name} Room"
self._attr_unique_id = f"{device.id}_room"
self._device = device
self._attr_should_poll = False
@property
def device_info(self):
return {
"identifiers": {(DOMAIN, self._device.id)},
}
@property
def state(self):
return self._device.room
@property
def available(self):
return True
async def async_added_to_hass(self):
"""Run when this Entity has been added to HA."""
# Sensors should also register callbacks to HA when their state changes
self._device.add_callback(self.async_write_ha_state)
async def async_will_remove_from_hass(self):
"""Entity being removed from hass."""
# The opposite of async_added_to_hass. Remove any registered call backs here.
self._device.remove_callback(self.async_write_ha_state)

View File

@ -0,0 +1,13 @@
{
"config": {
"step": {
"confirm": {
"description": "[%key:common::config_flow::description::confirm_setup%]"
}
},
"abort": {
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]"
}
}
}

View File

@ -0,0 +1,86 @@
"""Home Assistant websocket API."""
from __future__ import annotations
from typing import Any
import voluptuous as vol
from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
from .const import DOMAIN
from .hub import Device, Hub
@callback
def async_setup_commands(hass):
"""Set up the mobile app websocket API."""
websocket_api.async_register_command(hass, handle_register)
websocket_api.async_register_command(hass, handle_update)
@callback
@websocket_api.websocket_command(
{
vol.Required("type"): "immersive_home/register",
vol.Required("device_id"): str,
vol.Required("name"): str,
vol.Required("version"): str,
vol.Required("platform"): str,
}
)
@websocket_api.async_response
async def handle_register(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Set up a new Immersive Home Device."""
hub: Hub = hass.data[DOMAIN]["hub"]
hub.add_device(
Device(
msg["device_id"],
msg["name"],
msg["version"],
msg["platform"],
)
)
await hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, data=msg, context={"source": "registration"}
)
)
connection.send_result(msg["id"], {"result": "success"})
@callback
@websocket_api.websocket_command(
{
vol.Required("type"): "immersive_home/update",
vol.Required("device_id"): str,
vol.Optional("room"): str,
}
)
def handle_update(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Update data of a Immersive Home Device."""
hub: Hub = hass.data[DOMAIN]["hub"]
device = hub.get_device(msg["device_id"])
if device is None:
connection.send_error(msg["id"], "device_not_found", "Device not found")
return
if "room" in msg:
device.set_room(msg["room"])
connection.send_result(msg["id"], {"result": "success"})