From d76ff770ea012ccc75cd547233e4bf2621dd7ae1 Mon Sep 17 00:00:00 2001 From: basbruss <4a3.brussee.bas@gmail.com> Date: Thu, 23 May 2024 16:11:07 +0200 Subject: [PATCH 1/3] Add global elevation controls --- .../adaptive_cover/calculation.py | 17 +++++-- .../adaptive_cover/config_flow.py | 48 ++++++++++++++----- custom_components/adaptive_cover/const.py | 2 + .../adaptive_cover/coordinator.py | 4 ++ 4 files changed, 56 insertions(+), 15 deletions(-) diff --git a/custom_components/adaptive_cover/calculation.py b/custom_components/adaptive_cover/calculation.py index 0398f93..0f3bd1d 100644 --- a/custom_components/adaptive_cover/calculation.py +++ b/custom_components/adaptive_cover/calculation.py @@ -34,6 +34,8 @@ class AdaptiveGeneralCover(ABC): blind_spot_left: int blind_spot_right: int blind_spot_elevation: int + min_elevation: int + max_elevation: int sun_data: SunData = field(init=False) def __post_init__(self): @@ -81,8 +83,6 @@ def is_sun_in_blind_spot(self) -> bool: return blindspot return False - - @property def azi_min_abs(self) -> int: """Calculate min azimuth.""" @@ -102,6 +102,17 @@ def gamma(self) -> float: gamma = (self.win_azi - self.sol_azi + 180) % 360 - 180 return gamma + @property + def valid_elevation(self) -> bool: + """Check if elevation is within range.""" + if self.min_elevation is None and self.max_elevation is None: + return self.sol_elev >= 0 + if self.min_elevation is None: + return self.sol_elev <= self.max_elevation + if self.max_elevation is None: + return self.sol_elev >= self.min_elevation + return self.min_elevation <= self.sol_elev <= self.max_elevation + @property def valid(self) -> bool: """Determine if sun is in front of window.""" @@ -110,7 +121,7 @@ def valid(self) -> bool: azi_max = min(self.fov_right, 90) # valid sun positions are those within the blind's azimuth range and above the horizon (FOV) - valid = (self.gamma < azi_min) & (self.gamma > -azi_max) & (self.sol_elev >= 0) + valid = (self.gamma < azi_min) & (self.gamma > -azi_max) & (self.valid_elevation) return valid @property diff --git a/custom_components/adaptive_cover/config_flow.py b/custom_components/adaptive_cover/config_flow.py index 74d18b7..718347f 100644 --- a/custom_components/adaptive_cover/config_flow.py +++ b/custom_components/adaptive_cover/config_flow.py @@ -37,7 +37,9 @@ CONF_LENGTH_AWNING, CONF_MANUAL_OVERRIDE_DURATION, CONF_MANUAL_OVERRIDE_RESET, + CONF_MAX_ELEVATION, CONF_MAX_POSITION, + CONF_MIN_ELEVATION, CONF_MODE, CONF_OUTSIDETEMP_ENTITY, CONF_PRESENCE_ENTITY, @@ -99,6 +101,8 @@ min=1, max=100, step=1, mode="slider", unit_of_measurement="%" ) ), + vol.Optional(CONF_MIN_ELEVATION): vol.All(vol.Coerce(int), vol.Range(min=0, max=90)), + vol.Optional(CONF_MAX_ELEVATION): vol.All(vol.Coerce(int), vol.Range(min=0, max=90)), vol.Required(CONF_FOV_LEFT, default=90): selector.NumberSelector( selector.NumberSelectorConfig( min=1, max=90, step=1, mode="slider", unit_of_measurement="°" @@ -294,6 +298,10 @@ } ) +def _get_azimuth_edges(data) -> tuple[int,int]: + """Calculate azimuth edges.""" + return data[CONF_FOV_LEFT] + data[CONF_FOV_RIGHT] + class ConfigFlowHandler(ConfigFlow, domain=DOMAIN): """Handle ConfigFlow.""" @@ -309,9 +317,7 @@ def async_get_options_flow(config_entry): """Get the options flow for this handler.""" return OptionsFlowHandler(config_entry) - def _get_azimuth_edges(self, data) -> tuple[int,int]: - """Calculate azimuth edges.""" - return data[CONF_FOV_LEFT] + data[CONF_FOV_RIGHT] + async def async_step_user(self, user_input: dict[str, Any] | None = None): """Handle the initial step.""" @@ -366,12 +372,12 @@ async def async_step_tilt(self, user_input: dict[str, Any] | None = None): async def async_step_blind_spot(self, user_input: dict[str, Any] | None = None): """Add blindspot to data.""" - edges = self._get_azimuth_edges(self.config) + edges = _get_azimuth_edges(self.config) schema = vol.Schema( { vol.Required(CONF_BLIND_SPOT_LEFT,default=0): selector.NumberSelector(selector.NumberSelectorConfig(mode="slider", unit_of_measurement="°", min=0, max=edges-1)), vol.Required(CONF_BLIND_SPOT_RIGHT,default=1):selector.NumberSelector(selector.NumberSelectorConfig(mode="slider", unit_of_measurement="°", min=1, max=edges)), - vol.Optional(CONF_BLIND_SPOT_ELEVATION, default=90):selector.NumberSelector(selector.NumberSelectorConfig(mode="slider", unit_of_measurement="°", min=0, max=90)) + vol.Optional(CONF_BLIND_SPOT_ELEVATION): vol.All(vol.Coerce(int), vol.Range(min=0, max=90)) } ) if user_input is not None: @@ -459,9 +465,12 @@ async def async_step_update(self, user_input: dict[str, Any] | None = None): CONF_MANUAL_OVERRIDE_DURATION ), CONF_MANUAL_OVERRIDE_RESET: self.config.get(CONF_MANUAL_OVERRIDE_RESET), - CONF_BLIND_SPOT_RIGHT: self.config.get(CONF_BLIND_SPOT_RIGHT), - CONF_BLIND_SPOT_LEFT: self.config.get(CONF_BLIND_SPOT_LEFT), - CONF_BLIND_SPOT_ELEVATION: self.config.get(CONF_BLIND_SPOT_ELEVATION), + CONF_BLIND_SPOT_RIGHT: self.config.get(CONF_BLIND_SPOT_RIGHT, None), + CONF_BLIND_SPOT_LEFT: self.config.get(CONF_BLIND_SPOT_LEFT, None), + CONF_BLIND_SPOT_ELEVATION: self.config.get(CONF_BLIND_SPOT_ELEVATION, None), + CONF_ENABLE_BLIND_SPOT: self.config.get(CONF_ENABLE_BLIND_SPOT), + CONF_MIN_ELEVATION: self.config.get(CONF_MIN_ELEVATION, None), + CONF_MAX_ELEVATION: self.config.get(CONF_MAX_ELEVATION, None), }, ) @@ -525,6 +534,11 @@ async def async_step_vertical(self, user_input: dict[str, Any] | None = None): if self.options[CONF_CLIMATE_MODE]: schema = VERTICAL_OPTIONS if user_input is not None: + keys = [ + CONF_MIN_ELEVATION, + CONF_MAX_ELEVATION, + ] + self.optional_entities(keys, user_input) self.options.update(user_input) if self.options[CONF_CLIMATE_MODE]: return await self.async_step_climate() @@ -543,6 +557,11 @@ async def async_step_horizontal(self, user_input: dict[str, Any] | None = None): if self.options[CONF_CLIMATE_MODE]: schema = HORIZONTAL_OPTIONS if user_input is not None: + keys = [ + CONF_MIN_ELEVATION, + CONF_MAX_ELEVATION, + ] + self.optional_entities(keys, user_input) self.options.update(user_input) if self.options[CONF_CLIMATE_MODE]: return await self.async_step_climate() @@ -561,6 +580,11 @@ async def async_step_tilt(self, user_input: dict[str, Any] | None = None): if self.options[CONF_CLIMATE_MODE]: schema = TILT_OPTIONS if user_input is not None: + keys = [ + CONF_MIN_ELEVATION, + CONF_MAX_ELEVATION, + ] + self.optional_entities(keys, user_input) self.options.update(user_input) if self.options[CONF_CLIMATE_MODE]: return await self.async_step_climate() @@ -574,12 +598,12 @@ async def async_step_tilt(self, user_input: dict[str, Any] | None = None): async def async_step_blind_spot(self, user_input: dict[str, Any] | None = None): """Add blindspot to data.""" - edges = self._get_azimuth_edges(self.options) + edges = _get_azimuth_edges(self.options) schema = vol.Schema( { vol.Required(CONF_BLIND_SPOT_LEFT,default=0): selector.NumberSelector(selector.NumberSelectorConfig(mode="slider", unit_of_measurement="°", min=0, max=edges-1)), vol.Required(CONF_BLIND_SPOT_RIGHT,default=1):selector.NumberSelector(selector.NumberSelectorConfig(mode="slider", unit_of_measurement="°", min=1, max=edges)), - vol.Optional(CONF_BLIND_SPOT_ELEVATION, default=90):selector.NumberSelector(selector.NumberSelectorConfig(mode="slider", unit_of_measurement="°", min=0, max=90)) + vol.Optional(CONF_BLIND_SPOT_ELEVATION):vol.All(vol.Coerce(int), vol.Range(min=0, max=90)) } ) if user_input is not None: @@ -590,8 +614,8 @@ async def async_step_blind_spot(self, user_input: dict[str, Any] | None = None): errors={CONF_BLIND_SPOT_RIGHT: "Must be greater than 'Blind Spot Left Edge'"} ) self.options.update(user_input) - return await self.async_step_automation() - return self.async_show_form(step_id="blind_spot", data_schema=schema) + return await self._update_options() + return self.async_show_form(step_id="blind_spot", data_schema=self.add_suggested_values_to_schema(schema, user_input or self.options)) async def async_step_climate(self, user_input: dict[str, Any] | None = None): """Manage climate options.""" diff --git a/custom_components/adaptive_cover/const.py b/custom_components/adaptive_cover/const.py index a782a59..eccdf30 100644 --- a/custom_components/adaptive_cover/const.py +++ b/custom_components/adaptive_cover/const.py @@ -44,6 +44,8 @@ CONF_BLIND_SPOT_RIGHT = "blind_spot_right" CONF_BLIND_SPOT_LEFT = "blind_spot_left" CONF_BLIND_SPOT_ELEVATION = "blind_spot_elevation" +CONF_MIN_ELEVATION = "min_elevation" +CONF_MAX_ELEVATION = "max_elevation" CONF_DELTA_POSITION = "delta_position" diff --git a/custom_components/adaptive_cover/coordinator.py b/custom_components/adaptive_cover/coordinator.py index a27a1e6..506cdde 100644 --- a/custom_components/adaptive_cover/coordinator.py +++ b/custom_components/adaptive_cover/coordinator.py @@ -48,7 +48,9 @@ CONF_LENGTH_AWNING, CONF_MANUAL_OVERRIDE_DURATION, CONF_MANUAL_OVERRIDE_RESET, + CONF_MAX_ELEVATION, CONF_MAX_POSITION, + CONF_MIN_ELEVATION, CONF_OUTSIDETEMP_ENTITY, CONF_PRESENCE_ENTITY, CONF_START_ENTITY, @@ -415,6 +417,8 @@ def common_data(self): self.config_entry.options.get(CONF_BLIND_SPOT_LEFT), self.config_entry.options.get(CONF_BLIND_SPOT_RIGHT), self.config_entry.options.get(CONF_BLIND_SPOT_ELEVATION), + self.config_entry.options.get(CONF_MIN_ELEVATION, None), + self.config_entry.options.get(CONF_MAX_ELEVATION, None), ] @property From 70b7a14197b0d50503559af8f59b4b04eeec3622 Mon Sep 17 00:00:00 2001 From: basbruss <4a3.brussee.bas@gmail.com> Date: Thu, 23 May 2024 16:37:40 +0200 Subject: [PATCH 2/3] Add check to config flow --- .../adaptive_cover/config_flow.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/custom_components/adaptive_cover/config_flow.py b/custom_components/adaptive_cover/config_flow.py index 718347f..5197826 100644 --- a/custom_components/adaptive_cover/config_flow.py +++ b/custom_components/adaptive_cover/config_flow.py @@ -336,6 +336,13 @@ async def async_step_vertical(self, user_input: dict[str, Any] | None = None): """Show basic config for vertical blinds.""" self.type_blind = SensorType.BLIND if user_input is not None: + if user_input[CONF_MAX_ELEVATION] is not None and user_input[CONF_MIN_ELEVATION] is not None: + if user_input[CONF_MAX_ELEVATION] <= user_input[CONF_MIN_ELEVATION]: + return self.async_show_form( + step_id="vertical", + data_schema=CLIMATE_MODE.extend(VERTICAL_OPTIONS.schema), + errors={CONF_MAX_ELEVATION: "Must be greater than 'Minimal Elevation'"} + ) self.config.update(user_input) if self.config[CONF_ENABLE_BLIND_SPOT]: return await self.async_step_blind_spot() @@ -349,6 +356,13 @@ async def async_step_horizontal(self, user_input: dict[str, Any] | None = None): """Show basic config for horizontal blinds.""" self.type_blind = SensorType.AWNING if user_input is not None: + if user_input[CONF_MAX_ELEVATION] is not None and user_input[CONF_MIN_ELEVATION] is not None: + if user_input[CONF_MAX_ELEVATION] <= user_input[CONF_MIN_ELEVATION]: + return self.async_show_form( + step_id="horizontal", + data_schema=CLIMATE_MODE.extend(HORIZONTAL_OPTIONS.schema), + errors={CONF_MAX_ELEVATION: "Must be greater than 'Minimal Elevation'"} + ) self.config.update(user_input) if self.config[CONF_ENABLE_BLIND_SPOT]: return await self.async_step_blind_spot() @@ -362,6 +376,13 @@ async def async_step_tilt(self, user_input: dict[str, Any] | None = None): """Show basic config for tilted blinds.""" self.type_blind = SensorType.TILT if user_input is not None: + if user_input[CONF_MAX_ELEVATION] is not None and user_input[CONF_MIN_ELEVATION] is not None: + if user_input[CONF_MAX_ELEVATION] <= user_input[CONF_MIN_ELEVATION]: + return self.async_show_form( + step_id="tilt", + data_schema=CLIMATE_MODE.extend(TILT_OPTIONS.schema), + errors={CONF_MAX_ELEVATION: "Must be greater than 'Minimal Elevation'"} + ) self.config.update(user_input) if self.config[CONF_ENABLE_BLIND_SPOT]: return await self.async_step_blind_spot() @@ -539,6 +560,13 @@ async def async_step_vertical(self, user_input: dict[str, Any] | None = None): CONF_MAX_ELEVATION, ] self.optional_entities(keys, user_input) + if user_input[CONF_MAX_ELEVATION] is not None and user_input[CONF_MIN_ELEVATION] is not None: + if user_input[CONF_MAX_ELEVATION] <= user_input[CONF_MIN_ELEVATION]: + return self.async_show_form( + step_id="vertical", + data_schema=CLIMATE_MODE.extend(VERTICAL_OPTIONS.schema), + errors={CONF_MAX_ELEVATION: "Must be greater than 'Minimal Elevation'"} + ) self.options.update(user_input) if self.options[CONF_CLIMATE_MODE]: return await self.async_step_climate() @@ -562,6 +590,13 @@ async def async_step_horizontal(self, user_input: dict[str, Any] | None = None): CONF_MAX_ELEVATION, ] self.optional_entities(keys, user_input) + if user_input[CONF_MAX_ELEVATION] is not None and user_input[CONF_MIN_ELEVATION] is not None: + if user_input[CONF_MAX_ELEVATION] <= user_input[CONF_MIN_ELEVATION]: + return self.async_show_form( + step_id="horizontal", + data_schema=CLIMATE_MODE.extend(HORIZONTAL_OPTIONS.schema), + errors={CONF_MAX_ELEVATION: "Must be greater than 'Minimal Elevation'"} + ) self.options.update(user_input) if self.options[CONF_CLIMATE_MODE]: return await self.async_step_climate() @@ -585,6 +620,13 @@ async def async_step_tilt(self, user_input: dict[str, Any] | None = None): CONF_MAX_ELEVATION, ] self.optional_entities(keys, user_input) + if user_input[CONF_MAX_ELEVATION] is not None and user_input[CONF_MIN_ELEVATION] is not None: + if user_input[CONF_MAX_ELEVATION] <= user_input[CONF_MIN_ELEVATION]: + return self.async_show_form( + step_id="tilt", + data_schema=CLIMATE_MODE.extend(TILT_OPTIONS.schema), + errors={CONF_MAX_ELEVATION: "Must be greater than 'Minimal Elevation'"} + ) self.options.update(user_input) if self.options[CONF_CLIMATE_MODE]: return await self.async_step_climate() From 74119df8cdd2a104678c53ea18a1b56ac1bf1835 Mon Sep 17 00:00:00 2001 From: basbruss <4a3.brussee.bas@gmail.com> Date: Thu, 23 May 2024 20:24:06 +0200 Subject: [PATCH 3/3] Add strings and translations --- custom_components/adaptive_cover/strings.json | 24 ++++++++++++++----- .../adaptive_cover/translations/en.json | 24 ++++++++++++++----- .../adaptive_cover/translations/nl.json | 24 ++++++++++++++----- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/custom_components/adaptive_cover/strings.json b/custom_components/adaptive_cover/strings.json index 0610105..dfb1650 100644 --- a/custom_components/adaptive_cover/strings.json +++ b/custom_components/adaptive_cover/strings.json @@ -47,7 +47,9 @@ "sunset_offset": "Sunset Offset", "sunrise_offset": "Sunrise Offset", "climate_mode": "Climate Mode", - "blind_spot": "Setup Blindspot" + "blind_spot": "Setup Blindspot", + "min_elevation": "Minimum Elevation of the sun", + "max_elevation": "Maximum Elevation of the sun" }, "data_description": { "set_azimuth": "Adjust Azimuth", @@ -110,7 +112,9 @@ "sunset_offset": "Sunset Offset", "sunrise_offset": "Sunrise Offset", "climate_mode": "Climate Mode", - "blind_spot": "Setup Blindspot" + "blind_spot": "Setup Blindspot", + "min_elevation": "Minimum Elevation of the sun", + "max_elevation": "Maximum Elevation of the sun" }, "data_description": { "set_azimuth": "Specify the Azimuth", @@ -148,7 +152,9 @@ "sunrise_offset": "Sunrise Offset", "climate_mode": "Climate Mode", "tilt_mode": "Type of movement", - "blind_spot": "Setup Blindspot" + "blind_spot": "Setup Blindspot", + "min_elevation": "Minimum Elevation of the sun", + "max_elevation": "Maximum Elevation of the sun" }, "data_description": { "set_azimuth": "Specify the Azimuth", @@ -232,7 +238,9 @@ "sunset_offset": "Sunset Offset", "sunrise_offset": "Sunrise Offset", "climate_mode": "Climate Mode", - "blind_spot": "Setup Blindspot" + "blind_spot": "Setup Blindspot", + "min_elevation": "Minimum Elevation of the sun", + "max_elevation": "Maximum Elevation of the sun" }, "data_description": { "set_azimuth": "Adjust Azimuth", @@ -295,7 +303,9 @@ "sunset_offset": "Sunset Offset", "sunrise_offset": "Sunrise Offset", "climate_mode": "Climate Mode", - "blind_spot": "Setup Blindspot" + "blind_spot": "Setup Blindspot", + "min_elevation": "Minimum Elevation of the sun", + "max_elevation": "Maximum Elevation of the sun" }, "data_description": { "set_azimuth": "Specify the Azimuth", @@ -333,7 +343,9 @@ "sunrise_offset": "Sunrise Offset", "climate_mode": "Climate Mode", "tilt_mode": "Type of movement", - "blind_spot": "Setup Blindspot" + "blind_spot": "Setup Blindspot", + "min_elevation": "Minimum Elevation of the sun", + "max_elevation": "Maximum Elevation of the sun" }, "data_description": { "set_azimuth": "Specify the Azimuth", diff --git a/custom_components/adaptive_cover/translations/en.json b/custom_components/adaptive_cover/translations/en.json index 0610105..dfb1650 100644 --- a/custom_components/adaptive_cover/translations/en.json +++ b/custom_components/adaptive_cover/translations/en.json @@ -47,7 +47,9 @@ "sunset_offset": "Sunset Offset", "sunrise_offset": "Sunrise Offset", "climate_mode": "Climate Mode", - "blind_spot": "Setup Blindspot" + "blind_spot": "Setup Blindspot", + "min_elevation": "Minimum Elevation of the sun", + "max_elevation": "Maximum Elevation of the sun" }, "data_description": { "set_azimuth": "Adjust Azimuth", @@ -110,7 +112,9 @@ "sunset_offset": "Sunset Offset", "sunrise_offset": "Sunrise Offset", "climate_mode": "Climate Mode", - "blind_spot": "Setup Blindspot" + "blind_spot": "Setup Blindspot", + "min_elevation": "Minimum Elevation of the sun", + "max_elevation": "Maximum Elevation of the sun" }, "data_description": { "set_azimuth": "Specify the Azimuth", @@ -148,7 +152,9 @@ "sunrise_offset": "Sunrise Offset", "climate_mode": "Climate Mode", "tilt_mode": "Type of movement", - "blind_spot": "Setup Blindspot" + "blind_spot": "Setup Blindspot", + "min_elevation": "Minimum Elevation of the sun", + "max_elevation": "Maximum Elevation of the sun" }, "data_description": { "set_azimuth": "Specify the Azimuth", @@ -232,7 +238,9 @@ "sunset_offset": "Sunset Offset", "sunrise_offset": "Sunrise Offset", "climate_mode": "Climate Mode", - "blind_spot": "Setup Blindspot" + "blind_spot": "Setup Blindspot", + "min_elevation": "Minimum Elevation of the sun", + "max_elevation": "Maximum Elevation of the sun" }, "data_description": { "set_azimuth": "Adjust Azimuth", @@ -295,7 +303,9 @@ "sunset_offset": "Sunset Offset", "sunrise_offset": "Sunrise Offset", "climate_mode": "Climate Mode", - "blind_spot": "Setup Blindspot" + "blind_spot": "Setup Blindspot", + "min_elevation": "Minimum Elevation of the sun", + "max_elevation": "Maximum Elevation of the sun" }, "data_description": { "set_azimuth": "Specify the Azimuth", @@ -333,7 +343,9 @@ "sunrise_offset": "Sunrise Offset", "climate_mode": "Climate Mode", "tilt_mode": "Type of movement", - "blind_spot": "Setup Blindspot" + "blind_spot": "Setup Blindspot", + "min_elevation": "Minimum Elevation of the sun", + "max_elevation": "Maximum Elevation of the sun" }, "data_description": { "set_azimuth": "Specify the Azimuth", diff --git a/custom_components/adaptive_cover/translations/nl.json b/custom_components/adaptive_cover/translations/nl.json index ab2cf15..c43af07 100644 --- a/custom_components/adaptive_cover/translations/nl.json +++ b/custom_components/adaptive_cover/translations/nl.json @@ -46,7 +46,9 @@ "sunset_position": "Zonsondergang Posistie", "sunset_offset": "Afwijking Zonsondergang", "climate_mode": "Klimaatmodus", - "blind_spot": "Configureer schaduwzone" + "blind_spot": "Configureer schaduwzone", + "min_elevation": "Minimale zonhoogte", + "max_elevation": "Maximale zonhoogte" }, "data_description": { "set_azimuth": "Stel Azimuth in", @@ -110,7 +112,9 @@ "sunset_position": "Zonsondergang Posistie", "sunset_offset": "Afwijking Zonsondergang", "climate_mode": "Klimaat Modus", - "blind_spot": "Configureer schaduwzone" + "blind_spot": "Configureer schaduwzone", + "min_elevation": "Minimale zonhoogte", + "max_elevation": "Maximale zonhoogte" }, "data_description": { "set_azimuth": "Stel Azimuth in", @@ -147,7 +151,9 @@ "sunset_offset": "Afwijking Zonsondergang", "climate_mode": "Klimaat Modus", "tilt_mode": "Type beweging", - "blind_spot": "Configureer schaduwzone" + "blind_spot": "Configureer schaduwzone", + "min_elevation": "Minimale zonhoogte", + "max_elevation": "Maximale zonhoogte" }, "data_description": { "set_azimuth": "Stel Azimuth in", @@ -230,7 +236,9 @@ "sunset_position": "Zonsondergang Posistie", "sunset_offset": "Afwijking Zonsondergang", "climate_mode": "Klimaatmodus", - "blind_spot": "Configureer schaduwzone" + "blind_spot": "Configureer schaduwzone", + "min_elevation": "Minimale zonhoogte", + "max_elevation": "Maximale zonhoogte" }, "data_description": { "set_azimuth": "Stel Azimuth in", @@ -294,7 +302,9 @@ "sunset_position": "Zonsondergang Posistie", "sunset_offset": "Afwijking Zonsondergang", "climate_mode": "Klimaat Modus", - "blind_spot": "Configureer schaduwzone" + "blind_spot": "Configureer schaduwzone", + "min_elevation": "Minimale zonhoogte", + "max_elevation": "Maximale zonhoogte" }, "data_description": { "set_azimuth": "Stel Azimuth in", @@ -331,7 +341,9 @@ "sunset_offset": "Afwijking Zonsondergang", "climate_mode": "Klimaat Modus", "tilt_mode": "Type beweging", - "blind_spot": "Configureer schaduwzone" + "blind_spot": "Configureer schaduwzone", + "min_elevation": "Minimale zonhoogte", + "max_elevation": "Maximale zonhoogte" }, "data_description": { "set_azimuth": "Stel Azimuth in",