diff --git a/custom_components/glinet/api.py b/custom_components/glinet/api.py index 5e45723..7ae1930 100644 --- a/custom_components/glinet/api.py +++ b/custom_components/glinet/api.py @@ -493,3 +493,17 @@ def set_wifi_config(self, config: Dict[str, Any]) -> Optional[Dict]: def get_wifi_status(self) -> Optional[Dict]: """Get WiFi device status.""" return self._make_rpc_call("wifi", "get_status") + + def get_tethering_status(self) -> Optional[Dict]: + """Get Tethering status.""" + return self._make_rpc_call("tethering", "get_status") + + def set_tethering_connect(self) -> Optional[Dict]: + """Set Tethering status.""" + params = {} + params["device"]="usb0" + return self._make_rpc_call("tethering", "set_connect",params) + + def set_tethering_disconnect(self) -> Optional[Dict]: + """Set Tethering status.""" + return self._make_rpc_call("tethering", "disconnect",) \ No newline at end of file diff --git a/custom_components/glinet/coordinator.py b/custom_components/glinet/coordinator.py index 37b8b29..cd6f130 100644 --- a/custom_components/glinet/coordinator.py +++ b/custom_components/glinet/coordinator.py @@ -63,6 +63,9 @@ async def _async_update_data(self) -> Dict[str, Any]: wifi_config = await self.hass.async_add_executor_job(self.api.get_wifi_config) wifi_status_detail = await self.hass.async_add_executor_job(self.api.get_wifi_status) clients = await self.hass.async_add_executor_job(self.api.get_clients) + + #Tethering + tethering_status = await self.hass.async_add_executor_job(self.api.get_tethering_status) return { "vpn_status": vpn_status, @@ -83,6 +86,7 @@ async def _async_update_data(self) -> Dict[str, Any]: "ovpn_server_status": ovpn_server_status, "wifi_config": wifi_config, "wifi_status_detail": wifi_status_detail, + "tethering_status": tethering_status, "clients": clients, } @@ -180,6 +184,22 @@ async def async_set_wifi_enabled(self, iface_name: str, enabled: bool) -> bool: return True return False + # Tethering methods + async def async_start_tethering(self) -> bool: + """Start Tethering.""" + result = await self.hass.async_add_executor_job(self.api.set_tethering_connect) + if result and not result.get("err_code"): + await self.async_request_refresh() + return True + return False + + async def async_stop_tethering(self) -> bool: + """Stop Tethering.""" + result = await self.hass.async_add_executor_job(self.api.set_tethering_disconnect) + if result and not result.get("err_code"): + await self.async_request_refresh() + return True + return False # Create an alias for backward compatibility GLiNetCoordinator = GLiNetDataUpdateCoordinator diff --git a/custom_components/glinet/switch.py b/custom_components/glinet/switch.py index 2bbc07a..5b3ec0b 100644 --- a/custom_components/glinet/switch.py +++ b/custom_components/glinet/switch.py @@ -51,6 +51,9 @@ async def async_setup_entry( for iface in device_config.get("ifaces", []): entities.append(GLiNetWiFiSwitch(coordinator, iface, device_config, entry)) + #Tethering switches + entities.append(GLiNetTetheringSwitch(coordinator, entry)) + async_add_entities(entities) # Register firewall services @@ -348,3 +351,68 @@ async def async_turn_off(self, **kwargs: Any) -> None: success = await self.coordinator.async_set_wifi_enabled(self.iface_name, False) if not success: _LOGGER.error("Failed to disable WiFi: %s", self.ssid) + +class GLiNetTetheringSwitch(CoordinatorEntity, SwitchEntity): + """Representation of a GL.iNet tethering switch.""" + + def __init__( + self, + coordinator: GLiNetDataUpdateCoordinator, + entry: ConfigEntry, + ) -> None: + """Initialize the switch.""" + super().__init__(coordinator) + self._attr_name = "Tethering" + self._attr_unique_id = f"{entry.entry_id}_tethering" + self._attr_icon = "mdi:cellphone" + self._attr_state = "on" if self.coordinator.data.get("tethering_status", {}).get("status", 0) == 1 else "off" + self._attr_device_info = { + "identifiers": {(DOMAIN, entry.entry_id)}, + "name": entry.title, + "manufacturer": MANUFACTURER, + "model": coordinator.data.get("system_info", {}).get("model", "Unknown"), + "sw_version": coordinator.data.get("system_info", {}).get("firmware_version", "Unknown"), + } + + @property + def is_on(self) -> bool: + """Return true if the Tethering is running.""" + tethering_status = self.coordinator.data.get("tethering_status", {}) + return tethering_status.get("status", 0) == 1 + + @property + def available(self) -> bool: + """Return if entity is available.""" + return self.coordinator.last_update_success + + @property + def extra_state_attributes(self) -> dict[str, Any]: + """Return the state attributes.""" + tethering_status = self.coordinator.data.get("tethering_status", {}) + tethering_ipv4 = tethering_status.get("ipv4") + attrs = {} + if tethering_ipv4 is not None: + attrs = { + "ip": tethering_ipv4.get("ip"), + "gateway": tethering_ipv4.get("gateway"), + } + + return attrs + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn on Tethering.""" + _LOGGER.debug("Starting Tethering") + success = await self.coordinator.async_start_tethering() + if not success: + _LOGGER.error("Failed to start Tethering") + self.async_write_ha_state() + await self.coordinator.async_request_refresh() + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn off Tethering.""" + _LOGGER.debug("Stopping Tethering") + success = await self.coordinator.async_stop_tethering() + if not success: + _LOGGER.error("Failed to stop Tethering") + self.async_write_ha_state() + await self.coordinator.async_request_refresh()