chore: initial baseline before refactoring
Initial snapshot of Home Assistant config (HAOS 2026.4.4)
prior to dashboard restructuring and helper YAML migration.
Pre-commit cleanup applied:
- Removed blueprints/switch_manager/ (9.4 MB stale, component already gone)
- Removed .storage/switch_manager
- Removed home-assistant.log.{1,old,fault}
- Removed zigbee2mqtt/configuration_backup_v{1..4}.yaml
- Removed zigbee2mqtt/database.db.tmp.2024-09-27 (1.5y old)
- Created empty themes/ to satisfy configuration.yaml include
.gitignore uses allowlist strategy for .storage/ to keep
all tokens, keys, and PINs out of version control.
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import abc
|
||||
from dataclasses import dataclass
|
||||
|
||||
from cx_const import Number, StepperDir
|
||||
|
||||
|
||||
class MinMax:
|
||||
def __init__(self, min: Number, max: Number, margin: float = 0.05) -> None:
|
||||
self._min = min
|
||||
self._max = max
|
||||
self.margin_dist = (max - min) * margin
|
||||
|
||||
@property
|
||||
def min(self) -> Number:
|
||||
return self._min
|
||||
|
||||
@property
|
||||
def max(self) -> Number:
|
||||
return self._max
|
||||
|
||||
def is_min(self, value: Number) -> bool:
|
||||
return self._min == value
|
||||
|
||||
def is_max(self, value: Number) -> bool:
|
||||
return self._max == value
|
||||
|
||||
def is_between(self, value: Number) -> bool:
|
||||
return self._min < value < self._max
|
||||
|
||||
def in_min_boundaries(self, value: Number) -> bool:
|
||||
return self._min <= value <= (self._min + self.margin_dist)
|
||||
|
||||
def in_max_boundaries(self, value: Number) -> bool:
|
||||
return (self._max - self.margin_dist) <= value <= self._max
|
||||
|
||||
def clip(self, value: Number) -> Number:
|
||||
return max(self._min, min(value, self._max))
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"MinMax({self.min}, {self.max})"
|
||||
|
||||
|
||||
@dataclass
|
||||
class StepperOutput:
|
||||
next_value: Number
|
||||
next_direction: str | None
|
||||
|
||||
@property
|
||||
def exceeded(self) -> bool:
|
||||
return self.next_direction is None
|
||||
|
||||
|
||||
class Stepper(abc.ABC):
|
||||
sign_mapping = {StepperDir.UP: 1, StepperDir.DOWN: -1}
|
||||
|
||||
min_max: MinMax
|
||||
steps: Number
|
||||
previous_direction: str
|
||||
relative_steps: bool
|
||||
|
||||
@staticmethod
|
||||
def invert_direction(direction: str) -> str:
|
||||
return StepperDir.UP if direction == StepperDir.DOWN else StepperDir.DOWN
|
||||
|
||||
@staticmethod
|
||||
def sign(direction: str) -> int:
|
||||
return Stepper.sign_mapping[direction]
|
||||
|
||||
@staticmethod
|
||||
def apply_sign(value: Number, direction: str) -> Number:
|
||||
return Stepper.sign(direction) * value
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
min_max: MinMax,
|
||||
steps: Number,
|
||||
previous_direction: str = StepperDir.DOWN,
|
||||
relative_steps: bool = True,
|
||||
) -> None:
|
||||
self.min_max = min_max
|
||||
self.steps = steps
|
||||
self.previous_direction = previous_direction
|
||||
self.relative_steps = relative_steps
|
||||
|
||||
def _compute_step(self) -> float:
|
||||
if self.relative_steps:
|
||||
max_ = self.min_max.max
|
||||
min_ = self.min_max.min
|
||||
return (max_ - min_) / self.steps
|
||||
else:
|
||||
return self.steps
|
||||
|
||||
def get_direction(self, value: Number, direction: str) -> str:
|
||||
if direction == StepperDir.TOGGLE:
|
||||
direction = Stepper.invert_direction(self.previous_direction)
|
||||
self.previous_direction = direction
|
||||
return direction
|
||||
|
||||
@abc.abstractmethod
|
||||
def step(self, value: Number, direction: str) -> StepperOutput:
|
||||
"""
|
||||
This function updates the value according to the steps
|
||||
that needs to take and returns the new value together with
|
||||
the new direction it will need to go. If next_direction is
|
||||
None, the loop will stop executing.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class InvertStepper(Stepper):
|
||||
def step(self, value: Number, direction: str) -> StepperOutput:
|
||||
return StepperOutput(self.apply_sign(value, direction), next_direction=None)
|
||||
@@ -0,0 +1,17 @@
|
||||
from cx_const import Number
|
||||
from cx_core.stepper import Stepper, StepperOutput
|
||||
|
||||
|
||||
class BounceStepper(Stepper):
|
||||
def step(self, value: Number, direction: str) -> StepperOutput:
|
||||
value = self.min_max.clip(value)
|
||||
step = self._compute_step()
|
||||
|
||||
new_value = value + Stepper.apply_sign(step, direction)
|
||||
if self.min_max.is_between(new_value):
|
||||
return StepperOutput(round(new_value, 3), next_direction=direction)
|
||||
else:
|
||||
new_value = 2 * self.min_max.clip(new_value) - new_value
|
||||
return StepperOutput(
|
||||
round(new_value, 3), next_direction=Stepper.invert_direction(direction)
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
from cx_const import Number, StepperDir
|
||||
from cx_core.stepper import MinMax, Stepper, StepperOutput
|
||||
|
||||
|
||||
class IndexLoopStepper(Stepper):
|
||||
def __init__(
|
||||
self,
|
||||
size: int,
|
||||
previous_direction: str = StepperDir.DOWN,
|
||||
relative_steps: bool = True,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
MinMax(0, size - 1),
|
||||
size,
|
||||
previous_direction,
|
||||
relative_steps,
|
||||
)
|
||||
|
||||
def step(self, value: Number, direction: str) -> StepperOutput:
|
||||
value = self.min_max.clip(value)
|
||||
sign = self.sign(direction)
|
||||
# We add +1 to make the max be included
|
||||
max_ = int(self.min_max.max) + 1
|
||||
min_ = int(self.min_max.min)
|
||||
if self.relative_steps:
|
||||
step = (max_ - min_) // self.steps
|
||||
else:
|
||||
step = self.steps
|
||||
|
||||
new_value = (int(value) + step * sign) % (max_ - min_) + min_
|
||||
return StepperOutput(new_value, next_direction=direction)
|
||||
@@ -0,0 +1,16 @@
|
||||
from cx_const import Number
|
||||
from cx_core.stepper import Stepper, StepperOutput
|
||||
|
||||
|
||||
class LoopStepper(Stepper):
|
||||
def step(self, value: Number, direction: str) -> StepperOutput:
|
||||
value = self.min_max.clip(value)
|
||||
max_ = self.min_max.max
|
||||
min_ = self.min_max.min
|
||||
step = self._compute_step()
|
||||
|
||||
new_value = (
|
||||
((value + Stepper.apply_sign(step, direction)) - min_) % (max_ - min_)
|
||||
) + min_
|
||||
new_value = round(new_value, 3)
|
||||
return StepperOutput(new_value, next_direction=direction)
|
||||
@@ -0,0 +1,26 @@
|
||||
from cx_const import Number, StepperDir
|
||||
from cx_core.stepper import Stepper, StepperOutput
|
||||
|
||||
|
||||
class StopStepper(Stepper):
|
||||
def get_direction(self, value: Number, direction: str) -> str:
|
||||
value = self.min_max.clip(value)
|
||||
if direction == StepperDir.TOGGLE and self.min_max.in_min_boundaries(value):
|
||||
self.previous_direction = StepperDir.UP
|
||||
return self.previous_direction
|
||||
if direction == StepperDir.TOGGLE and self.min_max.in_max_boundaries(value):
|
||||
self.previous_direction = StepperDir.DOWN
|
||||
return self.previous_direction
|
||||
return super().get_direction(value, direction)
|
||||
|
||||
def step(self, value: Number, direction: str) -> StepperOutput:
|
||||
value = self.min_max.clip(value)
|
||||
step = self._compute_step()
|
||||
|
||||
new_value = value + Stepper.apply_sign(step, direction)
|
||||
new_value = round(new_value, 3)
|
||||
if self.min_max.is_between(new_value):
|
||||
return StepperOutput(new_value, next_direction=direction)
|
||||
else:
|
||||
new_value = self.min_max.clip(new_value)
|
||||
return StepperOutput(new_value, next_direction=None)
|
||||
Reference in New Issue
Block a user