Skip to content

Commit

Permalink
Merge pull request #158 from jaroschek/release/1.4.x
Browse files Browse the repository at this point in the history
Release 1.4.0
  • Loading branch information
jaroschek authored Sep 22, 2024
2 parents 2801b53 + b4b5548 commit 0c6e268
Show file tree
Hide file tree
Showing 17 changed files with 305 additions and 48 deletions.
50 changes: 49 additions & 1 deletion custom_components/myuplink/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
API_HOST,
API_VERSION,
CONF_ADDITIONAL_PARAMETER,
CONF_ENABLE_SMART_HOME_MODE,
CONF_FETCH_FIRMWARE,
CONF_FETCH_NOTIFICATIONS,
CONF_PARAMETER_WHITELIST,
Expand Down Expand Up @@ -441,6 +442,9 @@ class System:
# List of collected devices
devices: list[Device] = []

# Smart home mode of the system
smart_home_mode: str = "Default"

def __init__(self, raw_data: dict, api: MyUplink) -> None:
"""Initialize a system object."""
self.raw_data = raw_data
Expand Down Expand Up @@ -473,6 +477,9 @@ async def async_fetch_data(self) -> None:
Device(device_data, self) for device_data in self.raw_data["devices"]
]

if self.api.entry.options.get(CONF_ENABLE_SMART_HOME_MODE, True):
self.smart_home_mode = await self.api.get_smart_home_mode(self)

fetch_notifications = self.api.entry.options.get(CONF_FETCH_NOTIFICATIONS, True)
if fetch_notifications:
notifications = await self.api.get_notifications(self)
Expand All @@ -486,6 +493,10 @@ async def async_fetch_data(self) -> None:

await device.async_fetch_data()

async def update_smart_home_mode(self, value) -> None:
"""Put smart home mode for system."""
await self.api.put_smart_home_mode(self.id, str(value))


class Throttle:
"""Throttling requests to API."""
Expand Down Expand Up @@ -592,6 +603,43 @@ async def get_notifications(self, system: System) -> list[Notification]:

return [Notification(notification) for notification in data["notifications"]]

async def get_smart_home_mode(self, system: System) -> str:
"""Return smart home mode by system id."""
_LOGGER.debug("Fetch smart home mode for system %s", system.id)
async with self.lock, self.throttle:
resp = await self.auth.request(
"get", f"systems/{system.id}/smart-home-mode"
)
resp.raise_for_status()
data = await resp.json()

return data["smartHomeMode"]

async def put_smart_home_mode(self, system_id, value: str) -> bool:
"""Set the smart home mode for a system."""
_LOGGER.debug(
"Put smart home mode for system %s with value %s",
system_id,
value,
)
async with self.lock, self.throttle:
resp = await self.auth.request(
"put",
f"systems/{system_id}/smart-home-mode",
data=json.dumps({"smartHomeMode": value}),
headers={"Content-Type": "application/json-patch+json"},
)
resp.raise_for_status()
if resp.status == 200:
data = await resp.json()
return (
"payload" in data
and "state" in data["payload"]
and data["payload"]["state"] == "ok"
)

return False

async def get_device(self, device_id: str) -> Device:
"""Return a device by id."""
_LOGGER.debug("Fetch device with id %s", device_id)
Expand Down Expand Up @@ -667,7 +715,7 @@ async def get_zones(self, device_id) -> list[Zone]:
return [Zone(zone) for zone in await resp.json()]

async def patch_parameter(self, device_id, parameter_id: str, value: str) -> bool:
"""Return all smart home zones for a device."""
"""Update the value of a parameter for a device."""
_LOGGER.debug(
"Patch parameter %s for device %s with value %s",
parameter_id,
Expand Down
6 changes: 3 additions & 3 deletions custom_components/myuplink/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@

from .api import Device, Parameter
from .const import DOMAIN
from .entity import MyUplinkEntity, MyUplinkParameterEntity
from .entity import MyUplinkDeviceEntity, MyUplinkParameterEntity

_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up the sensors."""
"""Set up the platform entities."""

coordinator = hass.data[DOMAIN][entry.entry_id]
entities: list[BinarySensorEntity] = []
Expand Down Expand Up @@ -57,7 +57,7 @@ def _update_from_parameter(self, parameter: Parameter) -> None:
self._attr_device_class = BinarySensorDeviceClass.RUNNING


class MyUplinkConnectedBinarySensor(MyUplinkEntity, BinarySensorEntity):
class MyUplinkConnectedBinarySensor(MyUplinkDeviceEntity, BinarySensorEntity):
"""Representation of an myUplink connected sensor."""

_attr_device_class = BinarySensorDeviceClass.CONNECTIVITY
Expand Down
5 changes: 5 additions & 0 deletions custom_components/myuplink/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from .const import (
CONF_ADDITIONAL_PARAMETER,
CONF_DISCONNECTED_AVAILABLE,
CONF_ENABLE_SMART_HOME_MODE,
CONF_EXPERT_MODE,
CONF_FETCH_FIRMWARE,
CONF_FETCH_NOTIFICATIONS,
Expand All @@ -47,6 +48,10 @@ def get_options_schema(data: ConfigType) -> Schema:
"""Return the options schema."""
return vol.Schema(
{
vol.Required(
CONF_ENABLE_SMART_HOME_MODE,
default=data.get(CONF_ENABLE_SMART_HOME_MODE, True),
): selector.BooleanSelector(),
vol.Required(
CONF_FETCH_FIRMWARE, default=data.get(CONF_FETCH_FIRMWARE, True)
): selector.BooleanSelector(),
Expand Down
11 changes: 11 additions & 0 deletions custom_components/myuplink/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

CONF_ADDITIONAL_PARAMETER = "additional_parameter"
CONF_DISCONNECTED_AVAILABLE = "disconnected_available"
CONF_ENABLE_SMART_HOME_MODE = "enable_smart_home_mode"
CONF_EXPERT_MODE = "expert_mode"
CONF_FETCH_FIRMWARE = "fetch_firmware"
CONF_FETCH_NOTIFICATIONS = "fetch_notifications"
Expand Down Expand Up @@ -82,3 +83,13 @@ class CustomUnits(StrEnum):
TIME_DAYS = "days"
TIME_HOUR = "hour"
TIME_HOURS = "hours"


class SmartHomeModes(StrEnum):
"""Smart home modes."""

DEFAULT = "Default"
NORMAL = "Normal"
AWAY = "Away"
VACATION = "Vacation"
HOME = "Home"
49 changes: 45 additions & 4 deletions custom_components/myuplink/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,52 @@
DataUpdateCoordinator,
)

from .api import Device, Parameter
from .api import Device, Parameter, System
from .const import CONF_DISCONNECTED_AVAILABLE, DOMAIN


class MyUplinkEntity(CoordinatorEntity):
"""Base class for myUplink Entities."""
class MyUplinkSystemEntity(CoordinatorEntity):
"""Base class for myUplink system entities."""

def __init__(self, coordinator: DataUpdateCoordinator, system: System) -> None:
"""Initialize class."""
super().__init__(coordinator)
self._attr_unique_id = f"{DOMAIN}_{system.id}"
self._update_from_system(system)

@property
def device_info(self):
"""Return the device_info of the device."""
name_data = self._system.name.split()
model = name_data[0]
manufacturer = None
if len(name_data) > 1:
# Assumes first word in raw name is manufacturer
model = " ".join(name_data[1:])
manufacturer = name_data[0]
return DeviceInfo(
identifiers={(DOMAIN, self._system.id)},
manufacturer=manufacturer,
model=model,
name=self._system.name,
)

def _update_from_system(self, system: System) -> None:
"""Update attrs from system."""
self._system = system

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
for system in self.coordinator.data:
if system.id == self._system.id:
self._update_from_system(system)

super().async_write_ha_state()


class MyUplinkDeviceEntity(CoordinatorEntity):
"""Base class for myUplink device entities."""

def __init__(self, coordinator: DataUpdateCoordinator, device: Device) -> None:
"""Initialize class."""
Expand All @@ -37,6 +77,7 @@ def device_info(self):
manufacturer=manufacturer,
model=model,
name=self._device.name,
serial_number=self._device.serial_number,
sw_version=self._device.current_firmware_version,
)

Expand All @@ -55,7 +96,7 @@ def _handle_coordinator_update(self) -> None:
super().async_write_ha_state()


class MyUplinkParameterEntity(MyUplinkEntity):
class MyUplinkParameterEntity(MyUplinkDeviceEntity):
"""Representation of a myUplink parameter entity."""

def __init__(
Expand Down
2 changes: 1 addition & 1 deletion custom_components/myuplink/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
"issue_tracker": "https://github.com/jaroschek/home-assistant-myuplink/issues",
"requirements": [],
"ssdp": [],
"version": "1.3.1",
"version": "1.4.0",
"zeroconf": []
}
2 changes: 1 addition & 1 deletion custom_components/myuplink/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up the sensors."""
"""Set up the platform entities."""

coordinator = hass.data[DOMAIN][entry.entry_id]
entities: list[NumberEntity] = []
Expand Down
83 changes: 77 additions & 6 deletions custom_components/myuplink/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,41 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .api import Parameter
from .const import DOMAIN
from .entity import MyUplinkParameterEntity
from .api import Device, Parameter, System
from .const import (
CONF_DISCONNECTED_AVAILABLE,
CONF_ENABLE_SMART_HOME_MODE,
DOMAIN,
SmartHomeModes,
)
from .entity import MyUplinkDeviceEntity, MyUplinkParameterEntity, MyUplinkSystemEntity

_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up the sensors."""
"""Set up the platform entities."""

coordinator = hass.data[DOMAIN][entry.entry_id]
entities: list[SelectEntity] = []

enable_smart_home_mode = entry.options.get(CONF_ENABLE_SMART_HOME_MODE, True)

for system in coordinator.data:
if enable_smart_home_mode:
if len(system.devices) == 1:
entities.append(
MyUplinkSmartHomeModeDeviceSelectEntity(
coordinator, system.devices[0]
)
)
else:
entities.append(
MyUplinkSmartHomeModeSystemSelectEntity(coordinator, system)
)

for device in system.devices:
[
entities.append(
Expand All @@ -39,7 +58,7 @@ async def async_setup_entry(


class MyUplinkParameterSelectEntity(MyUplinkParameterEntity, SelectEntity):
"""Representation of a myUplink paramater binary sensor."""
"""Representation of a myUplink paramater select sensor."""

def _update_from_parameter(self, parameter: Parameter) -> None:
"""Update attrs from parameter."""
Expand All @@ -51,9 +70,61 @@ def _update_from_parameter(self, parameter: Parameter) -> None:
self._attr_current_option = parameter.string_value

async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
"""Change the selected parameter option."""
options = {}
for enum in self._parameter.enum_values:
options[enum["text"]] = enum["value"]
await self._parameter.update_parameter(options[option])
await self.async_update()


class MyUplinkSmartHomeModeDeviceSelectEntity(MyUplinkDeviceEntity, SelectEntity):
"""Representation of a myUplink smart home mode select sensor for a single device system."""

_attr_has_entity_name = True

def _update_from_device(self, device: Device) -> None:
"""Update attrs from device."""
super()._update_from_device(device)
self._attr_translation_key = f"{DOMAIN}_smart_home_mode"
self._attr_unique_id = f"{DOMAIN}_{device.system.id}_smart_home_mode"
self._attr_options = []
for mode in SmartHomeModes:
self._attr_options.append(mode.lower())
self._attr_current_option = device.system.smart_home_mode.lower()

async def async_select_option(self, option: str) -> None:
"""Change the selected smart home mode option."""
await self._device.system.update_smart_home_mode(option.title())
await self.async_update()

@property
def available(self):
"""Return if the device is online."""
return super().available and (
self._device.connection_state == "Connected"
or self._device.system.api.entry.options.get(
CONF_DISCONNECTED_AVAILABLE, False
)
)


class MyUplinkSmartHomeModeSystemSelectEntity(MyUplinkSystemEntity, SelectEntity):
"""Representation of a myUplink smart home mode select sensor for a multi device system."""

_attr_has_entity_name = True

def _update_from_system(self, system: System) -> None:
"""Update attrs from system."""
super()._update_from_system(system)
self._attr_translation_key = f"{DOMAIN}_smart_home_mode"
self._attr_unique_id = f"{DOMAIN}_{system.id}_smart_home_mode"
self._attr_options = []
for mode in SmartHomeModes:
self._attr_options.append(mode.lower())
self._attr_current_option = system.smart_home_mode.lower()

async def async_select_option(self, option: str) -> None:
"""Change the selected smart home mode option."""
await self._system.update_smart_home_mode(option.title())
await self.async_update()
Loading

0 comments on commit 0c6e268

Please sign in to comment.