Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,9 @@ dmypy.json

# hacs

.local/
.local/

MQTT.md

# Copilot instructions
.github/copilot-instructions.md
83 changes: 47 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,66 @@

# Tinxy Home Assistant Integration

Integrate [Tinxy.in](https://tinxy.in/) smart switches and devices with Home Assistant using this custom component.
> **Important Notice**
> Users of the Tinxy cloud integration must update to version **5.0.0** before **10th March 2026**.
> Older versions will stop working after this date. Please update via HACS or manually at the earliest.

Join [Discord server](https://discord.gg/VH4jgz2f) for support.
A custom Home Assistant integration for [Tinxy.in](https://tinxy.in/) smart devices.
Uses MQTT push for real-time state updates and control with no polling and no delays.

## Overview
---

This repository provides a Home Assistant integration for Tinxy smart devices, allowing you to control switches, fans, lights, and locks directly from Home Assistant.
## Community

Have questions, need help, or want to share feedback? Join the Tinxy community on Discord.

**[Join Discord](https://discord.gg/cSPwkkQg)**

---

## Supported Devices

- Switches
- Lights (including dimmer)
- Fans (with speed presets: Low / Medium / High)
- Locks

---

## Installation

### 1. Install HACS
HACS is a community app store for Home Assistant. Follow the [HACS setup guide](https://hacs.xyz/docs/setup/prerequisites) to install.

### 2. Add Repository to HACS
Once HACS is installed:
1. Navigate to **HACS → Integrations**.
<img src="https://user-images.githubusercontent.com/693151/220521463-ff3b6de5-0abd-4f15-81cb-0a4663e3991a.png" width="400"/>
2. Click the three dots at the top right and select **Custom repositories**.
<img src="https://user-images.githubusercontent.com/693151/220522658-5c196e7e-82d7-422c-9e67-15a5e9c7d139.png" width="250"/>
3. Paste `https://github.com/arevindh/ha-tinxy-cloud`, select **Integration**, and click **Add**.
<img src="https://user-images.githubusercontent.com/693151/220522068-aeb2423a-5d78-4318-a181-1037b2299a7b.png" width="400"/>
4. Close the Custom repositories section.
5. Click **Explore & Download** at the bottom right.
![image](https://user-images.githubusercontent.com/693151/220522243-48b85c0f-59ff-45f6-b664-37157eb1ec15.png)
6. Search for `Tinxy`, then add and install it.
7. Restart Home Assistant.

## Usage

1. **Get API Key:**
- Obtain your API key from the Tinxy mobile application.
2. **Configure Integration:**
- Go to **Settings → Integrations** in Home Assistant.
- Search for "Tinxy" and click on it.
- ![screen-1](https://user-images.githubusercontent.com/693151/220121949-4f48a2ad-bae5-42e9-9167-b6bc8f524251.png)
- Enter your API key and click **Submit**.
- ![screen-2](https://user-images.githubusercontent.com/693151/220121597-624f3abf-2d28-4ca9-8764-0fb9e819e138.png)
- Click **Finish** on the next screen.
- You can find all devices in the integration screen.
### HACS (Recommended)

This integration is available in the HACS default store.

1. Open **HACS** in Home Assistant and go to **Integrations**.
2. Search for **Tinxy** and click **Download**.
3. Restart Home Assistant.

### Manual

Copy the `custom_components/tinxy` folder into your Home Assistant `custom_components` directory and restart.

---

## Configuration

- You can adjust the `scan_interval` parameter to change how often device status is updated. It is recommended to keep this above `7` seconds to avoid slowing down your Home Assistant server.
1. In Home Assistant, go to **Settings → Devices & Services → Add Integration**.
2. Search for **Tinxy**.
3. Enter your API key from the Tinxy mobile app and click **Submit**.

All paired Tinxy devices will be discovered and added automatically.

## Known Issues
---

- Due to response delays from the Tinxy API, toggling devices may have a delay of approximately 3 seconds.
## Local Control

A local-network alternative is available at [arevindh/tinxylocal](https://github.com/arevindh/tinxylocal) for users who prefer not to use the cloud. Note that the local integration supports only a limited number of older device models. **EVA series devices are not supported** by the local integration.

---

## License

See [LICENSE](LICENSE) for details.


76 changes: 66 additions & 10 deletions custom_components/tinxy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .const import DOMAIN, TINXY_BACKEND
from .tinxycloud import TinxyCloud, TinxyHostConfiguration
from .const import DOMAIN, TINXY_BACKEND, CONF_MQTT_USERNAME, CONF_MQTT_PASSWORD
from .tinxycloud import TinxyCloud, TinxyHostConfiguration, TinxyMQTTCredentials
from .coordinator import TinxyUpdateCoordinator
from .mqtt_client import TinxyMQTTClient

# Logger for this module
LOGGER = logging.getLogger(__name__)
Expand All @@ -26,6 +27,7 @@
Platform.LIGHT,
Platform.FAN,
Platform.LOCK,
Platform.SENSOR,
]

async def async_setup_entry(
Expand All @@ -47,29 +49,76 @@ async def async_setup_entry(
# Ensure domain data exists
hass.data.setdefault(DOMAIN, {})

# Create web session for API communication
# Create web session for REST API communication
web_session = async_get_clientsession(hass)

# Prepare host configuration for TinxyCloud
# Prepare host configuration
host_config = TinxyHostConfiguration(
api_token=entry.data[CONF_API_KEY],
api_url=TINXY_BACKEND,
)

# Initialize TinxyCloud API
# Initialize REST API client and fetch device list (done once at startup)
api = TinxyCloud(host_config=host_config, web_session=web_session)
try:
await api.sync_devices()
LOGGER.info("Successfully synced Tinxy devices for entry_id=%s", entry.entry_id)
LOGGER.info("Successfully synced %d Tinxy devices.", len(api.devices))
except Exception as exc:
LOGGER.error("Failed to sync Tinxy devices: %s", exc)
return False

# Create update coordinator for device state management
# Create coordinator (MQTT push-driven; performs one REST fetch on first_refresh)
coordinator = TinxyUpdateCoordinator(hass, api)

# Store API and coordinator in hass data
hass.data[DOMAIN][entry.entry_id] = (api, coordinator)
# Collect unique physical device IDs (strip the "-relay_no" suffix)
unique_device_ids: list[str] = list(
{d["device_id"] for d in api.list_all_devices()}
)

# Create MQTT client wired to coordinator's push-update method
# Credentials are cached in entry.data to avoid an API call on every startup.
# The fetcher uses the cache on the first call and force-refreshes on auth failures.
_cred_fetched_once = False

async def _get_mqtt_credentials() -> TinxyMQTTCredentials:
nonlocal _cred_fetched_once
if not _cred_fetched_once and CONF_MQTT_USERNAME in entry.data:
_cred_fetched_once = True
LOGGER.debug("Tinxy: using cached MQTT credentials.")
return TinxyMQTTCredentials(
username=entry.data[CONF_MQTT_USERNAME],
password=entry.data[CONF_MQTT_PASSWORD],
)
# Fetch fresh credentials from the API (first run or after auth failure)
creds = await api.async_get_mqtt_credentials()
hass.config_entries.async_update_entry(
entry,
data={
**entry.data,
CONF_MQTT_USERNAME: creds.username,
CONF_MQTT_PASSWORD: creds.password,
},
)
_cred_fetched_once = True
LOGGER.debug("Tinxy: MQTT credentials fetched and cached.")
return creds

mqtt_client = TinxyMQTTClient(
hass=hass,
on_state_update=coordinator.async_update_from_mqtt,
credentials_fetcher=_get_mqtt_credentials,
)
# Give the REST client a reference so set_device_state() uses MQTT
api.mqtt_client = mqtt_client

# Start MQTT connection in the background
await mqtt_client.async_start(unique_device_ids)
LOGGER.info(
"Tinxy MQTT client started for %d physical devices.", len(unique_device_ids)
)

# Store references for platforms and unload
hass.data[DOMAIN][entry.entry_id] = (api, coordinator, mqtt_client)

# Forward setup to supported platforms
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
Expand All @@ -93,9 +142,16 @@ async def async_unload_entry(
bool: True if unload was successful, False otherwise.
"""
LOGGER.info("Unloading Tinxy integration for entry_id=%s", entry.entry_id)

# Stop MQTT client before unloading platforms
entry_data = hass.data[DOMAIN].get(entry.entry_id)
if entry_data and len(entry_data) >= 3:
mqtt_client: TinxyMQTTClient = entry_data[2]
await mqtt_client.async_stop()
LOGGER.info("Tinxy MQTT client stopped.")

unload_ok: bool = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
# Remove stored API and coordinator
hass.data[DOMAIN].pop(entry.entry_id, None)
LOGGER.info("Tinxy integration unloaded for entry_id=%s", entry.entry_id)
else:
Expand Down
6 changes: 6 additions & 0 deletions custom_components/tinxy/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@
DOMAIN = "tinxy"
COORDINATOR = "coordinator"
TINXY_BACKEND = "https://ha-backend.tinxy.in/"
MQTT_SERVER = "mqtt.tinxy.in"
MQTT_PORT = 1883

# Config entry keys for persisted MQTT credentials
CONF_MQTT_USERNAME = "mqtt_username"
CONF_MQTT_PASSWORD = "mqtt_password"
Loading
Loading