From 3838fb7e7ca66e6d3f6d7ef9e2b409815880c381 Mon Sep 17 00:00:00 2001 From: eriknn <82818949+eriknn@users.noreply.github.com> Date: Sun, 17 Nov 2024 01:59:07 +0100 Subject: [PATCH] Rework some Svensa entities --- .../pax_ble/coordinator_svensa.py | 27 ++++++------ .../pax_ble/devices/base_device.py | 1 - custom_components/pax_ble/devices/calima.py | 1 - custom_components/pax_ble/devices/svensa.py | 43 +++++++++---------- custom_components/pax_ble/number.py | 1 + custom_components/pax_ble/select.py | 18 +++++--- 6 files changed, 49 insertions(+), 42 deletions(-) diff --git a/custom_components/pax_ble/coordinator_svensa.py b/custom_components/pax_ble/coordinator_svensa.py index 4a5fe0e..5b78340 100644 --- a/custom_components/pax_ble/coordinator_svensa.py +++ b/custom_components/pax_ble/coordinator_svensa.py @@ -15,7 +15,7 @@ def __init__(self, hass, device, model, mac, pin, scan_interval, scan_interval_f super().__init__(hass, device, model, mac, pin, scan_interval, scan_interval_fast) # Initialize correct fan - _LOGGER.debug("Initializing Svansa!") + _LOGGER.debug("Initializing Svensa!") self._fan = Svensa(hass, mac, pin) async def read_sensordata(self, disconnect=False) -> bool: @@ -70,9 +70,11 @@ async def write_data(self, key) -> bool: try: # Write data match key: - case "automatic_cycles": + case "airing" | "fanspeed_airing": await self._fan.setAutomaticCycles( - int(self._state["automatic_cycles"]) + 26, + int(self._state["airing"]), + int(self._state["fanspeed_airing"]) ) case "boostmode": # Use default values if not set up @@ -97,11 +99,11 @@ async def write_data(self, key) -> bool: int(self._state["sensitivity_gas"]) != 0, int(self._state["sensitivity_gas"]), ) - case "sensor_delayedstart" | "sensor_runningtime" | "fanspeed_sensor": - await self._fan.setTimeFunctions( - int(self._state["sensor_delayedstart"]), - int(self._state["sensor_runningtime"]), - int(self._state["sensor_runningtime"]), + case "timer_runtime" | "timer_delay" | "fanspeed_sensor": + await self._fan.setTimerFunctions( + int(self._state["timer_runtime"]), + int(self._state["timer_delay"]) != 0, + int(self._state["timer_delay"]), int(self._state["fanspeed_sensor"]) ) case "trickle_on" | "fanspeed_trickle": @@ -133,7 +135,8 @@ async def read_configdata(self, disconnect=False) -> bool: return False AutomaticCycles = await self._fan.getAutomaticCycles() # Configuration - self._state["automatic_cycles"] = AutomaticCycles.TimeMin + self._state["airing"] = AutomaticCycles.TimeMin + self._state["fanspeed_airing"] = AutomaticCycles.Speed _LOGGER.debug(f"Automatic cycles: {AutomaticCycles}") ConstantOperation = await self._fan.getConstantOperation() # Configuration @@ -155,10 +158,10 @@ async def read_configdata(self, disconnect=False) -> bool: self._state["sensitivity_gas"] = PresenceGas.GasLevel _LOGGER.debug(f"PresenceGas: {PresenceGas}") - TimeFunctions = await self._fan.getTimeFunctions() # Configuration + TimeFunctions = await self._fan.getTimerFunctions() # Configuration + self._state["timer_runtime"] = TimeFunctions.PresenceTime + self._state["timer_delay"] = TimeFunctions.TimeMin self._state["fanspeed_sensor"] = TimeFunctions.Speed - self._state["sensor_delayedstart"] = TimeFunctions.PresenceTime - self._state["sensor_runningtime"] = TimeFunctions.TimeActive _LOGGER.debug(f"Time Functions: {TimeFunctions}") if disconnect: diff --git a/custom_components/pax_ble/devices/base_device.py b/custom_components/pax_ble/devices/base_device.py index af8e28c..2bc996f 100644 --- a/custom_components/pax_ble/devices/base_device.py +++ b/custom_components/pax_ble/devices/base_device.py @@ -6,7 +6,6 @@ from bleak import BleakClient from bleak.exc import BleakError import binascii -import math from collections import namedtuple import logging diff --git a/custom_components/pax_ble/devices/calima.py b/custom_components/pax_ble/devices/calima.py index 7d15d78..d31efa8 100644 --- a/custom_components/pax_ble/devices/calima.py +++ b/custom_components/pax_ble/devices/calima.py @@ -7,7 +7,6 @@ from collections import namedtuple from struct import pack, unpack -from typing import override _LOGGER = logging.getLogger(__name__) diff --git a/custom_components/pax_ble/devices/svensa.py b/custom_components/pax_ble/devices/svensa.py index a45ca03..9c38c2c 100644 --- a/custom_components/pax_ble/devices/svensa.py +++ b/custom_components/pax_ble/devices/svensa.py @@ -6,16 +6,14 @@ from collections import namedtuple from struct import pack, unpack -from typing import override _LOGGER = logging.getLogger(__name__) -# Tuples specifically For Svensa AutomaticCycles = namedtuple("AutomaticCycles", "Active Hour TimeMin Speed") ConstantOperation = namedtuple("ConstantOperation", "Active Speed") FanState = namedtuple("FanState", "Humidity AirQuality Temp Light RPM Mode") Humidity = namedtuple("Humidity", "Active Level Speed") -TimeFunctions = namedtuple("TimeFunctions", "PresenceTime TimeActive TimeMin Speed") +TimerFunctions = namedtuple("TimerFunctions", "PresenceTime TimeActive TimeMin Speed") PresenceGas = namedtuple("PresenceGas", "PresenceActive PresenceLevel GasActive GasLevel") class Svensa(BaseDevice): @@ -29,13 +27,13 @@ def __init__(self, hass, mac, pin): CHARACTERISTIC_MODEL_NUMBER: "00002a24-0000-1000-8000-00805f9b34fb", # Not used }) - # Update charateristics used in this file + # Update charateristics used in this device self.chars.update({ CHARACTERISTIC_AUTOMATIC_CYCLES: "7c4adc05-2f33-11e7-93ae-92361f002671", CHARACTERISTIC_TIME_FUNCTIONS: "7c4adc04-2f33-11e7-93ae-92361f002671" }) - # Add characteristics secific for Svensa + # Add characteristics specific for Svensa self.chars.update({ CHARACTERISTIC_HUMIDITY: "7c4adc01-2f33-11e7-93ae-92361f002671", # humActive, humLevel, fanSpeed CHARACTERISTIC_CONSTANT_OPERATION: "7c4adc03-2f33-11e7-93ae-92361f002671", @@ -48,7 +46,7 @@ def __init__(self, hass, mac, pin): async def getState(self) -> FanState: # Byte Byte Short Short Short Short Byte Byte Byte Byte Byte # Trg1 Trg2 Hum Gas Light FanSpeed Tbd Tbd Tbd Temp? Tbd - v = unpack("<2B4HBBBBB", await self._readUUID(self.chars[CHARACTERISTIC_SENSOR_DATA])) + v = unpack("<2B4H5B", await self._readUUID(self.chars[CHARACTERISTIC_SENSOR_DATA])) _LOGGER.debug("Read Fan States: %s", v) # Found in package com.component.svara.views.calima.SkyModeView @@ -97,15 +95,14 @@ async def getState(self) -> FanState: ############ CONFIGURATION FUNCTIONS ########### ################################################ async def getAutomaticCycles(self) -> AutomaticCycles: - return AutomaticCycles._make( - unpack("<3BH", await self._readUUID(self.chars[CHARACTERISTIC_AUTOMATIC_CYCLES])) - ) + # Active | Hour | TimeMin | Speed + # We fix so that TimeMin = 0 if Active = 0 + l = AutomaticCycles._make(unpack("<3BH", await self._readUUID(self.chars[CHARACTERISTIC_AUTOMATIC_CYCLES]))) - async def setAutomaticCycles(self, active:bool, hour:int, timeMin:int, speed:int) -> None: - if timeMin < 0 or timeMin > 3: - raise ValueError("Setting must be between 0-3") + return AutomaticCycles._make(unpack("<3BH",bytearray([l.Active,l.Hour,l.Active and l.TimeMin,l.Speed]))) - await self._writeUUID(self.chars[CHARACTERISTIC_AUTOMATIC_CYCLES], pack("<3BH", active, hour, timeMin, speed)) + async def setAutomaticCycles(self, hour:int, timeMin:int, speed:int) -> None: + await self._writeUUID(self.chars[CHARACTERISTIC_AUTOMATIC_CYCLES], pack("<3BH", timeMin > 0, hour, timeMin, speed)) async def getConstantOperation(self) -> ConstantOperation: v = unpack(" TimeFunctions: - return TimeFunctions._make( - unpack("<3BH", await self._readUUID(self.chars[CHARACTERISTIC_TIME_FUNCTIONS])) - ) + async def getTimerFunctions(self) -> TimerFunctions: + # PresenceTime | TimeActive | TimeMin | Speed, we fix so that TimeMin (Delay Time) = 0 if TimeActive = 0 + l = TimerFunctions._make(unpack("<3BH", await self._readUUID(self.chars[CHARACTERISTIC_TIME_FUNCTIONS]))) + return TimerFunctions._make(unpack("<3BH",bytearray([l.PresenceTime, l.TimeActive, l.TimeActive and l.TimeMin, l.Speed]))) - async def setTimeFunctions(self, presenceTime, timeActive, timeMin, speed) -> None: - if presenceTime not in (0, 5, 10): - raise ValueError("presenceTime must be 0, 5 or 10 minutes") - if timeActive not in (5, 10, 15, 30, 60): - raise ValueError("timeActive must be 5, 10, 15, 30 or 60 minutes") + async def setTimerFunctions(self, presenceTimeMin, timeActive:bool, timeMin:int, speed:int) -> None: + if presenceTimeMin not in (5, 10, 15, 30, 60): + raise ValueError("presenceTime must be 5, 10, 15, 30 or 60 minutes") + if timeMin not in (0, 2, 4): + raise ValueError("timeActive must be 0, 2 or 4 minutes") if speed % 25: raise ValueError("Speed must be a multiple of 25") await self._writeUUID( - self.chars[CHARACTERISTIC_TIME_FUNCTIONS], pack("<3BH", presenceTime, timeActive, timeMin, speed) + self.chars[CHARACTERISTIC_TIME_FUNCTIONS], pack("<3BH", presenceTimeMin, timeActive, timeMin, speed) ) \ No newline at end of file diff --git a/custom_components/pax_ble/number.py b/custom_components/pax_ble/number.py index 30a6481..84d3fb4 100644 --- a/custom_components/pax_ble/number.py +++ b/custom_components/pax_ble/number.py @@ -34,6 +34,7 @@ PaxEntity("fanspeed_light","Fanspeed Light",REVOLUTIONS_PER_MINUTE,None,EntityCategory.CONFIG,"mdi:engine",OPTIONS["fanspeed"],), ] SVENSA_ENTITIES = [ + PaxEntity("fanspeed_airing","Fanspeed Airing",REVOLUTIONS_PER_MINUTE,None,EntityCategory.CONFIG,"mdi:engine",OPTIONS["fanspeed"],), PaxEntity("fanspeed_sensor","Fanspeed Sensor",REVOLUTIONS_PER_MINUTE,None,EntityCategory.CONFIG,"mdi:engine",OPTIONS["fanspeed"],), ] RESTOREENTITIES = [ diff --git a/custom_components/pax_ble/select.py b/custom_components/pax_ble/select.py index 23ff0fd..befb452 100644 --- a/custom_components/pax_ble/select.py +++ b/custom_components/pax_ble/select.py @@ -13,6 +13,7 @@ # Creating nested dictionary of key/pairs OPTIONS = { + "airing": {"0": "Off", "30": "30 min", "60": "60 min", "90": "90 min", "120": "120 min"}, "automatic_cycles": {"0": "Off", "1": "30 min", "2": "60 min", "3": "90 min"}, "lightsensorsettings_delayedstart": {"0": "No delay", "5": "5 min", "10": "10 min"}, "lightsensorsettings_runningtime": { @@ -28,23 +29,29 @@ "2": "Medium sensitivity", "3": "High sensitivity", }, + "timer_delay": { + "0": "Off", + "2": "2 min", + "4": "4 min", + }, } PaxEntity = namedtuple('PaxEntity', ['key', 'entityName', 'category', 'icon', 'options']) ENTITIES = [ - PaxEntity("automatic_cycles","Automatic Cycles",EntityCategory.CONFIG,"mdi:fan-auto",OPTIONS["automatic_cycles"]), PaxEntity("sensitivity_humidity","Sensitivity Humidity",EntityCategory.CONFIG,"mdi:water-percent",OPTIONS["sensitivity"]), - PaxEntity("sensitivity_light","Sensitivity Light",EntityCategory.CONFIG,"mdi:brightness-5",OPTIONS["sensitivity"]), ] CALIMA_ENTITIES = [ + PaxEntity("automatic_cycles","Automatic Cycles",EntityCategory.CONFIG,"mdi:fan-auto",OPTIONS["automatic_cycles"]), + PaxEntity("sensitivity_light","Sensitivity Light",EntityCategory.CONFIG,"mdi:brightness-5",OPTIONS["sensitivity"]), PaxEntity("lightsensorsettings_delayedstart","LightSensorSettings DelayedStart",EntityCategory.CONFIG,"mdi:timer-outline",OPTIONS["lightsensorsettings_delayedstart"]), PaxEntity("lightsensorsettings_runningtime","LightSensorSettings Runningtime",EntityCategory.CONFIG,"mdi:timer-outline",OPTIONS["lightsensorsettings_runningtime"]), ] SVENSA_ENTITIES = [ - PaxEntity("sensitivity_presence","Sensitivity Presence",EntityCategory.CONFIG,"mdi:molecule",OPTIONS["sensitivity"]), + PaxEntity("airing","Airing",EntityCategory.CONFIG,"mdi:fan-auto",OPTIONS["airing"]), + PaxEntity("sensitivity_presence","Sensitivity Presence",EntityCategory.CONFIG,"mdi:brightness-5",OPTIONS["sensitivity"]), PaxEntity("sensitivity_gas","Sensitivity Gas",EntityCategory.CONFIG,"mdi:molecule",OPTIONS["sensitivity"]), - PaxEntity("sensor_delayedstart","Sensor DelayedStart",EntityCategory.CONFIG,"mdi:timer-outline",OPTIONS["lightsensorsettings_delayedstart"]), - PaxEntity("sensor_runningtime","Sensor Runningtime",EntityCategory.CONFIG,"mdi:timer-outline",OPTIONS["lightsensorsettings_runningtime"]), + PaxEntity("timer_runtime","Timer Runtime",EntityCategory.CONFIG,"mdi:timer-outline",OPTIONS["lightsensorsettings_runningtime"]), + PaxEntity("timer_delay","Timer Delay",EntityCategory.CONFIG,"mdi:timer-outline",OPTIONS["timer_delay"]), ] async def async_setup_entry(hass, config_entry, async_add_devices): @@ -122,3 +129,4 @@ async def async_select_option(self, option): """Restore value""" self.coordinator.set_data(self._key, old_value) self.async_schedule_update_ha_state(force_refresh=False) + # type: ignore \ No newline at end of file