From b8ee0a2888755e8c2c6b3127dda448d5199fd7d4 Mon Sep 17 00:00:00 2001 From: rgc99 Date: Tue, 24 Oct 2023 23:21:37 +0000 Subject: [PATCH] Add user defined static data --- .../irrigation_unlimited/const.py | 1 + .../irrigation_unlimited.py | 33 +++++++++++++ .../irrigation_unlimited/schema.py | 9 ++++ tests/configs/test_user.yaml | 49 +++++++++++++++++++ tests/test_user.py | 26 ++++++++++ 5 files changed, 118 insertions(+) create mode 100644 tests/configs/test_user.yaml create mode 100644 tests/test_user.py diff --git a/custom_components/irrigation_unlimited/const.py b/custom_components/irrigation_unlimited/const.py index 6a15d14..89dc8bc 100644 --- a/custom_components/irrigation_unlimited/const.py +++ b/custom_components/irrigation_unlimited/const.py @@ -112,6 +112,7 @@ CONF_PRECISION = "precision" CONF_QUEUE = "queue" CONF_QUEUE_MANUAL = "queue_manual" +CONF_USER = "user" # Defaults DEFAULT_NAME = DOMAIN diff --git a/custom_components/irrigation_unlimited/irrigation_unlimited.py b/custom_components/irrigation_unlimited/irrigation_unlimited.py index b246d65..7eea496 100644 --- a/custom_components/irrigation_unlimited/irrigation_unlimited.py +++ b/custom_components/irrigation_unlimited/irrigation_unlimited.py @@ -197,6 +197,7 @@ CONF_PRECISION, CONF_QUEUE, CONF_QUEUE_MANUAL, + CONF_USER, ) _LOGGER: Logger = getLogger(__package__) @@ -499,6 +500,24 @@ def to_dict(self) -> dict: return result +class IUUser(dict): + """Class to hold arbitrary static user information""" + + def load(self, config: OrderedDict, all_zones: OrderedDict): + """Load info data from the configuration""" + + def load_params(config: OrderedDict) -> None: + if config is None: + return + for key, data in config.items(): + self[f"{CONF_USER}_{key}"] = data + + self.clear() + if all_zones is not None: + load_params(all_zones.get(CONF_USER)) + load_params(config.get(CONF_USER)) + + class IUSchedule(IUBase): """Irrigation Unlimited Schedule class. Schedules are not actual points in time but describe a future event i.e. next Monday at @@ -1609,6 +1628,7 @@ def __init__( self._dirty: bool = True self._switch = IUSwitch(hass, coordinator, controller, self) self._volume = IUVolume(hass, coordinator) + self._user = IUUser() @property def unique_id(self) -> str: @@ -1752,6 +1772,11 @@ def configuration(self) -> str: """Return this zone as JSON""" return json.dumps(self.as_dict(), cls=IUJSONEncoder) + @property + def user(self) -> dict: + """Return the arbitrary user information""" + return self._user + def _is_setup(self) -> bool: """Check if this object is setup""" self._initialised = self._zone_sensor is not None @@ -1890,6 +1915,7 @@ def load(self, config: OrderedDict, all_zones: OrderedDict) -> "IUZone": self.find_add(sidx).load(schedule_config) self._switch.load(config, all_zones) self._volume.load(config, all_zones) + self._user.load(config, all_zones) self._dirty = True return self @@ -3077,6 +3103,7 @@ def __init__( self._sensor_update_required: bool = False self._sensor_last_update: datetime = None self._suspend_until: datetime = None + self._user = IUUser() self._dirty: bool = True @property @@ -3205,6 +3232,11 @@ def icon(self) -> str: return ICON_SUSPENDED return ICON_DISABLED + @property + def user(self) -> dict: + """Return the arbitrary user information""" + return self._user + def _status(self) -> str: """Return status of the controller""" if self._initialised: @@ -3317,6 +3349,7 @@ def load(self, config: OrderedDict) -> "IUController": self._sequences.clear() self._switch.load(config, None) + self._user.load(config, None) self._dirty = True return self diff --git a/custom_components/irrigation_unlimited/schema.py b/custom_components/irrigation_unlimited/schema.py index c0b6aea..0570e99 100644 --- a/custom_components/irrigation_unlimited/schema.py +++ b/custom_components/irrigation_unlimited/schema.py @@ -85,6 +85,7 @@ CONF_PRECISION, CONF_QUEUE, CONF_QUEUE_MANUAL, + CONF_USER, ) IU_ID = r"^[a-z0-9]+(_[a-z0-9]+)*$" @@ -103,6 +104,11 @@ def _parse_dd_mmm(value: str) -> date | None: return datetime.strptime(value, "%d %b").date() +USER_SCHEMA = vol.Schema( + {}, + extra=vol.ALLOW_EXTRA, +) + SHOW_SCHEMA = vol.Schema( { vol.Optional(CONF_CONFIG, False): cv.boolean, @@ -222,6 +228,7 @@ def _parse_dd_mmm(value: str) -> date | None: vol.Optional(CONF_CHECK_BACK): vol.All(CHECK_BACK_SCHEMA), vol.Optional(CONF_VOLUME): vol.All(VOLUME_SCHEMA), vol.Optional(CONF_DURATION): cv.positive_time_period_template, + vol.Optional(CONF_USER): vol.All(USER_SCHEMA), } ) @@ -235,6 +242,7 @@ def _parse_dd_mmm(value: str) -> date | None: vol.Optional(CONF_CHECK_BACK): vol.All(CHECK_BACK_SCHEMA), vol.Optional(CONF_VOLUME): vol.All(VOLUME_SCHEMA), vol.Optional(CONF_DURATION): cv.positive_time_period_template, + vol.Optional(CONF_USER): vol.All(USER_SCHEMA), } ) @@ -281,6 +289,7 @@ def _parse_dd_mmm(value: str) -> date | None: vol.Optional(CONF_ALL_ZONES_CONFIG): vol.All(ALL_ZONES_SCHEMA), vol.Optional(CONF_QUEUE_MANUAL): cv.boolean, vol.Optional(CONF_CHECK_BACK): vol.All(CHECK_BACK_SCHEMA), + vol.Optional(CONF_USER): vol.All(USER_SCHEMA), } ) diff --git a/tests/configs/test_user.yaml b/tests/configs/test_user.yaml new file mode 100644 index 0000000..37d082c --- /dev/null +++ b/tests/configs/test_user.yaml @@ -0,0 +1,49 @@ +default_config: + +irrigation_unlimited: + refresh_interval: 2000 + testing: + enabled: true + speed: 1.0 + output_events: false + show_log: false + autoplay: false + times: + - name: "1-Sequence 1" + start: "2023-10-24 06:00" + end: "2023-10-24 07:00" + results: + - {t: '2023-10-24 06:05', c: 1, z: 0, s: 1} + - {t: '2023-10-24 06:05', c: 1, z: 1, s: 1} + - {t: '2023-10-24 06:11', c: 1, z: 1, s: 0} + - {t: '2023-10-24 06:11', c: 1, z: 0, s: 0} + - {t: '2023-10-24 06:12', c: 1, z: 0, s: 1} + - {t: '2023-10-24 06:12', c: 1, z: 2, s: 1} + - {t: '2023-10-24 06:24', c: 1, z: 2, s: 0} + - {t: '2023-10-24 06:24', c: 1, z: 0, s: 0} + controllers: + - name: "Test controller 1" + user: + area: My Farm + picture: /my_pic.jpg + all_zones_config: + user: + actuator: KNX 6.1 + zones: + - name: "Zone 1" + user: + area: Eastern Pastures + flow_rate_gallon_per_minute: 25 + picture: /my_pic.jpg + gps: 42.746635,-75.770045 + - name: "Zone 2" + sequences: + - name: "Sequence 1" + delay: "0:01:00" + schedules: + - time: "06:05" + zones: + - zone_id: 1 + duration: "0:06:00" + - zone_id: 2 + duration: "0:12:00" diff --git a/tests/test_user.py b/tests/test_user.py new file mode 100644 index 0000000..110b613 --- /dev/null +++ b/tests/test_user.py @@ -0,0 +1,26 @@ +"""irrigation_unlimited user data test""" +import homeassistant.core as ha +from tests.iu_test_support import IUExam + +IUExam.quiet_mode() + + +async def test_user(hass: ha.HomeAssistant, skip_dependencies, skip_history): + """Test IUUser class.""" + # pylint: disable=unused-argument + + # Start an examination + async with IUExam(hass, "test_user.yaml") as exam: + await exam.begin_test(1) + attr = hass.states.get("binary_sensor.irrigation_unlimited_c1_m").attributes + assert attr["user_area"] == "My Farm" + assert attr["user_picture"] == "/my_pic.jpg" + attr = hass.states.get("binary_sensor.irrigation_unlimited_c1_z1").attributes + assert attr["user_area"] == "Eastern Pastures" + assert attr["user_actuator"] == "KNX 6.1" + assert attr["user_flow_rate_gallon_per_minute"] == 25 + assert attr["user_picture"] == "/my_pic.jpg" + assert attr["user_gps"] == "42.746635,-75.770045" + await exam.finish_test() + + exam.check_summary()