Skip to content

Commit

Permalink
Rework some Svensa entities
Browse files Browse the repository at this point in the history
  • Loading branch information
eriknn committed Nov 17, 2024
1 parent 09042da commit 3838fb7
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 42 deletions.
27 changes: 15 additions & 12 deletions custom_components/pax_ble/coordinator_svensa.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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":
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down
1 change: 0 additions & 1 deletion custom_components/pax_ble/devices/base_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from bleak import BleakClient
from bleak.exc import BleakError
import binascii
import math
from collections import namedtuple
import logging

Expand Down
1 change: 0 additions & 1 deletion custom_components/pax_ble/devices/calima.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

from collections import namedtuple
from struct import pack, unpack
from typing import override

_LOGGER = logging.getLogger(__name__)

Expand Down
43 changes: 20 additions & 23 deletions custom_components/pax_ble/devices/svensa.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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",
Expand All @@ -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
Expand Down Expand Up @@ -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("<BH", await self._readUUID(self.chars[CHARACTERISTIC_CONSTANT_OPERATION]))
Expand Down Expand Up @@ -166,19 +163,19 @@ async def setPresenceGas(self, presence_active:bool, presence_level:int, gas_act

await self._writeUUID(self.chars[CHARACTERISTIC_PRESENCE_GAS], pack("<4B", presence_active, presence_level, gas_active, gas_level))

async def getTimeFunctions(self) -> 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)
)
1 change: 1 addition & 0 deletions custom_components/pax_ble/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down
18 changes: 13 additions & 5 deletions custom_components/pax_ble/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand All @@ -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):
Expand Down Expand Up @@ -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

0 comments on commit 3838fb7

Please sign in to comment.