Skip to content

feat: Add Home Assistant smart device integration (Issue #366)#2117

Open
giwaov wants to merge 2 commits intoOpenMind:mainfrom
giwaov:feat/home-assistant-integration
Open

feat: Add Home Assistant smart device integration (Issue #366)#2117
giwaov wants to merge 2 commits intoOpenMind:mainfrom
giwaov:feat/home-assistant-integration

Conversation

@giwaov
Copy link

@giwaov giwaov commented Feb 5, 2026

Summary

This PR adds Home Assistant integration to OM1, enabling robots to control and monitor smart home devices.

🤖 Home Assistant Integrated

  • REST API connector (primary)
  • MQTT connector (alternative)

Features

Action Plugin (home_assistant)

  • Lights: on/off, toggle, brightness (0-255), RGB color
  • Switches: on/off, toggle
  • Thermostats: set temperature, HVAC modes (heat/cool/auto/off)
  • Covers: open/close/stop (blinds, garage doors)
  • Fans: on/off, toggle

Input Plugin (HomeAssistantStateInput)

  • Real-time device state monitoring
  • Change detection (only report when states change)
  • Natural language formatting for LLM consumption
  • Supports: lights, switches, climate, sensors, binary sensors, covers, fans

Files Changed

  • src/actions/home_assistant/interface.py - Action interface definitions
  • src/actions/home_assistant/connector/rest_api.py - REST API connector
  • src/actions/home_assistant/connector/mqtt.py - MQTT connector
  • src/inputs/plugins/home_assistant_state.py - State monitoring input
  • tests/actions/test_home_assistant.py - Action tests (30+ test cases)
  • tests/inputs/test_home_assistant_state.py - Input tests
  • config/home_assistant.json5 - Demo configuration
  • docs/smart_devices/HOME_ASSISTANT_SETUP.md - Setup documentation

Configuration Example

{
  agent_actions: [{
    name: "home_assistant",
    llm_label: "smart_home",
    connector: "rest_api",
    config: {
      base_url: "http://homeassistant.local:8123",
      access_token: "YOUR_TOKEN"
    }
  }],
  agent_inputs: [{
    type: "HomeAssistantStateInput",
    config: {
      base_url: "http://homeassistant.local:8123",
      access_token: "YOUR_TOKEN",
      entity_ids: ["light.living_room", "climate.bedroom"]
    }
  }]
}

🎥 Demo Video

Coming soon - will record once PR is reviewed.

📑 Notes

  • Setup: Get long-lived token from Home Assistant Profile → Long-Lived Access Tokens
  • Limitations: MQTT connector requires aiomqtt package
  • Improvements: Could add support for scenes, automations, and scripts

Closes #366

- Add home_assistant action plugin with REST API and MQTT connectors
- Add HomeAssistantStateInput for monitoring device states
- Support for lights (on/off, brightness, color), switches, thermostats, covers, fans
- Comprehensive test suite with 30+ test cases
- Documentation with setup guide and configuration examples
- Demo configuration for smart home control

Closes OpenMind#366
@giwaov giwaov requested a review from a team as a code owner February 5, 2026 14:57
Copilot AI review requested due to automatic review settings February 5, 2026 14:57
@giwaov giwaov requested review from a team as code owners February 5, 2026 14:57
@github-actions github-actions bot added documentation Improvements or additions to documentation robotics Robotics code changes python Python code tests Test files config Configuration files labels Feb 5, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds comprehensive Home Assistant smart home device integration to OM1, enabling control and monitoring of IoT devices through both REST API and MQTT protocols.

Changes:

  • Implements action plugin for controlling lights, switches, thermostats, covers, and fans with brightness/color/temperature control
  • Implements input plugin for real-time device state monitoring with change detection
  • Provides dual connectivity options: REST API (primary) and MQTT (alternative)
  • Includes 30+ unit tests covering configuration, device control, and state formatting

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 28 comments.

Show a summary per file
File Description
src/actions/home_assistant/interface.py Defines action interfaces with device types (light, switch, climate, cover, fan, media_player) and input/output dataclasses
src/actions/home_assistant/connector/rest_api.py REST API connector implementing device control via Home Assistant HTTP API with session management
src/actions/home_assistant/connector/mqtt.py MQTT connector for device control using MQTT protocol with aiomqtt library
src/inputs/plugins/home_assistant_state.py Input plugin for polling and formatting device states for LLM consumption
tests/actions/test_home_assistant.py Comprehensive unit tests for REST API connector covering all device types and error cases
tests/inputs/test_home_assistant_state.py Unit tests for input plugin covering state formatting and polling logic
docs/smart_devices/HOME_ASSISTANT_SETUP.md Complete setup documentation with examples, configuration, and troubleshooting
config/home_assistant.json5 Demo configuration file with examples for both action and input plugins

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 386 to 403
async def raw_to_text(self, raw_input: Optional[str]) -> Optional[Message]:
"""
Convert raw input to a Message object.

Parameters
----------
raw_input : Optional[str]
Raw state information.

Returns
-------
Optional[Message]
Message object or None.
"""
message = await self._raw_to_text(raw_input)
if message:
self.messages.append(message)
return message
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The raw_to_text method duplicates the _raw_to_text logic unnecessarily. In the codebase convention (see gps.py:93-105, face_presence_input.py:147-164), raw_to_text should call _raw_to_text and then append to messages, not reimplement the logic. The current implementation has both methods doing similar work, which violates DRY principle and the established pattern.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +18
"""
Home Assistant Device State Input Plugin for OM1.

This module provides an input plugin that monitors Home Assistant device states
and reports them to the OM1 fuser for processing by the LLM.
"""

import asyncio
import logging
import time
from dataclasses import dataclass
from typing import Any, Dict, List, Optional

import aiohttp
from pydantic import Field

from inputs.base import Message, SensorConfig
from inputs.base.loop import FuserInput
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing IOProvider integration. All other input plugins in the codebase (e.g., gps.py, face_presence_input.py, battery_turtlebot4.py) import and use IOProvider to log input data in formatted_latest_buffer. This plugin should follow the same pattern by importing IOProvider and calling io_provider.add_input() in formatted_latest_buffer.

Copilot uses AI. Check for mistakes.
Comment on lines +480 to +484
def tick(self) -> None:
"""
Tick method for periodic updates.
"""
time.sleep(60)
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tick() method sleeps for 60 seconds unconditionally. This blocks the thread and could impact the responsiveness of the action system. While this matches the base class default implementation (actions/base.py:95-99), consider whether this is appropriate for a Home Assistant connector that may need to handle multiple rapid commands. Most action connectors either override this with appropriate timing or don't block at all. For an async connector, consider using asyncio.sleep or removing the sleep entirely if not needed.

Copilot uses AI. Check for mistakes.
Comment on lines 193 to 196
try:
rgb = [int(x.strip()) for x in color_rgb.split(",")]
if len(rgb) == 3:
payload["color"] = {"r": rgb[0], "g": rgb[1], "b": rgb[2]}
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RGB color values are not validated to be within the 0-255 range. The code parses integers from the color_rgb string but doesn't check if they're valid (0-255). Values outside this range could cause unexpected behavior in Home Assistant. Consider adding validation to ensure each RGB component is in the valid range.

Copilot uses AI. Check for mistakes.
Comment on lines +323 to +335
async def test_connect_unsupported_device_type(self, connector):
"""Test handling of unsupported device types."""
input_data = HomeAssistantInput(
device_type="unsupported",
entity_id="unsupported.test",
action="test",
)

await connector.connect(input_data)

assert connector._last_result.success is False
assert "Unsupported device type" in connector._last_result.message

Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test coverage for network error scenarios. The _call_service method in rest_api.py handles aiohttp.ClientError and generic exceptions (lines 153-158), but there are no tests verifying this error handling works correctly. Consider adding tests that simulate network errors, timeouts, and connection failures to ensure graceful degradation.

Copilot uses AI. Check for mistakes.
that are exposed via MQTT in Home Assistant.
"""

import asyncio
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'asyncio' is not used.

Suggested change
import asyncio

Copilot uses AI. Check for mistakes.
home devices.
"""

import asyncio
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'asyncio' is not used.

Suggested change
import asyncio

Copilot uses AI. Check for mistakes.
"""

import pytest
from unittest.mock import AsyncMock, MagicMock, patch
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'MagicMock' is not used.

Suggested change
from unittest.mock import AsyncMock, MagicMock, patch
from unittest.mock import AsyncMock, patch

Copilot uses AI. Check for mistakes.
Comment on lines 16 to 18
SwitchAction,
ThermostatAction,
)
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'SwitchAction' is not used.

Suggested change
SwitchAction,
ThermostatAction,
)
ThermostatAction,
)

Copilot uses AI. Check for mistakes.

import pytest
import time
from unittest.mock import AsyncMock, MagicMock, patch
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'MagicMock' is not used.

Suggested change
from unittest.mock import AsyncMock, MagicMock, patch
from unittest.mock import AsyncMock, patch

Copilot uses AI. Check for mistakes.
- Remove unused imports (asyncio, MagicMock, SwitchAction)
- Remove unsupported MEDIA_PLAYER from DeviceType enum
- Add RGB color validation (0-255 range) in REST and MQTT connectors
- Add access_token empty warning in REST connector and input plugin
- Implement persistent MQTT connection instead of per-message
- Add close() method to MQTT connector for cleanup
- Document MQTT topic format convention in docstring
- Fix input plugin to match OM1 codebase patterns:
  - Import and use IOProvider for add_input()
  - Fix formatted_latest_buffer to use INPUT/START/END format
  - Fix raw_to_text to not return (just update self.messages)
  - Clear messages buffer after formatting
- Fix poll sleep to use remaining interval time
- Document state change detection limitation (state string only)
- Add network error tests (aiohttp.ClientError, HTTP errors)
- Add RGB out-of-range test and empty token warning test
- Fix test_poll_respects_interval and add interval-passed test
- Update formatted_latest_buffer test for new OM1 convention
- Change config verify_ssl default to true with security warning
- Fix LICENSE path in docs (../LICENSE -> ../../LICENSE)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

config Configuration files documentation Improvements or additions to documentation python Python code robotics Robotics code changes tests Test files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bounty] Smart device communication

1 participant