Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions switchbot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@
AirPurifierMode,
BulbColorMode,
CeilingLightColorMode,
ClimateAction,
ClimateMode,
ColorMode,
FanMode,
HumidifierAction,
HumidifierMode,
HumidifierWaterLevel,
LockStatus,
SmartThermostatRadiatorMode,
StripLightColorMode,
SwitchbotAccountConnectionError,
SwitchbotApiError,
Expand Down Expand Up @@ -54,6 +57,7 @@
SwitchbotRelaySwitch2PM,
)
from .devices.roller_shade import SwitchbotRollerShade
from .devices.smart_thermostat_radiator import SwitchbotSmartThermostatRadiator
from .devices.vacuum import SwitchbotVacuum
from .discovery import GetSwitchbotDevices
from .models import SwitchBotAdvertisement
Expand All @@ -62,13 +66,16 @@
"AirPurifierMode",
"BulbColorMode",
"CeilingLightColorMode",
"ClimateAction",
"ClimateMode",
"ColorMode",
"FanMode",
"GetSwitchbotDevices",
"HumidifierAction",
"HumidifierMode",
"HumidifierWaterLevel",
"LockStatus",
"SmartThermostatRadiatorMode",
"StripLightColorMode",
"SwitchBotAdvertisement",
"Switchbot",
Expand Down Expand Up @@ -99,6 +106,7 @@
"SwitchbotRelaySwitch2PM",
"SwitchbotRgbicLight",
"SwitchbotRollerShade",
"SwitchbotSmartThermostatRadiator",
"SwitchbotStripLight3",
"SwitchbotSupportedType",
"SwitchbotSupportedType",
Expand Down
7 changes: 7 additions & 0 deletions switchbot/adv_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
)
from .adv_parsers.remote import process_woremote
from .adv_parsers.roller_shade import process_worollershade
from .adv_parsers.smart_thermostat_radiator import process_smart_thermostat_radiator
from .adv_parsers.vacuum import process_vacuum, process_vacuum_k
from .const import SwitchbotModel
from .models import SwitchBotAdvertisement
Expand Down Expand Up @@ -377,6 +378,12 @@ class SwitchbotSupportedType(TypedDict):
"func": process_climate_panel,
"manufacturer_id": 2409,
},
b"\x00\x116@": {
"modelName": SwitchbotModel.SMART_THERMOSTAT_RADIATOR,
"modelFriendlyName": "Smart Thermostat Radiator",
"func": process_smart_thermostat_radiator,
"manufacturer_id": 2409,
},
}

_SWITCHBOT_MODEL_TO_CHAR = {
Expand Down
58 changes: 58 additions & 0 deletions switchbot/adv_parsers/smart_thermostat_radiator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Smart Thermostat Radiator"""

import logging

from ..const.climate import SmartThermostatRadiatorMode

_LOGGER = logging.getLogger(__name__)


def process_smart_thermostat_radiator(
data: bytes | None, mfr_data: bytes | None
) -> dict[str, bool | int | str]:
"""Process Smart Thermostat Radiator data."""
if mfr_data is None:
return {}

_seq_num = mfr_data[6]
_isOn = bool(mfr_data[7] & 0b10000000)
_battery = mfr_data[7] & 0b01111111

temp_data = mfr_data[8:11]
target_decimal = (temp_data[0] >> 4) & 0x0F
local_decimal = temp_data[0] & 0x0F

local_sign = 1 if (temp_data[1] & 0x80) else -1
local_int = temp_data[1] & 0x7F
local_temp = local_sign * (local_int + (local_decimal / 10))

target_sign = 1 if (temp_data[2] & 0x80) else -1
target_int = temp_data[2] & 0x7F
target_temp = target_sign * (target_int + (target_decimal / 10))

last_mode = SmartThermostatRadiatorMode.get_mode_name((mfr_data[11] >> 4) & 0x0F)
mode = SmartThermostatRadiatorMode.get_mode_name(mfr_data[11] & 0x07)

need_update_temp = bool((mfr_data[12] >> 5) & 0x01)
restarted = bool((mfr_data[12] >> 4) & 0x01)
fault_code = (mfr_data[12] >> 1) & 0x07
door_open = bool(mfr_data[12] & 0x01)

result = {
"sequence_number": _seq_num,
"isOn": _isOn,
"battery": _battery,
"temperature": local_temp,
"target_temperature": target_temp,
"mode": mode,
"last_mode": last_mode,
"need_update_temp": need_update_temp,
"restarted": restarted,
"fault_code": fault_code,
"door_open": door_open,
}

_LOGGER.debug(
"Smart Thermostat Radiator mfr data: %s, result: %s", mfr_data.hex(), result
)
return result
5 changes: 5 additions & 0 deletions switchbot/const/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from ..enum import StrEnum
from .air_purifier import AirPurifierMode
from .climate import ClimateAction, ClimateMode, SmartThermostatRadiatorMode
from .evaporative_humidifier import (
HumidifierAction,
HumidifierMode,
Expand Down Expand Up @@ -98,6 +99,7 @@ class SwitchbotModel(StrEnum):
RGBICWW_FLOOR_LAMP = "RGBICWW Floor Lamp"
K11_VACUUM = "K11+ Vacuum"
CLIMATE_PANEL = "Climate Panel"
SMART_THERMOSTAT_RADIATOR = "Smart Thermostat Radiator"


__all__ = [
Expand All @@ -107,12 +109,15 @@ class SwitchbotModel(StrEnum):
"AirPurifierMode",
"BulbColorMode",
"CeilingLightColorMode",
"ClimateAction",
"ClimateMode",
"ColorMode",
"FanMode",
"HumidifierAction",
"HumidifierMode",
"HumidifierWaterLevel",
"LockStatus",
"SmartThermostatRadiatorMode",
"StripLightColorMode",
"SwitchbotAccountConnectionError",
"SwitchbotApiError",
Expand Down
50 changes: 50 additions & 0 deletions switchbot/const/climate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""Representation of climate-related constants."""

from enum import Enum


class ClimateMode(Enum):
"""Climate Modes."""

OFF = 0
HEAT = 1
COOL = 2
HEAT_COOL = 3
AUTO = 4
DRY = 5
FAN_ONLY = 6


class ClimateAction(Enum):
"""Climate Actions."""

OFF = 0
IDLE = 1
HEATING = 2


class SmartThermostatRadiatorMode(Enum):
"""Smart Thermostat Radiator Modes."""

SCHEDULE = 0
MANUAL = 1
OFF = 2
ECONOMIC = 3
COMFORT = 4
FAST_HEATING = 5

@property
def lname(self) -> str:
return self.name.lower()

@classmethod
def get_modes(cls) -> list[str]:
return [mode.lname for mode in cls]

@classmethod
def get_mode_name(cls, mode_value: int) -> str:
return cls(mode_value).lname

@classmethod
def get_valid_modes(cls) -> list[str]:
return [mode.lname for mode in cls if mode != cls.OFF]
Loading