Skip to content

Commit

Permalink
Merge pull request #22 from jmwaldrip/main
Browse files Browse the repository at this point in the history
Adding support for Heidi Climate System
  • Loading branch information
kbickar committed Jan 17, 2024
2 parents 393d510 + c73cf14 commit a30d661
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 0 deletions.
1 change: 1 addition & 0 deletions asyncsleepiq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from .actuator import SleepIQActuator
from .bed import SleepIQBed
from .consts import *
from .core_climate import SleepIQCoreClimate
from .exceptions import (
SleepIQAPIException,
SleepIQLoginException,
Expand Down
11 changes: 11 additions & 0 deletions asyncsleepiq/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ class FootWarmingTemps(int, enum.Enum):
MEDIUM = 57
HIGH = 72

class CoreTemps(int, enum.Enum):
OFF = 0
HEATING_PUSH_LOW = 21
HEATING_PUSH_MED = 22
HEATING_PUSH_HIGH = 23
COOLING_PULL_LOW = 41
COOLING_PULL_MED = 42
COOLING_PULL_HIGH = 43

FAVORITE = 1
READ = 2
Expand Down Expand Up @@ -106,4 +114,7 @@ class End(str, enum.Enum):
"GetFootwarmingPresence": "FWPG",
"SetFootwarmingSettings": "FWTS",
"GetFootwarmingSettings": "FWTG",
"GetHeidiPresence": "THPG",
"SetHeidiMode": "THMS",
"GetHeidiMode": "THMG",
}
38 changes: 38 additions & 0 deletions asyncsleepiq/core_climate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""Foundation for Core Climate for SleepIQ API."""
from __future__ import annotations

from typing import Any

from .api import SleepIQAPI

from .consts import SIDES_FULL, CoreTemps, Side


class SleepIQCoreClimate:
"""
CoreClimate representation for SleepIQ API.
Controls heating and cooling.
"""

def __init__(self, api: SleepIQAPI, bed_id: str, side: Side, timer: int, temperature: int) -> None:
"""Initialize CoreClimate object."""
self._api = api
self.bed_id = bed_id
self.side = side
self.is_on = (temperature > 0)
self.timer = timer
self.temperature = temperature

def __str__(self) -> str:
"""Return string representation."""
return f"SleepIQCoreClimate[{self.side}]: {'On' if self.is_on else 'Off'}, {self.timer}, {CoreTemps(self.temperature).name}"
__repr__ = __str__

async def turn_on(self, temperature: CoreTemps, time: int) -> None:
"""Turn on core climate mode through API."""
await self.set_mode(temperature, time)

async def turn_off(self) -> None:
"""Turn off core climate mode through API."""
# The API requires a valid time value even if we're turning the climate off
await self.set_mode(CoreTemps.OFF, 1)
2 changes: 2 additions & 0 deletions asyncsleepiq/foundation.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from .light import SleepIQLight
from .foot_warmer import SleepIQFootWarmer
from .preset import SleepIQPreset
from .core_climate import SleepIQCoreClimate


class SleepIQFoundation:
Expand All @@ -27,6 +28,7 @@ def __init__(self, api: SleepIQAPI, bed_id: str) -> None:
self.bed_id = bed_id
self.lights: list[SleepIQLight] = []
self.foot_warmers: list[SleepIQFootWarmer] = []
self.core_climates: list[SleepIQCoreClimate] = []
self.features: dict[str, bool] = {
"boardIsASingle": False,
"hasMassageAndLight": False,
Expand Down
34 changes: 34 additions & 0 deletions asyncsleepiq/fuzion/core_climate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Foundation for Core Climate for Fuzion SleepIQ API."""
from __future__ import annotations

from typing import Any

from ..api import SleepIQAPI

from ..core_climate import SleepIQCoreClimate

from ..consts import SIDES_FULL, CoreTemps, Side


class SleepIQFuzionCoreClimate(SleepIQCoreClimate):
"""
CoreClimate (Also known as Heidi) representation for SleepIQ Fuzion API.
Heidi is the name of the climate calls in the SleepIQ API.
Controls heating and cooling.
"""
max_core_climate_time = 600

async def set_mode(self, temperature: CoreTemps, time: int) -> None:
"""Set core climate state through API."""
if time <= 0 or time > self.max_core_climate_time:
raise ValueError(f"Invalid Time, must be between 0 and {self.max_core_climate_time}")

args = [SIDES_FULL[self.side].lower(), temperature.name.lower(), str(time)]
await self._api.bamkey(self.bed_id, "SetHeidiMode", args)
await self.update({})

async def update(self, data: dict[str, Any]) -> None:
"""Update the core climate data through the API."""
args = [SIDES_FULL[self.side].lower()]
self.preset = await self._api.bamkey(self.bed_id, "GetHeidiMode", args)

9 changes: 9 additions & 0 deletions asyncsleepiq/fuzion/foundation.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .foot_warmer import SleepIQFuzionFootWarmer
from .light import SleepIQFuzionLight
from .preset import SleepIQFuzionPreset
from .core_climate import SleepIQFuzionCoreClimate

FEATURE_NAMES = [
"bedType", # Not sure what best to call this, but there's one flag at the start of the list that's (from testing) always "dual".
Expand Down Expand Up @@ -54,6 +55,7 @@ async def init_features(self) -> None:
await self.init_actuators()
await self.init_presets({})
await self.init_foot_warmers()
await self.init_core_climates()

async def update_foundation_status(self) -> None:
"""Update all foundation data from API."""
Expand Down Expand Up @@ -111,6 +113,13 @@ async def init_foot_warmers(self) -> None:
if result == "1":
self.foot_warmers.append(SleepIQFuzionFootWarmer(self._api, self.bed_id, side, 0, 0))

async def init_core_climates(self) -> None:
"""Initialize list of core climates available on foundation."""
for side in [Side.LEFT, Side.RIGHT]:
result = await self._api.bamkey(self.bed_id, "GetHeidiPresence", args=[SIDES_FULL[side].lower()])
if result == "true":
self.core_climates.append(SleepIQFuzionCoreClimate(self._api, self.bed_id, side, 0, 0))

async def fetch_features(self) -> None:
"""Update list of features available for foundation from API."""
vals = await self._api.bamkey(self.bed_id, "GetSystemConfiguration")
Expand Down

0 comments on commit a30d661

Please sign in to comment.