chore: initial baseline before refactoring

Initial snapshot of Home Assistant config (HAOS 2026.4.4)
prior to dashboard restructuring and helper YAML migration.

Pre-commit cleanup applied:
- Removed blueprints/switch_manager/ (9.4 MB stale, component already gone)
- Removed .storage/switch_manager
- Removed home-assistant.log.{1,old,fault}
- Removed zigbee2mqtt/configuration_backup_v{1..4}.yaml
- Removed zigbee2mqtt/database.db.tmp.2024-09-27 (1.5y old)
- Created empty themes/ to satisfy configuration.yaml include

.gitignore uses allowlist strategy for .storage/ to keep
all tokens, keys, and PINs out of version control.
This commit is contained in:
2026-05-02 14:24:53 +02:00
commit 5f01411780
149 changed files with 47886 additions and 0 deletions
@@ -0,0 +1,35 @@
import abc
from typing import TYPE_CHECKING, Any
from cx_const import DefaultActionsMapping
from cx_helper import get_classes
if TYPE_CHECKING:
from cx_core.controller import Controller
EventData = dict[str, Any]
class Integration(abc.ABC):
name: str
controller: "Controller"
kwargs: dict[str, Any]
def __init__(self, controller: "Controller", kwargs: dict[str, Any]):
self.controller = controller
self.kwargs = kwargs
def get_default_actions_mapping(self) -> DefaultActionsMapping | None:
return None
@abc.abstractmethod
async def listen_changes(self, controller_id: str) -> None:
raise NotImplementedError
def get_integrations(
controller: "Controller", kwargs: dict[str, Any]
) -> list[Integration]:
integration_classes = get_classes(__file__, __package__, Integration)
integrations = [cls_(controller, kwargs) for cls_ in integration_classes]
return integrations
@@ -0,0 +1,34 @@
from typing import Any
from appdaemon.plugins.hass.hassapi import Hass
from cx_const import DefaultActionsMapping
from cx_core.integration import EventData, Integration
LISTENS_TO_ID = "id"
LISTENS_TO_UNIQUE_ID = "unique_id"
class DeCONZIntegration(Integration):
name = "deconz"
def get_default_actions_mapping(self) -> DefaultActionsMapping | None:
return self.controller.get_deconz_actions_mapping()
async def listen_changes(self, controller_id: str) -> None:
listens_to = self.kwargs.get("listen_to", LISTENS_TO_ID)
if listens_to not in (LISTENS_TO_ID, LISTENS_TO_UNIQUE_ID):
raise ValueError(
"`listens_to` for deCONZ integration should either be `id` or `unique_id`"
)
await Hass.listen_event(
self.controller,
self.event_callback,
"deconz_event",
**{listens_to: controller_id}
)
async def event_callback(
self, event_name: str, data: EventData, kwargs: dict[str, Any]
) -> None:
type_ = self.kwargs.get("type", "event")
await self.controller.handle_action(data[type_], extra=data)
@@ -0,0 +1,41 @@
from typing import Any
from appdaemon.plugins.hass.hassapi import Hass
from cx_core.integration import EventData, Integration
class EventIntegration(Integration):
name = "event"
def get_arg(self, arg: str) -> Any:
try:
return self.kwargs[arg]
except KeyError:
raise ValueError(f"{arg} is a mandatory field for event integration.")
async def listen_changes(self, controller_id: str) -> None:
event_type: str = self.get_arg("event_type")
controller_key: str = self.get_arg("controller_key")
self.controller.log(
f"Listening to `{event_type}` events for controller `{controller_key}={controller_id}`"
)
await Hass.listen_event(
self.controller,
self.event_callback,
event_type,
**{controller_key: controller_id},
)
async def event_callback(
self, event_name: str, data: EventData, kwargs: dict[str, Any]
) -> None:
action_template: str = self.get_arg("action_template")
try:
action = action_template.format(**data)
except Exception:
self.controller.log(
f"Template `{action_template}` could not be rendered with data={data}",
level="WARNING",
)
return
await self.controller.handle_action(action)
@@ -0,0 +1,36 @@
from typing import TYPE_CHECKING, Any
from appdaemon.plugins.hass.hassapi import Hass
from cx_const import DefaultActionsMapping
from cx_core.integration import EventData, Integration
if TYPE_CHECKING:
from cx_core.controller import Controller
class HomematicIntegration(Integration):
name = "homematic"
_registered_controller_ids: set[str]
def __init__(self, controller: "Controller", kwargs: dict[str, Any]):
self._registered_controller_ids = set()
super().__init__(controller, kwargs)
def get_default_actions_mapping(self) -> DefaultActionsMapping | None:
return self.controller.get_homematic_actions_mapping()
async def listen_changes(self, controller_id: str) -> None:
self._registered_controller_ids.add(controller_id)
await Hass.listen_event(
self.controller, self.event_callback, "homematic.keypress"
)
async def event_callback(
self, event_name: str, data: EventData, kwargs: dict[str, Any]
) -> None:
if data["name"] not in self._registered_controller_ids:
return
param = data["param"]
channel = data["channel"]
action = f"{param}_{channel}"
await self.controller.handle_action(action, extra=data)
@@ -0,0 +1,28 @@
from typing import Any
from appdaemon.plugins.hass.hassapi import Hass
from cx_const import DefaultActionsMapping
from cx_core.integration import EventData, Integration
class LutronIntegration(Integration):
name = "lutron_caseta"
def get_default_actions_mapping(self) -> DefaultActionsMapping | None:
return self.controller.get_lutron_caseta_actions_mapping()
async def listen_changes(self, controller_id: str) -> None:
await Hass.listen_event(
self.controller,
self.event_callback,
"lutron_caseta_button_event",
serial=controller_id,
)
async def event_callback(
self, event_name: str, data: EventData, kwargs: dict[str, Any]
) -> None:
button = data["button_number"]
action_type = data["action"]
action = f"button_{button}_{action_type}"
await self.controller.handle_action(action, extra=data)
@@ -0,0 +1,43 @@
import json
from typing import Any
from appdaemon.plugins.mqtt.mqttapi import Mqtt
from cx_const import DefaultActionsMapping
from cx_core.integration import EventData, Integration
class MQTTIntegration(Integration):
name = "mqtt"
def get_default_actions_mapping(self) -> DefaultActionsMapping | None:
return self.controller.get_z2m_actions_mapping()
async def listen_changes(self, controller_id: str) -> None:
await Mqtt.listen_event(
self.controller, self.event_callback, topic=controller_id, namespace="mqtt"
)
async def event_callback(
self, event_name: str, data: EventData, kwargs: dict[str, Any]
) -> None:
self.controller.log(f"MQTT data event: {data}", level="DEBUG")
payload_key = self.kwargs.get("key")
if "payload" not in data:
return
payload = data["payload"]
action_key: str
if payload_key is None:
action_key = payload
else:
try:
action_key = str(json.loads(payload)[payload_key]).lower()
except json.decoder.JSONDecodeError:
raise ValueError(
f"`key` is being used ({payload_key}). "
f"Following payload is not a valid JSON: {payload}"
)
except KeyError:
raise ValueError(
f"Following payload does not contain `{payload_key}`: {payload}"
)
await self.controller.handle_action(action_key)
@@ -0,0 +1,25 @@
from typing import Any
from appdaemon.plugins.hass.hassapi import Hass
from cx_const import DefaultActionsMapping
from cx_core.integration import EventData, Integration
class ShellyIntegration(Integration):
name = "shelly"
def get_default_actions_mapping(self) -> DefaultActionsMapping | None:
return self.controller.get_shelly_actions_mapping()
async def listen_changes(self, controller_id: str) -> None:
await Hass.listen_event(
self.controller, self.event_callback, "shelly.click", device=controller_id
)
async def event_callback(
self, event_name: str, data: EventData, kwargs: dict[str, Any]
) -> None:
click_type = data["click_type"]
channel = data["channel"]
action = f"{click_type}_{channel}"
await self.controller.handle_action(action, extra=data)
@@ -0,0 +1,27 @@
from typing import Any
from appdaemon.plugins.hass.hassapi import Hass
from cx_const import DefaultActionsMapping
from cx_core.integration import EventData, Integration
class ShellyForHASSIntegration(Integration):
name = "shellyforhass"
def get_default_actions_mapping(self) -> DefaultActionsMapping | None:
return self.controller.get_shellyforhass_actions_mapping()
async def listen_changes(self, controller_id: str) -> None:
await Hass.listen_event(
self.controller,
self.event_callback,
"shellyforhass.click",
entity_id=controller_id,
)
async def event_callback(
self, event_name: str, data: EventData, kwargs: dict[str, Any]
) -> None:
click_type = data["click_type"]
action = f"{click_type}"
await self.controller.handle_action(action, extra=data)
@@ -0,0 +1,28 @@
from typing import Any
from appdaemon.plugins.hass.hassapi import Hass
from cx_const import DefaultActionsMapping
from cx_core.integration import Integration
class StateIntegration(Integration):
name = "state"
def get_default_actions_mapping(self) -> DefaultActionsMapping | None:
return self.controller.get_state_actions_mapping()
async def listen_changes(self, controller_id: str) -> None:
attribute = self.kwargs.get("attribute", None)
await Hass.listen_state(
self.controller, self.state_callback, controller_id, attribute=attribute
)
async def state_callback(
self,
entity: str | None,
attribute: str | None,
old: str | None,
new: str,
kwargs: dict[str, Any],
) -> None:
await self.controller.handle_action(new, previous_state=old)
@@ -0,0 +1,51 @@
import json
from typing import Any
from appdaemon.plugins.mqtt.mqttapi import Mqtt
from cx_const import DefaultActionsMapping
from cx_core.integration import EventData, Integration
class TasmotaIntegration(Integration):
name = "tasmota"
def get_default_actions_mapping(self) -> DefaultActionsMapping | None:
return self.controller.get_tasmota_actions_mapping()
async def listen_changes(self, controller_id: str) -> None:
component_key = self.kwargs.get("component")
if component_key is None:
raise ValueError(
"`component` attribute is mandatory. "
"Check example from https://xaviml.github.io/controllerx/start/integrations/tasmota"
)
await Mqtt.listen_event(
self.controller, self.event_callback, topic=controller_id, namespace="mqtt"
)
async def event_callback(
self, event_name: str, data: EventData, kwargs: dict[str, Any]
) -> None:
self.controller.log(f"MQTT data event: {data}", level="DEBUG")
component_key: str = self.kwargs["component"]
payload_key: str = self.kwargs.get("key", "Action")
if "payload" not in data:
return
payload: str = data["payload"]
# Even though this checks if "compoenent_key" is in the payload
# (as string, not dictionary), it is preferred for its performance
if component_key not in payload:
return
try:
action_key = str(json.loads(payload)[component_key][payload_key])
except json.decoder.JSONDecodeError:
raise ValueError(
f"`key` is being used ({payload_key}). "
f"Following payload is not a valid JSON: {payload}"
)
except KeyError:
raise ValueError(
"Following payload does not contain "
f"payload_key=`{payload_key}`: {payload}"
)
await self.controller.handle_action(action_key)
@@ -0,0 +1,85 @@
import json
from typing import Any
from appdaemon.plugins.hass.hassapi import Hass
from appdaemon.plugins.mqtt.mqttapi import Mqtt
from cx_const import DefaultActionsMapping
from cx_core.integration import EventData, Integration
LISTENS_TO_HA = "ha"
LISTENS_TO_MQTT = "mqtt"
LISTENS_TO_EVENT = "event"
class Z2MIntegration(Integration):
name = "z2m"
def get_default_actions_mapping(self) -> DefaultActionsMapping | None:
return self.controller.get_z2m_actions_mapping()
async def listen_changes(self, controller_id: str) -> None:
listens_to = self.kwargs.get("listen_to", LISTENS_TO_HA)
if listens_to == LISTENS_TO_HA:
self.controller.log(
"⚠️ Listening to HA sensor actions is now deprecated and will be removed in the future. Use `listen_to: mqtt` or `listen_to: event` instead."
" Read more about it here: https://xaviml.github.io/controllerx/others/z2m-ha-sensor-deprecated",
level="WARNING",
ascii_encode=False,
)
await Hass.listen_state(self.controller, self.state_callback, controller_id)
elif listens_to == LISTENS_TO_MQTT:
topic_prefix = self.kwargs.get("topic_prefix", "zigbee2mqtt")
await Mqtt.listen_event(
self.controller,
self.event_callback,
topic=f"{topic_prefix}/{controller_id}",
namespace="mqtt",
)
elif listens_to == LISTENS_TO_EVENT:
await Hass.listen_state(
self.controller,
self.state_callback,
f"event.{controller_id}",
attribute="event_type",
)
else:
raise ValueError(
"`listen_to` has to be either `ha`, `mqtt` or `event`. Default is `ha`."
)
async def event_callback(
self, event_name: str, data: EventData, kwargs: dict[str, Any]
) -> None:
self.controller.log(f"MQTT data event: {data}", level="DEBUG")
action_key = self.kwargs.get("action_key", "action")
action_group_key = self.kwargs.get("action_group_key", "action_group")
if "payload" not in data:
return
payload = json.loads(data["payload"])
if action_key not in payload:
self.controller.log(
f"⚠️ There is no `{action_key}` in the MQTT topic payload",
level="WARNING",
ascii_encode=False,
)
return
if action_group_key in payload and "action_group" in self.kwargs:
action_group = self.controller.get_list(self.kwargs["action_group"])
if payload["action_group"] not in action_group:
self.controller.log(
f"Action group {payload['action_group']} not found in "
f"action groups: {action_group}",
level="DEBUG",
)
return
await self.controller.handle_action(payload[action_key], extra=payload)
async def state_callback(
self,
entity: str | None,
attribute: str | None,
old: str | None,
new: str,
kwargs: dict[str, Any],
) -> None:
await self.controller.handle_action(new, previous_state=old)
@@ -0,0 +1,46 @@
from typing import Any
from appdaemon.plugins.hass.hassapi import Hass
from cx_const import DefaultActionsMapping
from cx_core.integration import EventData, Integration
class ZHAIntegration(Integration):
name = "zha"
def get_default_actions_mapping(self) -> DefaultActionsMapping | None:
return self.controller.get_zha_actions_mapping()
async def listen_changes(self, controller_id: str) -> None:
await Hass.listen_event(
self.controller, self.event_callback, "zha_event", device_ieee=controller_id
)
def get_action(self, data: EventData) -> str:
command = data["command"]
args = data["args"]
if isinstance(args, dict):
args = args["args"]
args = list(map(str, args))
action = command
if not (command == "stop" or command == "release"):
if len(args) > 0:
action += "_" + "_".join(args)
return action
async def event_callback(
self, event_name: str, data: EventData, kwargs: dict[str, Any]
) -> None:
action = self.controller.get_zha_action(data)
if action is None:
# If there is no action extracted from the controller then
# we extract with the standard function
try:
action = self.get_action(data)
except Exception:
self.controller.log(
f"The following event could not be parsed: {data}", level="WARNING"
)
return
await self.controller.handle_action(action, extra=data)