Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds support for timer to on and timer to off, configured in minutes #24

Merged
merged 1 commit into from
Sep 15, 2023
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
1 change: 1 addition & 0 deletions custom_components/ac_infinity/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
Platform.SELECT,
Platform.NUMBER,
Platform.TIME,
Platform.TEXT,
]
HOST = "http://www.acinfinityserver.com"

Expand Down
4 changes: 2 additions & 2 deletions custom_components/ac_infinity/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ def _handle_coordinator_update(self) -> None:
async def async_set_native_value(self, value: int) -> None:
await self.set_setting_value(value)
_LOGGER.debug(
"User updated value of %s.%s %s",
"User updated value of %s.%s to %s",
self._attr_unique_id,
self._data_key,
self._attr_native_value,
value,
)


Expand Down
2 changes: 1 addition & 1 deletion custom_components/ac_infinity/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ async def async_select_option(self, option: str) -> None:
index + 1
) # data is 1 based. Adjust from 0 based enum
_LOGGER.debug(
"User updated value of %s.%s %s",
"User updated value of %s.%s to %s",
self._attr_unique_id,
self._data_key,
option,
Expand Down
96 changes: 96 additions & 0 deletions custom_components/ac_infinity/text.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import logging

from homeassistant.components.text import TextEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from custom_components.ac_infinity import (
ACInfinityDataUpdateCoordinator,
ACInfinityPortEntity,
)
from custom_components.ac_infinity.ac_infinity import (
ACInfinityDevice,
ACInfinityDevicePort,
)
from custom_components.ac_infinity.const import (
DOMAIN,
SETTING_KEY_ACTIVE_TIMER_OFF,
SETTING_KEY_ACTIVE_TIMER_ON,
)

_LOGGER = logging.getLogger(__name__)
TIMER_REGEX = r"^(0|[1-9]\d{0,2}|1[0-3]\d{2}|1440)$" # timer minute values must be between 0 and 1440 (24 hours)


class ACInfinityPortTimerEntity(ACInfinityPortEntity, TextEntity):
def __init__(
self,
coordinator: ACInfinityDataUpdateCoordinator,
device: ACInfinityDevice,
port: ACInfinityDevicePort,
data_key: str,
label: str,
) -> None:
super().__init__(coordinator, device, port, data_key, label, "mdi:timer")

self._attr_pattern = TIMER_REGEX
self._attr_native_value = self.__get_timer_seting_value_as_str()

@callback
def _handle_coordinator_update(self) -> None:
self._attr_native_value = self.__get_timer_seting_value_as_str()

self.async_write_ha_state()
_LOGGER.debug(
"%s._attr_native_value updated to %s",
self._attr_unique_id,
self._attr_native_value,
)

async def async_set_value(self, value: str) -> None:
await self.set_setting_value(
int(value) * 60
) # timers are stored as seconds. we configure them as minutes
_LOGGER.debug(
"User updated value of %s.%s to %s",
self._attr_unique_id,
self._data_key,
value,
)

def __get_timer_seting_value_as_str(self):
return str(
self.get_setting_value(default=0) // 60
) # timers are stored as seconds. we configure them as minutes


async def async_setup_entry(
hass: HomeAssistant, config: ConfigEntry, add_entities_callback: AddEntitiesCallback
) -> None:
"""Setup the AC Infinity Platform."""

coordinator: ACInfinityDataUpdateCoordinator = hass.data[DOMAIN][config.entry_id]

select_entities = {
SETTING_KEY_ACTIVE_TIMER_ON: {
"label": "Minutes to On",
},
SETTING_KEY_ACTIVE_TIMER_OFF: {
"label": "Minutes to Off",
},
}

devices = coordinator.ac_infinity.get_all_device_meta_data()

entities = []
for device in devices:
for port in device.ports:
for key, descr in select_entities.items():
entities.append(
ACInfinityPortTimerEntity(
coordinator, device, port, key, str(descr["label"])
)
)

add_entities_callback(entities)
4 changes: 2 additions & 2 deletions custom_components/ac_infinity/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ async def async_set_value(self, value: time) -> None:
total_minutes = None if value is None else (value.hour * 60) + value.minute
await self.set_setting_value(total_minutes)
_LOGGER.debug(
"User updated value of %s.%s %s",
"User updated value of %s.%s to %s",
self._attr_unique_id,
self._data_key,
self._attr_native_value,
total_minutes,
)


Expand Down
110 changes: 110 additions & 0 deletions tests/test_text.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import asyncio
from asyncio import Future

import pytest
from pytest_mock import MockFixture

from custom_components.ac_infinity.const import (
DOMAIN,
SETTING_KEY_ACTIVE_TIMER_OFF,
SETTING_KEY_ACTIVE_TIMER_ON,
)
from custom_components.ac_infinity.text import (
ACInfinityPortTimerEntity,
async_setup_entry,
)
from tests import ACTestObjects, execute_and_get_port_entity, setup_entity_mocks
from tests.data_models import DEVICE_ID, MAC_ADDR


@pytest.fixture
def setup(mocker: MockFixture):
return setup_entity_mocks(mocker)


@pytest.mark.asyncio
class TestText:
set_data_mode_value = 0

async def test_async_setup_all_sensors_created(self, setup):
"""All sensors created"""
test_objects: ACTestObjects = setup

await async_setup_entry(
test_objects.hass,
test_objects.configEntry,
test_objects.entities.add_entities_callback,
)

assert len(test_objects.entities._added_entities) == 8

@pytest.mark.parametrize(
"key", [SETTING_KEY_ACTIVE_TIMER_ON, SETTING_KEY_ACTIVE_TIMER_OFF]
)
@pytest.mark.parametrize("port", [1, 2, 3, 4])
async def test_async_setup_schedule_end_time_created_for_each_port(
self, setup, key, port
):
"""Setting for scheduled end time created on setup"""

sensor: ACInfinityPortTimerEntity = await execute_and_get_port_entity(
setup, async_setup_entry, port, key
)

assert "Minutes to" in sensor._attr_name
assert sensor._attr_unique_id == f"{DOMAIN}_{MAC_ADDR}_port_{port}_{key}"

@pytest.mark.parametrize(
"setting", [SETTING_KEY_ACTIVE_TIMER_ON, SETTING_KEY_ACTIVE_TIMER_OFF]
)
@pytest.mark.parametrize(
"value,expected",
[(86400, "1440"), (1440, "24"), (0, "0")], # minutes to seconds
)
@pytest.mark.parametrize("port", [1, 2, 3, 4])
async def test_async_update_value_Correct(
self,
setup,
setting,
value,
expected,
port,
):
"""Reported sensor value matches the value in the json payload"""

test_objects: ACTestObjects = setup
sensor: ACInfinityPortTimerEntity = await execute_and_get_port_entity(
setup, async_setup_entry, port, setting
)

test_objects.ac_infinity._port_settings[str(DEVICE_ID)][port][setting] = value
sensor._handle_coordinator_update()

assert sensor._attr_native_value == expected
test_objects.write_ha_mock.assert_called()

@pytest.mark.parametrize(
"expected,field_value",
[(86400, "1440"), (1440, "24"), (0, "0")], # minutes to seconds
)
@pytest.mark.parametrize(
"setting", [SETTING_KEY_ACTIVE_TIMER_ON, SETTING_KEY_ACTIVE_TIMER_OFF]
)
@pytest.mark.parametrize("port", [1, 2, 3, 4])
async def test_async_set_native_value(
self, setup, setting, expected: int, port, field_value
):
"""Reported sensor value matches the value in the json payload"""
future: Future = asyncio.Future()
future.set_result(None)

test_objects: ACTestObjects = setup

sensor: ACInfinityPortTimerEntity = await execute_and_get_port_entity(
setup, async_setup_entry, port, setting
)
await sensor.async_set_value(field_value)

test_objects.set_mock.assert_called_with(
str(DEVICE_ID), port, setting, expected
)
2 changes: 1 addition & 1 deletion tests/test_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def setup(mocker: MockFixture):


@pytest.mark.asyncio
class TestNumbers:
class TestTime:
set_data_mode_value = 0

async def test_async_setup_all_sensors_created(self, setup):
Expand Down