Skip to content

Commit

Permalink
feat: update to add kumo station outdoor temperature sensor (#67)
Browse files Browse the repository at this point in the history
* feat: update to add kumo station outdoor temperature sensor

* Refactor based on recent best practice changes

* Update for deprecated field

* Bump pykumo to 0.2.0

* Getting ready for version update

This may be the wrong version number, but in case it is right, it will be ready to merge
  • Loading branch information
brgaulin authored Jun 18, 2022
1 parent c8da6ef commit 854423a
Show file tree
Hide file tree
Showing 10 changed files with 395 additions and 333 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ Mitubishi Kumo Cloud (Kumo for short) is a custom component for Home Assistant t
- Implements standard Home Assistant [`climate`](https://www.home-assistant.io/integrations/climate/) entities.
- Supports reading and setting the mode (heat/cool/etc.), setpoint, fan speed, and vane swing.
- Supports fully local control, except for initial setup. (See `prefer_cache` in Configuration for details.)
- Supports displaying Outdoor Temperature for Kumo Station
- Supports displaying WiFi RSSI of each unit(disabled by default)

## Installation

You can install Kumo in one of two ways.
You can install Kumo in one of two ways.

- **Automatic Installation.** Kumo is available in the HACS default store. Search for "Kumo" in the Integrations panel, and then click the **Mitsubishi Kumo Cloud** item in the results. Click the Install link, and then restart Home Assistant.
- **Automatic Installation.** Kumo is available in the HACS default store. Search for "Kumo" in the Integrations panel, and then click the **Mitsubishi Kumo Cloud** item in the results. Click the Install link, and then restart Home Assistant.
- **Manual Installation.** To control your installation yourself, download the hass-kumo repo, and then copy the `custom_components/kumo` directory into a corresponding `custom_components/kumo` within your Home Assistant configuration directory. Then restart Home Assistant.

We recommend using the HACS installation method, which makes future updates to Kumo easy to track and install. Click the HACS badge above for details on installing and using HACS.
Expand All @@ -39,7 +41,7 @@ kumo:

Add the referenced secrets to your secrets.yaml.

- `prefer_cache`, if present, controls whether to contact the KumoCloud servers on startup, or to prefer locally cached info on how to communicate with the indoor units. Default is `false`, to accommodate changing unit availability or DHCP leases. If your configuration is static (including the units' IP addresses on your LAN), it's safe to set this to `true`. This will allow you to control your system even if KumoCloud or your Internet connection suffer an outage.
- `prefer_cache`, if present, controls whether to contact the KumoCloud servers on startup, or to prefer locally cached info on how to communicate with the indoor units. Default is `false`, to accommodate changing unit availability or DHCP leases. If your configuration is static (including the units' IP addresses on your LAN), it's safe to set this to `true`. This will allow you to control your system even if KumoCloud or your Internet connection suffer an outage.
- `connect_timeout` and `response_timeout`, if present, control network timeouts for each command or status poll from the indoor unit(s). Increase these numbers if you see frequent log messages about timeouts. Decrease these numbers to improve overall HA responsivness if you anticipate your units being offline.

### IP Addresses
Expand All @@ -51,14 +53,14 @@ In some cases, Kumo is unable to retrieve the indoor units' addresses from the K
## Home Assistant Entities and Control

Each indoor unit appears as a separate [`climate`](https://www.home-assistant.io/integrations/climate/) entity in Home Assistant. Entity names are derived from the name you created for the unit in KumoCloud. For example, `climate.bedroom` or `climate.living_room`.
Each indoor unit appears as a separate [`climate`](https://www.home-assistant.io/integrations/climate/) entity in Home Assistant. Entity names are derived from the name you created for the unit in KumoCloud. For example, `climate.bedroom` or `climate.living_room`.

Entity attributes can tell you more about the current state of the indoor unit, as well as the unit's capabilities. Attributes may include the following:

- `hvac_modes`: The different modes of operation supported by the unit. For example: `off, cool, dry, heat, fan_only`.
- `min_temp`: The minimum temperature the unit can be set to. For example, `45`.
- `max_temp`: The maximum temperature the unit can be set to: For example, `95`.
- `fan_modes`: The different modes supported for the fan. This corresponds to fan speed, and noise. For example: `superQuiet, quiet, low, powerful, superPowerful, auto`.
- `fan_modes`: The different modes supported for the fan. This corresponds to fan speed, and noise. For example: `superQuiet, quiet, low, powerful, superPowerful, auto`.
- `swing_modes`: The different modes supported for the fan vanes. For example: `horizontal, midhorizontal, midpoint, midvertical, auto, swing`.
- `current_temperature`: The current ambient temperature, as sensed by the indoor unit. For example, `73`.
- `temperature`: The target temperature. For example, `77`.
Expand Down
171 changes: 46 additions & 125 deletions custom_components/kumo/__init__.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
"""Support for Mitsubishi KumoCloud devices."""
import asyncio
import logging
from typing import Optional

import homeassistant.helpers.config_validation as cv
import pykumo
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.util.json import load_json, save_json

from .coordinator import KumoDataUpdateCoordinator
from .const import (
CONF_CONNECT_TIMEOUT,
CONF_PREFER_CACHE,
CONF_RESPONSE_TIMEOUT,
DOMAIN,
KUMO_CONFIG_CACHE,
KUMO_DATA,
KUMO_DATA_COORDINATORS,
PLATFORMS,
)

_LOGGER = logging.getLogger(__name__)
Expand All @@ -39,7 +41,7 @@
)


class KumoData:
class KumoCloudSettings:
"""Hold object representing KumoCloud account."""

def __init__(self, account, domain_config, domain_options):
Expand All @@ -65,151 +67,70 @@ def get_raw_json(self):
"""Retrieve raw JSON config from account."""
return self._account.get_raw_json()

async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
"""Setup Kumo Entry"""
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN].setdefault(entry.entry_id, {})
username = entry.data.get(CONF_USERNAME)
password = entry.data.get(CONF_PASSWORD)
prefer_cache = entry.data.get(CONF_PREFER_CACHE)

def setup_kumo(hass, config):
"""Set up the Kumo indoor units."""
hass.async_create_task(async_load_platform(hass, "climate", DOMAIN, {}, config))
hass.async_add_job(hass.config_entries.async_forward_entry_setup(config, "climate"))
account = await async_kumo_setup(hass, prefer_cache, username, password)

if not account:
# Attempt setup again, but flip the prefer_cache flag
account = await async_kumo_setup(hass, not prefer_cache, username, password)

async def async_setup(hass, config):
"""Set up the Kumo Cloud devices. Will create climate and sensor components to support devices listed on the provided Kumo Cloud account."""
if DOMAIN not in config:
return True
# pylint: disable=C0415
username = config[DOMAIN].get(CONF_USERNAME)
password = config[DOMAIN].get(CONF_PASSWORD)
prefer_cache = config[DOMAIN].get(CONF_PREFER_CACHE)
domain_options = {
"connect_timeout": config[DOMAIN].get(CONF_CONNECT_TIMEOUT),
"response_timeout": config[DOMAIN].get(CONF_RESPONSE_TIMEOUT),
}

# Read config from either remote KumoCloud server or
# cached JSON.
cached_json = {}
success = False
if prefer_cache:
# Try to load from cache
cached_json = await hass.async_add_executor_job(
load_json, hass.config.path(KUMO_CONFIG_CACHE)
) or {"fetched": False}
account = pykumo.KumoCloudAccount(username, password, kumo_dict=cached_json)
else:
# Try to load from server
account = pykumo.KumoCloudAccount(username, password)
setup_success = await hass.async_add_executor_job(account.try_setup)
if setup_success:
if prefer_cache:
_LOGGER.info("Loaded config from local cache")
success = True
else:
await hass.async_add_executor_job(
save_json, hass.config.path(KUMO_CONFIG_CACHE), account.get_raw_json()
)
_LOGGER.info("Loaded config from KumoCloud server")
success = True
else:
# Fall back
if prefer_cache:
# Try to load from server
account = pykumo.KumoCloudAccount(username, password)
else:
# Try to load from cache
cached_json = await hass.async_add_executor_job(
load_json, hass.config.path(KUMO_CONFIG_CACHE)
) or {"fetched": False}
account = pykumo.KumoCloudAccount(username, password, kumo_dict=cached_json)
setup_success = await hass.async_add_executor_job(account.try_setup)
if setup_success:
if prefer_cache:
await hass.async_add_executor_job(
save_json,
hass.config.path(KUMO_CONFIG_CACHE),
account.get_raw_json(),
)
_LOGGER.info("Loaded config from KumoCloud server as fallback")
success = True
else:
_LOGGER.info("Loaded config from local cache as fallback")
success = True

if success:
hass.data[KUMO_DATA] = KumoData(account, config[DOMAIN], domain_options)
setup_kumo(hass, config)
if account:
hass.data[DOMAIN][entry.entry_id][KUMO_DATA] = KumoCloudSettings(account, entry.data, entry.options)

# Create a data coordinator for each Kumo device
hass.data[DOMAIN][entry.entry_id].setdefault(KUMO_DATA_COORDINATORS, {})
coordinators = hass.data[DOMAIN][entry.entry_id][KUMO_DATA_COORDINATORS]
connect_timeout = float(
entry.options.get(CONF_CONNECT_TIMEOUT, "1.2")
)
response_timeout = float(
entry.options.get(CONF_RESPONSE_TIMEOUT, "8")
)
timeouts = (connect_timeout, response_timeout)
pykumos = await hass.async_add_executor_job(account.make_pykumos, timeouts, True)
for device in pykumos.values():
if device.get_serial() not in coordinators:
coordinators[device.get_serial()] = KumoDataUpdateCoordinator(hass, device)

hass.config_entries.async_setup_platforms(entry, PLATFORMS)
return True

_LOGGER.warning("Could not load config from KumoCloud server or cache")
return False


async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
"""Setup Entry"""
username = entry.data.get(CONF_USERNAME)
password = entry.data.get(CONF_PASSWORD)
prefer_cache = entry.data.get(CONF_PREFER_CACHE)
# Read config from either remote KumoCloud server or
# cached JSON.
cached_json = {}
success = False
async def async_kumo_setup(hass: HomeAssistantType, prefer_cache: bool, username: str, password: str) -> Optional[pykumo.KumoCloudAccount]:
"""Attempt to load data from cache or Kumo Cloud"""
if prefer_cache:
# Try to load from cache
cached_json = await hass.async_add_executor_job(
load_json, hass.config.path(KUMO_CONFIG_CACHE)
) or {"fetched": False}
account = pykumo.KumoCloudAccount(username, password, kumo_dict=cached_json)
else:
# Try to load from server
account = pykumo.KumoCloudAccount(username, password)

setup_success = await hass.async_add_executor_job(account.try_setup)

if setup_success:
if prefer_cache:
_LOGGER.info("Loaded config from local cache")
success = True
else:
await hass.async_add_executor_job(
save_json, hass.config.path(KUMO_CONFIG_CACHE), account.get_raw_json()
)
_LOGGER.info("Loaded config from KumoCloud server")
success = True
else:
# Fall back
if prefer_cache:
# Try to load from server
account = pykumo.KumoCloudAccount(username, password)
else:
# Try to load from cache
cached_json = await hass.async_add_executor_job(
load_json, hass.config.path(KUMO_CONFIG_CACHE)
) or {"fetched": False}
account = pykumo.KumoCloudAccount(username, password, kumo_dict=cached_json)
setup_success = await hass.async_add_executor_job(account.try_setup)
if setup_success:
if prefer_cache:
await hass.async_add_executor_job(
save_json,
hass.config.path(KUMO_CONFIG_CACHE),
account.get_raw_json(),
)
_LOGGER.info("Loaded config from KumoCloud server as fallback")
success = True
else:
_LOGGER.info("Loaded config from local cache as fallback")
success = True
if success:
data = KumoData(account, entry.data, entry.options)
hass.data[DOMAIN] = data
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, "climate")
)
return True
_LOGGER.warning("Could not load config from KumoCloud server or cache")
return False

return account

async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry):
"""Unload Entry"""
hass.data.pop(DOMAIN)
tasks = []
tasks.append(hass.config_entries.async_forward_entry_unload(entry, "climate"))
return all(await asyncio.gather(*tasks))
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
Loading

0 comments on commit 854423a

Please sign in to comment.