Skip to content

Commit

Permalink
v1.3.0 Enable multi-device selections during setup UI
Browse files Browse the repository at this point in the history
  • Loading branch information
manymuch committed Jul 28, 2024
1 parent 7966be2 commit e8b0bd5
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 88 deletions.
64 changes: 24 additions & 40 deletions custom_components/hisense/__init__.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,35 @@
from homeassistant import config_entries, core
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN
from .pyhisenseapi import HiSenseLogin, HiSenseAC
from .pyhisenseapi import HiSenseAC


async def async_setup(hass: core.HomeAssistant, config: dict):
# Initialize integration data structure
hass.data[DOMAIN] = {}
return True

async def async_setup_entry(hass: core.HomeAssistant, entry: config_entries.ConfigEntry):
# Setup the API client for a device
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = {}

session = async_get_clientsession(hass)
hisense_login = HiSenseLogin(
session=session
)
# Setup devices based on the selected devices from the config flow
for device_info in entry.data["devices"]:
device_id = device_info["device_id"]
wifi_id = device_info["wifi_id"]
refresh_token = device_info["refresh_token"]
hass.data[DOMAIN][entry.entry_id][device_id] = HiSenseAC(
wifi_id=wifi_id,
device_id=device_id,
refresh_token=refresh_token,
session=session
)

access_token, refresh_token = await hisense_login.login(entry.data["username"], entry.data["password"])
home_id_list = await hisense_login.get_home_id_list(access_token)
# TODO let the user to select home_id
home_id = home_id_list[0]
device_id_list, wifi_id_list = await hisense_login.get_device_id_list(access_token, home_id)
# TODO let the user to select device
device_id = device_id_list[0]
wifi_id = wifi_id_list[0]

hass.data[DOMAIN][entry.entry_id] = HiSenseAC(
wifi_id=wifi_id,
device_id=device_id,
refresh_token=refresh_token,
session=session
)
# Forward the setup to the climate platform
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, "climate"))
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, "switch"))
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, "button"))
return True
for platform in ("climate", "switch", "button"):
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, platform)
)

return True

async def async_unload_entry(hass: core.HomeAssistant, entry: config_entries.ConfigEntry):
# Unload both climate and switch config entries
unload_climate = await hass.config_entries.async_forward_entry_unload(entry, "climate")
unload_switch = await hass.config_entries.async_forward_entry_unload(entry, "switch")
unload_button = await hass.config_entries.async_forward_entry_unload(entry, "button")
if unload_climate and unload_switch and unload_button:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_climate and unload_switch and unload_button
return all(
await hass.config_entries.async_forward_entry_unload(entry, platform)
for platform in ("climate", "switch", "button")
)
23 changes: 12 additions & 11 deletions custom_components/hisense/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@
_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(hass: HomeAssistant, entry, async_add_entities: AddEntitiesCallback):
api = hass.data[DOMAIN][entry.entry_id]
async_add_entities([HisenseACUpdateButton(api)], True)
async_add_entities([HisenseACRefreshTokenButton(api)], True)

async def async_setup_entry(hass, config_entry, async_add_entities):
api = hass.data[DOMAIN][config_entry.entry_id]
entities = [HisenseACUpdateButton(api[device_id], config_entry.entry_id) for device_id in api]
async_add_entities(entities, True)
entities = [HisenseACRefreshTokenButton(api[device_id], config_entry.entry_id) for device_id in api]
async_add_entities(entities, True)


class HisenseACUpdateButton(ButtonEntity):
def __init__(self, api):
def __init__(self, api, config_entry_id):
self._api = api
self._config_entry_id = config_entry_id
self._attr_name = f"Force update button"
self._attr_unique_id = f"{api.device_id}_force_update_button"
self._attr_icon = "mdi:refresh"
Expand All @@ -41,16 +45,13 @@ async def async_press(self):
"""Handle the button press."""
_LOGGER.debug(f"Button pressed for entity: {self._attr_unique_id}")
await self._api.check_status()
# Ensure the climate entity is updated after status check
climate_entity = self.hass.data[DOMAIN].get(self._api.device_id)
if climate_entity:
await climate_entity.async_update()
climate_entity.async_write_ha_state()
self.async_schedule_update_ha_state(True)


class HisenseACRefreshTokenButton(ButtonEntity):
def __init__(self, api):
def __init__(self, api, config_entry_id):
self._api = api
self._config_entry_id = config_entry_id
self._attr_name = f"Refresh token"
self._attr_unique_id = f"{api.device_id}_refresh_token"
self._attr_icon = "mdi:refresh"
Expand Down
13 changes: 7 additions & 6 deletions custom_components/hisense/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@
_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(hass, entry, async_add_entities):
api = hass.data[DOMAIN][entry.entry_id]
entity = HisenseACClimate(api)
async_add_entities([entity], True)
async def async_setup_entry(hass, config_entry, async_add_entities):
api = hass.data[DOMAIN][config_entry.entry_id]
entities = [HisenseACClimate(api[device_id], config_entry.entry_id) for device_id in api]
async_add_entities(entities, True)


class HisenseACClimate(ClimateEntity):
def __init__(self, api):
def __init__(self, api, config_entry_id):
self._api = api
self._config_entry_id = config_entry_id
self._attr_name = f"Hisense AC"
self._attr_unique_id = f"{api.device_id}_climate"
self._attr_supported_features = (
Expand Down Expand Up @@ -158,7 +159,7 @@ async def async_set_swing_mode(self, swing_mode):
# Update the entity's current swing mode
self._attr_swing_mode = swing_mode
# Notify Home Assistant that the entity's state has changed
self.async_write_ha_state()
self.async_schedule_update_ha_state(True)
else:
_LOGGER.error("Unsupported swing mode: %s", swing_mode)

Expand Down
82 changes: 72 additions & 10 deletions custom_components/hisense/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,93 @@
import voluptuous as vol
from homeassistant import config_entries

from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers import config_validation as cv
from .const import DOMAIN, CONF_USERNAME, CONF_PASSWORD

from .pyhisenseapi import HiSenseLogin

class HisenseACConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL

def __init__(self):
self._home_id_list = None
self._access_token = None
self._refresh_token = None

async def async_step_user(self, user_input=None):
errors = {}

if user_input is not None:
# Here you could add code to validate the input, such as attempting to connect to the AC
# For simplicity, we'll assume the input is valid
return self.async_create_entry(title="Hisense Smart Control", data=user_input)
session = async_get_clientsession(self.hass)
hisense_login = HiSenseLogin(session=session)

try:
access_token, refresh_token = await hisense_login.login(
user_input[CONF_USERNAME], user_input[CONF_PASSWORD]
)
self._home_id_list = await hisense_login.get_home_id_list(access_token)
self._access_token = access_token
self._refresh_token = refresh_token
return await self.async_step_home()
except Exception:
errors["base"] = "invalid_auth"

return self.async_show_form(
step_id="user",
data_schema=vol.Schema({
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD, ): str,
}),
data_schema=vol.Schema(
{
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
}
),
description_placeholders={
"username_hint": "app login username",
"password_hint": "app login password",
},
errors=errors,
)


async def async_step_home(self, user_input=None):
errors = {}

if user_input is not None:
home_id = user_input["home_id"]
session = async_get_clientsession(self.hass)
hisense_login = HiSenseLogin(session=session)
self._device_wifi_id_dict = await hisense_login.get_device_wifi_id_dict(
self._access_token, home_id
)

return await self.async_step_device()

return self.async_show_form(
step_id="home",
data_schema=vol.Schema(
{vol.Required("home_id"): vol.In(self._home_id_list)}
),
errors=errors,
)

async def async_step_device(self, user_input=None):
if user_input is not None:
device_ids = user_input["device_ids"]
devices = [
{"device_id": device_id,
"wifi_id": self._device_wifi_id_dict[device_id],
"refresh_token": self._refresh_token,
}
for device_id in device_ids
]
return self.async_create_entry(
title="Hisense Smart Control",
data={"devices": devices}
)

data_schema = vol.Schema(
{
vol.Required("device_ids"): cv.multi_select(
list(self._device_wifi_id_dict.keys())
),
}
)
return self.async_show_form(step_id="device", data_schema=data_schema)
2 changes: 1 addition & 1 deletion custom_components/hisense/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"domain": "hisense",
"name": "Hisense Smart Devices",
"config_flow": true,
"version": "1.2.0",
"version": "1.3.0",
"integration_type": "device",
"documentation": "https://github.com/manymuch/HisenseHA",
"requirements": ["asyncio"],
Expand Down
10 changes: 4 additions & 6 deletions custom_components/hisense/pyhisenseapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ async def get_home_id_list(self, access_token):
else:
return None

async def get_device_id_list(self, access_token, home_id, device_keywords="空调"):
async def get_device_wifi_id_dict(self, access_token, home_id, device_keywords="空调"):
timestamp = self.get_timestamp()
url='http://api-wg.hismarttv.com/wg/dm/getHomeDeviceList'
headers = {
Expand All @@ -104,14 +104,12 @@ async def get_device_id_list(self, access_token, home_id, device_keywords="空
result_code = result["response"]["resultCode"]
if result_code == 0:
device_list = result["response"]["deviceList"]
device_id_list = []
wifi_id_list = []
device_wifi_id_dict = dict()
for device in device_list:
device_type_name = device["deviceTypeName"]
if device_keywords in device_type_name:
device_id_list.append(device["deviceId"])
wifi_id_list.append(device["wifiId"])
return device_id_list, wifi_id_list
device_wifi_id_dict[device["deviceId"]] = device["wifiId"]
return device_wifi_id_dict
else:
return None

Expand Down
18 changes: 10 additions & 8 deletions custom_components/hisense/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(hass: HomeAssistant, entry, async_add_entities: AddEntitiesCallback):
api = hass.data[DOMAIN][entry.entry_id]
async_add_entities([AcScreenSwitch(api)], True)
async_add_entities([AuxHeatSwitch(api)], True)
async def async_setup_entry(hass, config_entry, async_add_entities):
api = hass.data[DOMAIN][config_entry.entry_id]
entities = [AcScreenSwitch(api[device_id]) for device_id in api]
async_add_entities(entities, True)
entities = [AuxHeatSwitch(api[device_id]) for device_id in api]
async_add_entities(entities, True)


class AcScreenSwitch(SwitchEntity):
Expand Down Expand Up @@ -41,14 +43,14 @@ async def async_turn_on(self):
await self._api.send_logic_command(41, 1)
self._is_on = True
await self.async_update()
self.async_write_ha_state()
self.async_schedule_update_ha_state(True)

async def async_turn_off(self):
_LOGGER.debug(f"Turning off screen for {self._attr_unique_id}")
await self._api.send_logic_command(41, 0)
self._is_on = False
await self.async_update()
self.async_write_ha_state()
self.async_schedule_update_ha_state(True)

async def async_update(self):
status = self._api.get_status()
Expand Down Expand Up @@ -82,13 +84,13 @@ async def async_turn_on(self):
await self._api.send_logic_command(28, 1)
self._is_on = True
await self.async_update()
self.async_write_ha_state()
self.async_schedule_update_ha_state(True)

async def async_turn_off(self):
await self._api.send_logic_command(28, 0)
self._is_on = False
await self.async_update()
self.async_write_ha_state()
self.async_schedule_update_ha_state(True)

async def async_update(self):
status = self._api.get_status()
Expand Down
12 changes: 10 additions & 2 deletions custom_components/hisense/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,24 @@
"step": {
"user": {
"title": "HiSense Smart Device",
"description": "Please enter your device information",
"description": "Login with App username and password",
"data": {
"username": "App login username, could be the phone number",
"password": "App login password, if not available set it in the app first"
}
},
"home": {
"title": "HiSense Smart Device",
"description": "Select your home ID"
},
"device": {
"title": "HiSense Smart Device",
"description": "Select your device(s)"
}
},
"error": {
"cannot_connect": "Unable to connect to the device",
"invalid_auth": "Invalid authentication",
"invalid_auth": "Failed to login",
"unknown": "An unknown error occurred"
},
"title": "Hisense AC"
Expand Down
16 changes: 12 additions & 4 deletions custom_components/hisense/translations/zh-Hans.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,24 @@
"step": {
"user": {
"title": "海信智能设备",
"description": "请通过手机App抓包获得以下信息",
"description": "使用海信智慧家的手机号和密码登录",
"data": {
"username": "海信APP登录用户手机号",
"password": "如果没有登录密码请先去海信App里设置一个"
"username": "海信智慧家登录用户手机号",
"password": "如果没有登录密码请先去海信智慧家App里设置一个"
}
},
"home": {
"title": "海信智能设备",
"description": "选择海信App中的家庭ID(单选)"
},
"device": {
"title": "海信智能设备",
"description": "选择需要添加的设备(可多选)"
}
},
"error": {
"cannot_connect": "无法连接到设备",
"invalid_auth": "认证无效",
"invalid_auth": "登录失败",
"unknown": "发生未知错误"
},
"title": "海信空调"
Expand Down

0 comments on commit e8b0bd5

Please sign in to comment.