Skip to content

Commit

Permalink
Merge pull request #21 from KiraPC/dev
Browse files Browse the repository at this point in the history
Bugs Fixed
  • Loading branch information
joshepw authored Oct 9, 2023
2 parents 6882e81 + e4fb6ae commit dbb64f6
Show file tree
Hide file tree
Showing 14 changed files with 176 additions and 96 deletions.
24 changes: 18 additions & 6 deletions custom_components/switchbotremote/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,19 @@
from homeassistant.helpers.entity import DeviceInfo
from .client.remote import SupportedRemote

from .const import DOMAIN, IR_CAMERA_TYPES, IR_FAN_TYPES, IR_LIGHT_TYPES, CLASS_BY_TYPE
from .const import (
DOMAIN,
IR_CAMERA_TYPES,
IR_FAN_TYPES,
IR_LIGHT_TYPES,
CLASS_BY_TYPE,
CONF_CUSTOMIZE_COMMANDS,
CONF_WITH_ION,
CONF_WITH_TIMER,
CONF_WITH_BRIGHTNESS,
CONF_WITH_TEMPERATURE,
)


class SwitchBotRemoteButton(ButtonEntity):
_attr_has_entity_name = False
Expand Down Expand Up @@ -58,7 +70,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e

for remote in remotes:
options = entry.data.get(remote.id, {})
customize_commands = options.get("customize_commands", "")
customize_commands = options.get(CONF_CUSTOMIZE_COMMANDS, [])

if (remote.type in IR_CAMERA_TYPES):
entities.append(SwitchBotRemoteButton(
Expand All @@ -69,21 +81,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e
hass, remote, "TIMER", "mdi:timer"))

if (remote.type in IR_FAN_TYPES):
if (options.get("with_ion", False)):
if (options.get(CONF_WITH_ION, False)):
entities.append(SwitchBotRemoteButton(
hass, remote, "ION", "mdi:air-filter"))
if (options.get("with_timer", False)):
if (options.get(CONF_WITH_TIMER, False)):
entities.append(SwitchBotRemoteButton(
hass, remote, "TIMER", "mdi:timer"))

if (remote.type in IR_LIGHT_TYPES):
if (options.get("with_brightness", False)):
if (options.get(CONF_WITH_BRIGHTNESS, False)):
entities.append(SwitchBotRemoteButton(
hass, remote, "DARKER", "mdi:brightness-4"))
entities.append(SwitchBotRemoteButton(
hass, remote, "BRIGHTER", "mdi:brightness-6"))

if (options.get("with_temperature", False)):
if (options.get(CONF_WITH_TEMPERATURE, False)):
entities.append(SwitchBotRemoteButton(
hass, remote, "WARM", "mdi:octagram-minus"))
entities.append(SwitchBotRemoteButton(
Expand Down
48 changes: 31 additions & 17 deletions custom_components/switchbotremote/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,19 @@
from homeassistant.config_entries import ConfigEntry
from .client.remote import SupportedRemote

from .const import (DOMAIN, IR_CLIMATE_TYPES, AIR_CONDITIONER_CLASS)
from .const import (
DOMAIN,
IR_CLIMATE_TYPES,
AIR_CONDITIONER_CLASS,
CONF_POWER_SENSOR,
CONF_TEMPERATURE_SENSOR,
CONF_HUMIDITY_SENSOR,
CONF_TEMP_MIN,
CONF_TEMP_MAX,
CONF_TEMP_STEP,
CONF_HVAC_MODES,
)
from .config_flow import DEFAULT_HVAC_MODES

_LOGGER = logging.getLogger(__name__)

Expand All @@ -42,6 +54,7 @@

class SwitchBotRemoteClimate(ClimateEntity, RestoreEntity):
_attr_has_entity_name = False
_attr_force_update = True

def __init__(self, sb: SupportedRemote, options: dict = {}) -> None:
super().__init__()
Expand All @@ -52,22 +65,20 @@ def __init__(self, sb: SupportedRemote, options: dict = {}) -> None:
self.options = options

self._last_on_operation = None
self._operation_modes = [
HVACMode.OFF,
HVACMode.COOL,
HVACMode.DRY,
HVACMode.FAN_ONLY,
HVACMode.HEAT,
]
self._operation_modes = options.get(
CONF_HVAC_MODES, DEFAULT_HVAC_MODES)

if HVACMode.OFF not in self._operation_modes:
self._operation_modes.append(HVACMode.OFF)

self._hvac_mode = HVACMode.OFF

self._temperature_unit = TEMP_CELSIUS
self._target_temperature = 28
self._target_temperature_step = options.get("temp_step", 1)
self._max_temp = options.get("temp_max", DEFAULT_MAX_TEMP)
self._min_temp = options.get("temp_min", DEFAULT_MIN_TEMP)
self._power_sensor = options.get("power_sensor", None)
self._target_temperature_step = options.get(CONF_TEMP_STEP, 1)
self._max_temp = options.get(CONF_TEMP_MAX, DEFAULT_MAX_TEMP)
self._min_temp = options.get(CONF_TEMP_MIN, DEFAULT_MIN_TEMP)
self._power_sensor = options.get(CONF_POWER_SENSOR, None)

self._fan_mode = FAN_AUTO
self._fan_modes = [
Expand All @@ -79,8 +90,8 @@ def __init__(self, sb: SupportedRemote, options: dict = {}) -> None:

self._supported_features = ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE

self._temperature_sensor = options.get("temperature_sensor", None)
self._humidity_sensor = options.get("humidity_sensor", None)
self._temperature_sensor = options.get(CONF_TEMPERATURE_SENSOR, None)
self._humidity_sensor = options.get(CONF_HUMIDITY_SENSOR, None)
self._current_temperature = None
self._current_humidity = None

Expand Down Expand Up @@ -288,21 +299,24 @@ async def async_added_to_hass(self):
'last_on_operation')

if self._temperature_sensor:
async_track_state_change(self.hass, self._temperature_sensor, self._async_temp_sensor_changed)
async_track_state_change(
self.hass, self._temperature_sensor, self._async_temp_sensor_changed)

temp_sensor_state = self.hass.states.get(self._temperature_sensor)
if temp_sensor_state and temp_sensor_state.state != STATE_UNKNOWN:
self._async_update_temp(temp_sensor_state)

if self._humidity_sensor:
async_track_state_change(self.hass, self._humidity_sensor, self._async_humidity_sensor_changed)
async_track_state_change(
self.hass, self._humidity_sensor, self._async_humidity_sensor_changed)

humidity_sensor_state = self.hass.states.get(self._humidity_sensor)
if humidity_sensor_state and humidity_sensor_state.state != STATE_UNKNOWN:
self._async_update_humidity(humidity_sensor_state)

if self._power_sensor:
async_track_state_change(self.hass, self._power_sensor, self._async_power_sensor_changed)
async_track_state_change(
self.hass, self._power_sensor, self._async_power_sensor_changed)

power_sensor_state = self.hass.states.get(self._power_sensor)
if power_sensor_state and power_sensor_state.state != STATE_UNKNOWN:
Expand Down
112 changes: 68 additions & 44 deletions custom_components/switchbotremote/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.selector import selector
from homeassistant.components.climate.const import HVACMode
from .client import SwitchBot

from .const import (
Expand All @@ -25,8 +26,42 @@
VACUUM_CLASS,
WATER_HEATER_CLASS,
OTHERS_CLASS,

CONF_POWER_SENSOR,
CONF_TEMPERATURE_SENSOR,
CONF_HUMIDITY_SENSOR,
CONF_TEMP_MIN,
CONF_TEMP_MAX,
CONF_TEMP_STEP,
CONF_HVAC_MODES,
CONF_CUSTOMIZE_COMMANDS,
CONF_WITH_SPEED,
CONF_WITH_ION,
CONF_WITH_TIMER,
CONF_WITH_BRIGHTNESS,
CONF_WITH_TEMPERATURE,
CONF_ON_COMMAND,
CONF_OFF_COMMAND,
)

DEFAULT_HVAC_MODES = [
HVACMode.AUTO,
HVACMode.COOL,
HVACMode.DRY,
HVACMode.FAN_ONLY,
HVACMode.HEAT,
HVACMode.OFF,
]

HVAC_MODES = [
{"label": "Auto", "value": str(HVACMode.AUTO)},
{"label": "Cool", "value": str(HVACMode.COOL)},
{"label": "Dry", "value": str(HVACMode.DRY)},
{"label": "Fan Only", "value": str(HVACMode.FAN_ONLY)},
{"label": "Heat", "value": str(HVACMode.HEAT)},
{"label": "Off", "value": str(HVACMode.OFF)},
]

_LOGGER = logging.getLogger(__name__)

STEP_USER_DATA_SCHEMA = vol.Schema(
Expand All @@ -37,60 +72,52 @@
}
)

# TODO: Fix the entity selector default value or empty issue
STEP_CONFIGURE_DEVICE = {
AIR_CONDITIONER_CLASS: lambda x: vol.Schema({
# selector({"entity": {"filter": {"domain": ["binary_sensor","input_boolean","light","sensor","switch"]}}})
vol.Optional("power_sensor", default=x.get("power_sensor", "")): str,
# selector({"entity": {"filter": {"domain": "sensor"}}})
vol.Optional("temperature_sensor", default=x.get("temperature_sensor", "")): str,
# selector({"entity": {"filter": {"domain": "sensor"}}})
vol.Optional("humidity_sensor", default=x.get("humidity_sensor", "")): str,
vol.Optional("temp_min", default=x.get("temp_min", 16)): int,
vol.Optional("temp_max", default=x.get("temp_max", 30)): int,
vol.Optional("temp_step", default=x.get("temp_step", 1.0)): selector({"number": {"min": 0.1, "max": 2.0, "step": 0.1, "mode": "slider"}}),
vol.Optional("customize_commands", default=x.get("customize_commands", [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
vol.Optional(CONF_POWER_SENSOR, description={"suggested_value": x.get(CONF_POWER_SENSOR)}): selector({"entity": {"filter": {"domain": ["binary_sensor", "input_boolean", "light", "sensor", "switch"]}}}),
vol.Optional(CONF_TEMPERATURE_SENSOR, description={"suggested_value": x.get(CONF_TEMPERATURE_SENSOR)}): selector({"entity": {"filter": {"domain": "sensor"}}}),
vol.Optional(CONF_HUMIDITY_SENSOR, description={"suggested_value": x.get(CONF_HUMIDITY_SENSOR)}): selector({"entity": {"filter": {"domain": "sensor"}}}),
vol.Optional(CONF_TEMP_MIN, default=x.get(CONF_TEMP_MIN, 16)): int,
vol.Optional(CONF_TEMP_MAX, default=x.get(CONF_TEMP_MAX, 30)): int,
vol.Optional(CONF_TEMP_STEP, default=x.get(CONF_TEMP_STEP, 1.0)): selector({"number": {"min": 0.1, "max": 2.0, "step": 0.1, "mode": "slider"}}),
vol.Optional(CONF_HVAC_MODES, description={"suggested_value": x.get(CONF_HVAC_MODES, DEFAULT_HVAC_MODES)}): vol.All(selector({"select": {"multiple": True, "options": HVAC_MODES}})),
vol.Optional(CONF_CUSTOMIZE_COMMANDS, default=x.get(CONF_CUSTOMIZE_COMMANDS, [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
}),
MEDIA_CLASS: lambda x: vol.Schema({
# selector({"entity": {"filter": {"domain": ["binary_sensor","input_boolean","light","sensor","switch"]}}})
vol.Optional("power_sensor", default=x.get("power_sensor", "")): str,
vol.Optional("customize_commands", default=x.get("customize_commands", [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
vol.Optional(CONF_POWER_SENSOR, description={"suggested_value": x.get(CONF_POWER_SENSOR)}): selector({"entity": {"filter": {"domain": ["binary_sensor", "input_boolean", "light", "sensor", "switch"]}}}),
vol.Optional(CONF_CUSTOMIZE_COMMANDS, default=x.get(CONF_CUSTOMIZE_COMMANDS, [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
}),
FAN_CLASS: lambda x: vol.Schema({
# selector({"entity": {"filter": {"domain": ["binary_sensor","input_boolean","light","sensor","switch"]}}})
vol.Optional("power_sensor", default=x.get("power_sensor", "")): str,
vol.Optional("with_speed", default=x.get("with_speed", False)): bool,
vol.Optional("with_ion", default=x.get("with_ion", False)): bool,
vol.Optional("with_timer", default=x.get("with_timer", False)): bool,
vol.Optional("customize_commands", default=x.get("customize_commands", [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
vol.Optional(CONF_POWER_SENSOR, description={"suggested_value": x.get(CONF_POWER_SENSOR)}): selector({"entity": {"filter": {"domain": ["binary_sensor", "input_boolean", "light", "sensor", "switch"]}}}),
vol.Optional(CONF_WITH_SPEED, default=x.get(CONF_WITH_SPEED, False)): bool,
vol.Optional(CONF_WITH_ION, default=x.get(CONF_WITH_ION, False)): bool,
vol.Optional(CONF_WITH_TIMER, default=x.get(CONF_WITH_TIMER, False)): bool,
vol.Optional(CONF_CUSTOMIZE_COMMANDS, default=x.get(CONF_CUSTOMIZE_COMMANDS, [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
}),
LIGHT_CLASS: lambda x: vol.Schema({
# selector({"entity": {"filter": {"domain": ["binary_sensor","input_boolean","light","sensor","switch"]}}})
vol.Optional("power_sensor", default=x.get("power_sensor", "")): str,
vol.Optional("with_brightness", default=x.get("with_brightness", False)): bool,
vol.Optional("with_temperature", default=x.get("with_temperature", False)): bool,
vol.Optional("customize_commands", default=x.get("customize_commands", [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
vol.Optional(CONF_POWER_SENSOR, description={"suggested_value": x.get(CONF_POWER_SENSOR)}): selector({"entity": {"filter": {"domain": ["binary_sensor", "input_boolean", "light", "sensor", "switch"]}}}),
vol.Optional(CONF_WITH_BRIGHTNESS, default=x.get(CONF_WITH_BRIGHTNESS, False)): bool,
vol.Optional(CONF_WITH_TEMPERATURE, default=x.get(CONF_WITH_TEMPERATURE, False)): bool,
vol.Optional(CONF_CUSTOMIZE_COMMANDS, default=x.get(CONF_CUSTOMIZE_COMMANDS, [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
}),
CAMERA_CLASS: lambda x: vol.Schema({
vol.Optional("customize_commands", default=x.get("customize_commands", [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
vol.Optional(CONF_CUSTOMIZE_COMMANDS, default=x.get(CONF_CUSTOMIZE_COMMANDS, [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
}),
VACUUM_CLASS: lambda x: vol.Schema({
vol.Optional("customize_commands", default=x.get("customize_commands", [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
vol.Optional(CONF_CUSTOMIZE_COMMANDS, default=x.get(CONF_CUSTOMIZE_COMMANDS, [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
}),
WATER_HEATER_CLASS: lambda x: vol.Schema({
# selector({"entity": {"filter": {"domain": ["binary_sensor","input_boolean","light","sensor","switch"]}}})
vol.Optional("power_sensor", default=x.get("power_sensor", "")): str,
vol.Optional("temperature_sensor", default=x.get("temperature_sensor", "")): selector({"entity": {"filter": {"domain": "sensor"}}}),
vol.Optional("temp_min", default=x.get("temp_min", 40)): int,
vol.Optional("temp_max", default=x.get("temp_max", 65)): int,
vol.Optional("customize_commands", default=x.get("customize_commands", [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
vol.Optional(CONF_POWER_SENSOR, description={"suggested_value": x.get(CONF_POWER_SENSOR)}): selector({"entity": {"filter": {"domain": ["binary_sensor", "input_boolean", "light", "sensor", "switch"]}}}),
vol.Optional(CONF_TEMPERATURE_SENSOR, description={"suggested_value": x.get(CONF_TEMPERATURE_SENSOR)}): selector({"entity": {"filter": {"domain": "sensor"}}}),
vol.Optional(CONF_TEMP_MIN, default=x.get(CONF_TEMP_MIN, 40)): int,
vol.Optional(CONF_TEMP_MAX, default=x.get(CONF_TEMP_MAX, 65)): int,
vol.Optional(CONF_CUSTOMIZE_COMMANDS, default=x.get(CONF_CUSTOMIZE_COMMANDS, [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
}),
OTHERS_CLASS: lambda x: vol.Schema({
# selector({"entity": {"filter": {"domain": ["binary_sensor","input_boolean","light","sensor","switch"]}}})
vol.Optional("power_sensor", default=x.get("power_sensor", "")): str,
vol.Optional("on_command", default=x.get("on_command", "")): str,
vol.Optional("off_command", default=x.get("off_command", "")): str,
vol.Optional("customize_commands", default=x.get("customize_commands", [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
vol.Optional(CONF_POWER_SENSOR, description={"suggested_value": x.get(CONF_POWER_SENSOR)}): selector({"entity": {"filter": {"domain": ["binary_sensor", "input_boolean", "light", "sensor", "switch"]}}}),
vol.Optional(CONF_ON_COMMAND, default=x.get(CONF_ON_COMMAND, "")): str,
vol.Optional(CONF_OFF_COMMAND, default=x.get(CONF_OFF_COMMAND, "")): str,
vol.Optional(CONF_CUSTOMIZE_COMMANDS, default=x.get(CONF_CUSTOMIZE_COMMANDS, [])): selector({"select": {"multiple": True, "custom_value": True, "options": []}}),
}),
}

Expand Down Expand Up @@ -168,10 +195,7 @@ async def async_step_init(self, user_input: dict[str, Any] | None = None) -> Flo
for remote in self.discovered_devices:
devices[remote.id] = remote.name

return self.async_show_form(
step_id="init",
data_schema=vol.Schema({vol.Required("selected_device"): vol.In(devices)})
)
return self.async_show_form(step_id="init", data_schema=vol.Schema({vol.Required("selected_device"): vol.In(devices)}))

async def async_step_edit_device(self, user_input=None):
"""Handle editing a device."""
Expand All @@ -187,9 +211,9 @@ async def async_step_edit_device(self, user_input=None):
schema = vol.Schema({})
for remote in self.discovered_devices:
if remote.id == self.selected_device and remote.type in CLASS_BY_TYPE:
config = self.config_entry.data.get(remote.id, {})
schema = STEP_CONFIGURE_DEVICE[CLASS_BY_TYPE[remote.type]](
self.config_entry.data.get(remote.id, {})
)
config)

return self.async_show_form(
step_id="edit_device",
Expand Down
16 changes: 16 additions & 0 deletions custom_components/switchbotremote/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@

DOMAIN = "switchbotremote"

CONF_POWER_SENSOR = "power_sensor"
CONF_TEMPERATURE_SENSOR = "temperature_sensor"
CONF_HUMIDITY_SENSOR = "humidity_sensor"
CONF_TEMP_MIN = "temp_min"
CONF_TEMP_MAX = "temp_max"
CONF_TEMP_STEP = "temp_step"
CONF_HVAC_MODES = "hvac_modes"
CONF_CUSTOMIZE_COMMANDS = "customize_commands"
CONF_WITH_SPEED = "with_speed"
CONF_WITH_ION = "with_ion"
CONF_WITH_TIMER = "with_timer"
CONF_WITH_BRIGHTNESS = "with_brightness"
CONF_WITH_TEMPERATURE = "with_temperature"
CONF_ON_COMMAND = "on_command"
CONF_OFF_COMMAND = "off_command"

"""Supported Devices"""
DIY_AIR_CONDITIONER_TYPE = "DIY Air Conditioner"
AIR_CONDITIONER_TYPE = "Air Conditioner"
Expand Down
Loading

0 comments on commit dbb64f6

Please sign in to comment.