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
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,29 @@
# refoss_rpc
Only support R11
- Home Assistant version: 2025.2.5 or above

## Installation

### Custom Repositories in HACS (recommended)
- Make sure the [HACS integration](https://hacs.xyz/) is properly installed for your instance of home assistant.
- Reference [Custom Repositories](https://hacs.xyz/docs/faq/custom_repositories),In the HACS UI go to "Integrations", click on "+" in the lower right corner".
- Paste https://github.com/Refoss/refoss_rpc into the field that says "Add custom repository URL", select "Integration" from "Category" dropdown and click "Add".
- You should now see a card with the Refoss RPC integration in the HACS -> "Integrations" section. Click "Install".
- Select the latest version from the dropdown and click "Install".
- Restart Home Assistant.

### Manual installation
- Using the tool of choice open the directory for your HA configuration (where you find configuration.yaml).
- If you do not have a custom_components directory there, you need to create it.
- In releases(https://github.com/Refoss/refoss_rpc/releases), download the version you need.
- In the downloaded file, locate the refoss_rpc directory and copy it to the custom_components directory.
- Restart Home Assistant.

## Configuration
- In the HA UI go to "Configuration" -> "Integrations", click "+", search for "Refoss RPC", and select the "Refoss RPC" integration from the list.
Or click here: [![Start Config Flow](https://my.home-assistant.io/badges/config_flow_start.svg)](https://my.home-assistant.io/redirect/config_flow_start?domain=refoss_rpc)

## Supported device models

| Model | Version |
|-------------------------------------|--------------------|
| `Refoss Smart Wi-Fi Switch, R11` | `all` |
2 changes: 0 additions & 2 deletions custom_components/refoss_rpc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
RefossConfigEntry,
RefossCoordinator,
RefossEntryData,
RefossPollingCoordinator,
)

PLATFORMS: Final = [
Expand Down Expand Up @@ -73,7 +72,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: RefossConfigEntry) -> bo

runtime_data.coordinator = RefossCoordinator(hass, entry, device)
runtime_data.coordinator.async_setup()
runtime_data.poll_coordinator = RefossPollingCoordinator(hass, entry, device)
await hass.config_entries.async_forward_entry_setups(entry, runtime_data.platforms)

return True
Expand Down
10 changes: 3 additions & 7 deletions custom_components/refoss_rpc/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,8 @@

LOGGER: Logger = getLogger(__package__)


# Refresh interval for polling
REFOSS_SENSORS_POLLING_INTERVAL: Final = 60

# Reconnect interval for devices
REFOSS_RECONNECT_INTERVAL = 60
#Check interval for devices
REFOSS_CHECK_INTERVAL = 60


# Button Click events for devices
Expand All @@ -41,7 +37,7 @@
UPTIME_DEVIATION: Final = 5

# Time to wait before reloading entry when device config change
ENTRY_RELOAD_COOLDOWN = 60
ENTRY_RELOAD_COOLDOWN = 30


OTA_BEGIN = "ota_begin"
Expand Down
46 changes: 16 additions & 30 deletions custom_components/refoss_rpc/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@
OTA_ERROR,
OTA_PROGRESS,
OTA_SUCCESS,
REFOSS_RECONNECT_INTERVAL,
REFOSS_SENSORS_POLLING_INTERVAL,
REFOSS_CHECK_INTERVAL,
)
from .utils import get_host, update_device_fw_info

Expand All @@ -54,7 +53,6 @@ class RefossEntryData:

platforms: list[Platform]
coordinator: RefossCoordinator | None = None
poll_coordinator: RefossPollingCoordinator | None = None


RefossConfigEntry = ConfigEntry[RefossEntryData]
Expand Down Expand Up @@ -174,7 +172,7 @@ def __init__(
) -> None:
"""Initialize the Refoss coordinator."""
self.entry = entry
super().__init__(hass, entry, device, REFOSS_RECONNECT_INTERVAL)
super().__init__(hass, entry, device, REFOSS_CHECK_INTERVAL)

self.connected = False
self._connection_lock = asyncio.Lock()
Expand Down Expand Up @@ -255,11 +253,22 @@ async def _async_update_data(self) -> None:
"""Fetch data."""
if self.hass.is_stopping:
return

async with self._connection_lock:
if self.device.connected: # Already connected
if not self.device.connected:
if not await self._async_device_connect_task():
raise UpdateFailed("Device reconnect error")
return
if not await self._async_device_connect_task():
raise UpdateFailed("Device reconnect error")
try:
LOGGER.debug("Polling Refoss Device - %s", self.name)
await self.device.poll()
except DeviceConnectionError as err:
raise UpdateFailed(f"Device disconnected: {err!r}") from err
except RpcCallError as err:
raise UpdateFailed(f"RPC call failed: {err!r}") from err
except InvalidAuthError:
await self.async_shutdown_device_and_start_reauth()
return

async def _async_disconnected(self, reconnect: bool) -> None:
"""Handle device disconnected."""
Expand Down Expand Up @@ -346,29 +355,6 @@ async def shutdown(self) -> None:
await self._async_disconnected(False)


class RefossPollingCoordinator(RefossCoordinatorBase):
"""Polling coordinator for a Refoss device."""

def __init__(
self, hass: HomeAssistant, entry: RefossConfigEntry, device: RpcDevice
) -> None:
"""Initialize the polling coordinator."""
super().__init__(hass, entry, device, REFOSS_SENSORS_POLLING_INTERVAL)

async def _async_update_data(self) -> None:
"""Fetch data."""
if not self.device.connected:
raise UpdateFailed("Device disconnected")

LOGGER.debug("Polling Refoss Device - %s", self.name)
try:
await self.device.poll()
except (DeviceConnectionError, RpcCallError) as err:
raise UpdateFailed(f"Device disconnected: {err!r}") from err
except InvalidAuthError:
await self.async_shutdown_device_and_start_reauth()


def get_refoss_coordinator_by_device_id(
hass: HomeAssistant, device_id: str
) -> RefossCoordinator | None:
Expand Down
7 changes: 0 additions & 7 deletions custom_components/refoss_rpc/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ def async_setup_entry_refoss(
if not coordinator.device.initialized:
return

polling_coordinator = config_entry.runtime_data.poll_coordinator

entities = []
for sensor_id in sensors:
description = sensors[sensor_id]
Expand All @@ -63,10 +61,6 @@ def async_setup_entry_refoss(
domain = sensor_class.__module__.split(".")[-1]
unique_id = f"{coordinator.mac}-{key}-{sensor_id}"
async_remove_refoss_entity(hass, domain, unique_id)
elif description.use_polling_coordinator:
entities.append(
sensor_class(polling_coordinator, key, sensor_id, description)
)
else:
entities.append(sensor_class(coordinator, key, sensor_id, description))
if not entities:
Expand All @@ -84,7 +78,6 @@ class RefossEntityDescription(EntityDescription):

value: Callable[[Any, Any], Any] | None = None
removal_condition: Callable[[dict, dict, str], bool] | None = None
use_polling_coordinator: bool = False
supported: Callable = lambda _: False


Expand Down
3 changes: 0 additions & 3 deletions custom_components/refoss_rpc/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ class RefossSensorDescription(RefossEntityDescription, SensorEntityDescription):
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
use_polling_coordinator=True,
),
"rssi": RefossSensorDescription(
key="wifi",
Expand All @@ -105,7 +104,6 @@ class RefossSensorDescription(RefossEntityDescription, SensorEntityDescription):
removal_condition=is_refoss_wifi_stations_disabled,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
use_polling_coordinator=True,
),
"uptime": RefossSensorDescription(
key="sys",
Expand All @@ -115,7 +113,6 @@ class RefossSensorDescription(RefossEntityDescription, SensorEntityDescription):
device_class=SensorDeviceClass.TIMESTAMP,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
use_polling_coordinator=True,
),
}

Expand Down
2 changes: 1 addition & 1 deletion custom_components/refoss_rpc/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"flow_title": "{name}",
"step": {
"user": {
"description": "Before setup, devices must be connected to the network.\n\nThis path can be configured for refoss product models including R11, etc. \n\nFor more information, please refer to 'Help'.",
"description": "Before setup, devices must be connected to the network.\n\nThis path can be configured for Refoss product models including R11, etc. \n\nFor more information, please refer to 'Help'.",
"data": {
"host": "[%key:common::config_flow::data::host%]"
},
Expand Down
2 changes: 1 addition & 1 deletion custom_components/refoss_rpc/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"data_description": {
"host": "The hostname or IP address of the Refoss device to connect to."
},
"description": "Before setup, devices must be connected to the network.\n\nThis path can be configured for refoss product models including R11, etc. \n\nFor more information, please refer to 'Help'."
"description": "Before setup, devices must be connected to the network.\n\nThis path can be configured for Refoss product models including R11, etc. \n\nFor more information, please refer to 'Help'."
}
}
},
Expand Down