Implement number+uom, rename wrap_back to handler

This commit is contained in:
Edward Brown 2025-11-26 11:36:39 +00:00
parent f054115dcd
commit 21d1ac15c3
5 changed files with 148 additions and 144 deletions

View file

@ -34,7 +34,7 @@ class ClientHaco(mqtt.Client):
payload = message.payload.decode()
logger.info(f"{message.topic.value}{Constants.ARROW}{payload}")
topic_command = self.device.subscriptions[message.topic.value]
echo_val = await topic_command.wrap_back(message)
echo_val = await topic_command.handle(message)

View file

@ -1,38 +1,32 @@
from dataclasses import dataclass
from enum import StrEnum
from typing import ClassVar, Type
from haco.control import Control
from haco.tools import get_range_pair
from haco.data.uom import Uom
from haco.uom import Uom
from haco.utils import Converters, ConvertersNumeric
class Mode(StrEnum):
BOX = "box"
SLIDER = "slider"
@dataclass(kw_only=True)
class Number(Control):
NAME = 'number'
UOM = Uom
DATA = dict(
platform='number'
)
converters: ClassVar[Type[Converters]] = ConvertersNumeric
# def init(name, number_range, mode, step, uom, entity_id, icon, callbacks)
def __init__(self, name, number_range: range, icon=None, mode=None, step=None, uom=None):
min: float = 0.
max: float = 100.
mode: Mode = Mode.SLIDER
step: float = 1.
uom: Uom | None = None
self.min, self.max = get_range_pair(number_range)
self.mode = mode
self.step = step
self.uom = uom
def command(self, value):
raise NotImplementedError()
super().__init__(name, icon=icon)
@property
def control_type(self):
if self.step is None or self.step == 1:
return int
else:
return float
def get_config_ha_ex(self):
data = {
'min': self.min,
'max': self.max,
'mode': self.mode,
'unit_of_measurement': self.uom,
'step': self.step
}
return data
def state(self, value):
raise NotImplementedError()

View file

@ -34,7 +34,7 @@ class AnnounceTopic:
method_name = self.callback_name
method = getattr(self.capability.control, method_name, None)
self.control_method = method
setattr(self.capability.control, method_name, self.wrap_back)
setattr(self.capability.control, method_name, self.handle)
def fill(self, mask):
return mask.format(**self.fills)
@ -76,7 +76,7 @@ class AnnounceTopic:
def capability(self):
return self.parent
def wrap_back(self, value):
async def handle(self, value):
raise NotImplementedError()
@ -87,7 +87,7 @@ class AnnounceTopicState(AnnounceTopic):
def get_subscriptions(self):
return {}
async def wrap_back(self, value):
async def handle(self, value=None):
if not self.control_method:
logger.error(f'Incomplete base class: {self.callback_class_method_name}')
@ -117,7 +117,7 @@ class AnnounceTopicCommand(AnnounceTopic):
def state(self):
return self.capability.state
async def wrap_back(self, message):
async def handle(self, message):
if not self.control_method:
logger.error(f'Incomplete base class: {self.callback_class_method_name}')
@ -141,7 +141,7 @@ class AnnounceTopicCommand(AnnounceTopic):
# Echo back as new state
topic_state = self.state
if topic_state:
await topic_state.wrap_back(value_raw)
await topic_state.handle(value_raw)
def get_subscriptions(self):

View file

@ -1,138 +1,142 @@
class Uom:
from enum import StrEnum
class Uom(StrEnum):
"""
Sourced from: https://github.com/home-assistant/core/blob/03b1c7ad1deb4b9494df01b27ef8e1d7dfe179ba/homeassistant/const.py#L489C1-L489C1
Sourced from: https://github.com/home-assistant/core/blob/03b1c7ad1deb4b9494df01b27ef8e1d7dfe179ba/homeassistant/const.py#L489
"""
POWER_WATT: str = "W"
POWER_KILO_WATT: str = "kW"
POWER_WATT = "W"
POWER_KILO_WATT = "kW"
VOLT: str = "V"
VOLT = "V"
ENERGY_WATT_HOUR: str = "Wh"
ENERGY_KILO_WATT_HOUR: str = "kWh"
ENERGY_WATT_HOUR = "Wh"
ENERGY_KILO_WATT_HOUR = "kWh"
ELECTRICAL_CURRENT_AMPERE: str = "A"
ELECTRICAL_VOLT_AMPERE: str = "VA"
ELECTRICAL_CURRENT_AMPERE = "A"
ELECTRICAL_VOLT_AMPERE = "VA"
DEGREE: str = "°"
DEGREE = "°"
CURRENCY_EURO: str = "€"
CURRENCY_DOLLAR: str = "$"
CURRENCY_CENT: str = "¢"
CURRENCY_EURO = "€"
CURRENCY_DOLLAR = "$"
CURRENCY_CENT = "¢"
TEMP_CELSIUS: str = "°C"
TEMP_FAHRENHEIT: str = "°F"
TEMP_KELVIN: str = "K"
TEMP_CELSIUS = "°C"
TEMP_FAHRENHEIT = "°F"
TEMP_KELVIN = "K"
TIME_MICROSECONDS: str = "μs"
TIME_MILLISECONDS: str = "ms"
TIME_SECONDS: str = "s"
TIME_MINUTES: str = "min"
TIME_HOURS: str = "h"
TIME_DAYS: str = "d"
TIME_WEEKS: str = "w"
TIME_MONTHS: str = "m"
TIME_YEARS: str = "y"
TIME_MICROSECONDS = "μs"
TIME_MILLISECONDS = "ms"
TIME_SECONDS = "s"
TIME_MINUTES = "min"
TIME_HOURS = "h"
TIME_DAYS = "d"
TIME_WEEKS = "w"
TIME_MONTHS = "m"
TIME_YEARS = "y"
LENGTH_MILLIMETERS: str = "mm"
LENGTH_CENTIMETERS: str = "cm"
LENGTH_METERS: str = "m"
LENGTH_KILOMETERS: str = "km"
LENGTH_MILLIMETERS = "mm"
LENGTH_CENTIMETERS = "cm"
LENGTH_METERS = "m"
LENGTH_KILOMETERS = "km"
LENGTH_INCHES: str = "in"
LENGTH_FEET: str = "ft"
LENGTH_YARD: str = "yd"
LENGTH_MILES: str = "mi"
LENGTH_INCHES = "in"
LENGTH_FEET = "ft"
LENGTH_YARD = "yd"
LENGTH_MILES = "mi"
FREQUENCY_HERTZ: str = "Hz"
FREQUENCY_GIGAHERTZ: str = "GHz"
FREQUENCY_HERTZ = "Hz"
FREQUENCY_GIGAHERTZ = "GHz"
PRESSURE_PA: str = "Pa"
PRESSURE_HPA: str = "hPa"
PRESSURE_BAR: str = "bar"
PRESSURE_MBAR: str = "mbar"
PRESSURE_INHG: str = "inHg"
PRESSURE_PSI: str = "psi"
PRESSURE_PA = "Pa"
PRESSURE_HPA = "hPa"
PRESSURE_BAR = "bar"
PRESSURE_MBAR = "mbar"
PRESSURE_INHG = "inHg"
PRESSURE_PSI = "psi"
VOLUME_LITERS: str = "L"
VOLUME_MILLILITERS: str = "mL"
VOLUME_CUBIC_METERS: str = "m³"
VOLUME_CUBIC_FEET: str = "ft³"
VOLUME_LITERS = "L"
VOLUME_MILLILITERS = "mL"
VOLUME_CUBIC_METERS = "m³"
VOLUME_CUBIC_FEET = "ft³"
VOLUME_GALLONS: str = "gal"
VOLUME_FLUID_OUNCE: str = "fl. oz."
VOLUME_GALLONS = "gal"
VOLUME_FLUID_OUNCE = "fl. oz."
VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR: str = "m³/h"
VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE: str = "ft³/m"
VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR = "m³/h"
VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE = "ft³/m"
AREA_SQUARE_METERS: str = "m²"
AREA_SQUARE_METERS = "m²"
MASS_GRAMS: str = "g"
MASS_KILOGRAMS: str = "kg"
MASS_MILLIGRAMS: str = "mg"
MASS_MICROGRAMS: str = "µg"
MASS_GRAMS = "g"
MASS_KILOGRAMS = "kg"
MASS_MILLIGRAMS = "mg"
MASS_MICROGRAMS = "µg"
MASS_OUNCES: str = "oz"
MASS_POUNDS: str = "lb"
MASS_OUNCES = "oz"
MASS_POUNDS = "lb"
CONDUCTIVITY: str = "µS/cm"
CONDUCTIVITY = "µS/cm"
LIGHT_LUX: str = "lx"
LIGHT_LUX = "lx"
UV_INDEX: str = "UV index"
UV_INDEX = "UV index"
PERCENTAGE: str = "%"
PERCENTAGE = "%"
IRRADIATION_WATTS_PER_SQUARE_METER: str = "W/m²"
IRRADIATION_WATTS_PER_SQUARE_METER = "W/m²"
PRECIPITATION_MILLIMETERS_PER_HOUR: str = "mm/h"
PRECIPITATION_MILLIMETERS_PER_HOUR = "mm/h"
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: str = "µg/m³"
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER: str = "mg/m³"
CONCENTRATION_PARTS_PER_CUBIC_METER: str = "p/m³"
CONCENTRATION_PARTS_PER_MILLION: str = "ppm"
CONCENTRATION_PARTS_PER_BILLION: str = "ppb"
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER = "µg/m³"
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER = "mg/m³"
CONCENTRATION_PARTS_PER_CUBIC_METER = "p/m³"
CONCENTRATION_PARTS_PER_MILLION = "ppm"
CONCENTRATION_PARTS_PER_BILLION = "ppb"
SPEED_MILLIMETERS_PER_DAY: str = "mm/d"
SPEED_INCHES_PER_DAY: str = "in/d"
SPEED_METERS_PER_SECOND: str = "m/s"
SPEED_INCHES_PER_HOUR: str = "in/h"
SPEED_KILOMETERS_PER_HOUR: str = "km/h"
SPEED_MILES_PER_HOUR: str = "mph"
SPEED_MILLIMETERS_PER_DAY = "mm/d"
SPEED_INCHES_PER_DAY = "in/d"
SPEED_METERS_PER_SECOND = "m/s"
SPEED_INCHES_PER_HOUR = "in/h"
SPEED_KILOMETERS_PER_HOUR = "km/h"
SPEED_MILES_PER_HOUR = "mph"
SIGNAL_STRENGTH_DECIBELS: str = "dB"
SIGNAL_STRENGTH_DECIBELS_MILLIWATT: str = "dBm"
SIGNAL_STRENGTH_DECIBELS = "dB"
SIGNAL_STRENGTH_DECIBELS_MILLIWATT = "dBm"
DATA_BITS: str = "bit"
DATA_KILOBITS: str = "kbit"
DATA_MEGABITS: str = "Mbit"
DATA_GIGABITS: str = "Gbit"
DATA_BYTES: str = "B"
DATA_KILOBYTES: str = "kB"
DATA_MEGABYTES: str = "MB"
DATA_GIGABYTES: str = "GB"
DATA_TERABYTES: str = "TB"
DATA_PETABYTES: str = "PB"
DATA_EXABYTES: str = "EB"
DATA_ZETTABYTES: str = "ZB"
DATA_YOTTABYTES: str = "YB"
DATA_KIBIBYTES: str = "KiB"
DATA_MEBIBYTES: str = "MiB"
DATA_GIBIBYTES: str = "GiB"
DATA_TEBIBYTES: str = "TiB"
DATA_PEBIBYTES: str = "PiB"
DATA_EXBIBYTES: str = "EiB"
DATA_ZEBIBYTES: str = "ZiB"
DATA_YOBIBYTES: str = "YiB"
DATA_RATE_BITS_PER_SECOND: str = "bit/s"
DATA_RATE_KILOBITS_PER_SECOND: str = "kbit/s"
DATA_RATE_MEGABITS_PER_SECOND: str = "Mbit/s"
DATA_RATE_GIGABITS_PER_SECOND: str = "Gbit/s"
DATA_RATE_BYTES_PER_SECOND: str = "B/s"
DATA_RATE_KILOBYTES_PER_SECOND: str = "kB/s"
DATA_RATE_MEGABYTES_PER_SECOND: str = "MB/s"
DATA_RATE_GIGABYTES_PER_SECOND: str = "GB/s"
DATA_RATE_KIBIBYTES_PER_SECOND: str = "KiB/s"
DATA_RATE_MEBIBYTES_PER_SECOND: str = "MiB/s"
DATA_RATE_GIBIBYTES_PER_SECOND: str = "GiB/s"
DATA_BITS = "bit"
DATA_KILOBITS = "kbit"
DATA_MEGABITS = "Mbit"
DATA_GIGABITS = "Gbit"
DATA_BYTES = "B"
DATA_KILOBYTES = "kB"
DATA_MEGABYTES = "MB"
DATA_GIGABYTES = "GB"
DATA_TERABYTES = "TB"
DATA_PETABYTES = "PB"
DATA_EXABYTES = "EB"
DATA_ZETTABYTES = "ZB"
DATA_YOTTABYTES = "YB"
DATA_KIBIBYTES = "KiB"
DATA_MEBIBYTES = "MiB"
DATA_GIBIBYTES = "GiB"
DATA_TEBIBYTES = "TiB"
DATA_PEBIBYTES = "PiB"
DATA_EXBIBYTES = "EiB"
DATA_ZEBIBYTES = "ZiB"
DATA_YOBIBYTES = "YiB"
DATA_RATE_BITS_PER_SECOND = "bit/s"
DATA_RATE_KILOBITS_PER_SECOND = "kbit/s"
DATA_RATE_MEGABITS_PER_SECOND = "Mbit/s"
DATA_RATE_GIGABITS_PER_SECOND = "Gbit/s"
DATA_RATE_BYTES_PER_SECOND = "B/s"
DATA_RATE_KILOBYTES_PER_SECOND = "kB/s"
DATA_RATE_MEGABYTES_PER_SECOND = "MB/s"
DATA_RATE_GIGABYTES_PER_SECOND = "GB/s"
DATA_RATE_KIBIBYTES_PER_SECOND = "KiB/s"
DATA_RATE_MEBIBYTES_PER_SECOND = "MiB/s"
DATA_RATE_GIBIBYTES_PER_SECOND = "GiB/s"

View file

@ -2,6 +2,7 @@ import string
from dataclasses import dataclass
from typing import Callable
from fmtr.tools import json
from fmtr.tools.datatype_tools import to_bool
CHARS_ALLOWED = string.ascii_lowercase + string.digits
@ -48,6 +49,11 @@ class ConvertersString(Converters):
command: Callable = str
state: Callable = str
class ConvertersNumeric(Converters):
command: Callable = json.from_json
state: Callable = json.to_json
@dataclass
class Metadata:
exclude: bool = False