Skip to content
This repository has been archived by the owner on Nov 12, 2020. It is now read-only.

Make compatibele with HACS #17

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7dd4bc5
Change to HACS format
kloknibor Sep 28, 2019
d1e0c1e
fix
kloknibor Sep 28, 2019
d80e22a
Changing log level to debug
kloknibor Sep 28, 2019
a2bfc85
spinning speed added
conorsham Feb 28, 2020
88234de
added startTime
shortibln Feb 28, 2020
71bba30
fixed finishTime calculation
shortibln Feb 28, 2020
788521c
added calculation of kickoff time (mashine start)
shortibln Feb 28, 2020
787f857
Merge pull request #1 from shortibln/master
kloknibor Mar 11, 2020
6845270
Update manifest.json
kloknibor Mar 11, 2020
a645f64
Solving error in log
kloknibor Mar 11, 2020
ddd567a
Fix I/O error and stability HA
kloknibor Jun 5, 2020
4c0c48d
Fixing second I/O issue
kloknibor Jun 6, 2020
8686553
fixed binary sensor and light error
kloknibor Jun 6, 2020
ed73342
Probably fix missing argument for MieleClient
n0rthdev Jun 6, 2020
a16aaa2
Merge pull request #2 from martinweu/patch-1
kloknibor Jun 7, 2020
3848ce2
fix for error about base url
kloknibor Jun 17, 2020
a2c198d
Pull refresh_token into an async init routine
palfrey Sep 27, 2020
324fa51
Refresh token doesn't need to be async
palfrey Sep 27, 2020
0d3fec4
Make plate steps and eco feedback available in device status
slootjes Oct 3, 2020
97f93ce
Update __init__.py
albatorsk Nov 20, 2020
01bb06b
Merge pull request #9 from palfrey/async-refresh-token
kloknibor Nov 22, 2020
935b56f
Merge pull request #10 from slootjes/master
kloknibor Nov 22, 2020
a60a4c6
Merge pull request #12 from albatorsk/patch-1
kloknibor Nov 22, 2020
3a7c784
Update README.md
kloknibor Nov 22, 2020
12355ed
Fixing I/O in event loop error
kloknibor Nov 28, 2020
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
**/*.pyc
.idea/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ source bin/activate
pip3 install requests_oauthlib
```

* Following the [instructions on the Miele developer site](https://www.miele.com/developer/getinvolved.html), you need to request your personal ```ClientID``` and ```ClientSecret```.
* Following the [instructions on the Miele developer site](https://www.miele.com/f/com/en/register_api.aspx), you need to request your personal ```ClientID``` and ```ClientSecret```.

## Installation of the custom component

Expand Down
24 changes: 11 additions & 13 deletions miele/__init__.py → custom_components/miele/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,10 @@
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.event import async_track_time_interval
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.network import get_url

from .miele_at_home import MieleClient, MieleOAuth

REQUIREMENTS = ['requests_oauthlib']

DEPENDENCIES = ['http']

_LOGGER = logging.getLogger(__name__)

DEVICES = []
Expand All @@ -50,7 +47,7 @@
CONFIGURATOR_SUBMIT_CAPTION = 'I have authorized Miele@home.'
CONFIGURATOR_DESCRIPTION = 'To link your Miele account, ' \
'click the link, login, and authorize:'
CONFIGURATOR_DESCRIPTION_IMAGE='https://api.mcs3.miele.com/images/miele-logo-immer-besser.svg'
CONFIGURATOR_DESCRIPTION_IMAGE='https://api.mcs3.miele.com/assets/images/miele_logo.svg'

MIELE_COMPONENTS = ['binary_sensor', 'light', 'sensor']

Expand Down Expand Up @@ -80,7 +77,7 @@ async def miele_configuration_callback(callback_data):

configurator = hass.components.configurator
_CONFIGURING[DOMAIN] = configurator.async_request_config(
DEFAULT_NAME,
DEFAULT_NAME,
miele_configuration_callback,
link_name=CONFIGURATOR_LINK_NAME,
link_url=oauth.authorization_url,
Expand Down Expand Up @@ -108,7 +105,7 @@ async def async_setup(hass, config):
hass.data[DOMAIN] = {}

if DATA_OAUTH not in hass.data[DOMAIN]:
callback_url = '{}{}'.format(hass.config.api.base_url, AUTH_CALLBACK_PATH)
callback_url = '{}{}'.format(get_url(hass), AUTH_CALLBACK_PATH)
cache = config[DOMAIN].get(CONF_CACHE_PATH, hass.config.path(DEFAULT_CACHE_PATH))
hass.data[DOMAIN][DATA_OAUTH] = MieleOAuth(
config[DOMAIN].get(CONF_CLIENT_ID), config[DOMAIN].get(CONF_CLIENT_SECRET),
Expand All @@ -125,19 +122,20 @@ async def async_setup(hass, config):

component = EntityComponent(_LOGGER, DOMAIN, hass)

client = MieleClient(hass.data[DOMAIN][DATA_OAUTH])
client = MieleClient(hass, hass.data[DOMAIN][DATA_OAUTH])
hass.data[DOMAIN][DATA_CLIENT] = client
hass.data[DOMAIN][DATA_DEVICES] = _to_dict(client.get_devices(lang))
data_get_devices = await client.get_devices(lang)
hass.data[DOMAIN][DATA_DEVICES] = _to_dict(data_get_devices)

DEVICES.extend([create_sensor(client, hass, home_device, lang) for k, home_device in hass.data[DOMAIN][DATA_DEVICES].items()])
await component.async_add_entities(DEVICES, False)

for component in MIELE_COMPONENTS:
load_platform(hass, component, DOMAIN, {}, config)

def refresh_devices(event_time):
async def refresh_devices(event_time):
_LOGGER.debug("Attempting to update Miele devices")
device_state = client.get_devices(lang)
device_state = await client.get_devices(lang)
if device_state is None:
_LOGGER.error("Did not receive Miele devices")
else:
Expand Down Expand Up @@ -290,6 +288,6 @@ async def action(self, action):

async def async_update(self):
if not self.unique_id in self._hass.data[DOMAIN][DATA_DEVICES]:
_LOGGER.error('Miele device not found: {}'.format(self.unique_id))
_LOGGER.debug('Miele device not found: {}'.format(self.unique_id))
else:
self._home_device = self._hass.data[DOMAIN][DATA_DEVICES][self.unique_id]
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from datetime import timedelta

from homeassistant.helpers.entity import Entity
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.binary_sensor import BinarySensorEntity

from custom_components.miele import DOMAIN as MIELE_DOMAIN, DATA_DEVICES

Expand All @@ -13,6 +13,7 @@

ALL_DEVICES = []


def _map_key(key):
if key == 'signalInfo':
return 'Info'
Expand All @@ -21,9 +22,9 @@ def _map_key(key):
elif key == 'signalDoor':
return 'Door'


# pylint: disable=W0612
def setup_platform(hass, config, add_devices, discovery_info=None):

global ALL_DEVICES

devices = hass.data[MIELE_DOMAIN][DATA_DEVICES]
Expand All @@ -41,11 +42,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(binary_devices)
ALL_DEVICES = ALL_DEVICES + binary_devices


def update_device_state():
for device in ALL_DEVICES:
device.async_schedule_update_ha_state(True)

class MieleBinarySensor(BinarySensorDevice):

class MieleBinarySensor(BinarySensorEntity):

def __init__(self, hass, device, key):
self._hass = hass
Expand All @@ -67,7 +70,7 @@ def unique_id(self):
def name(self):
"""Return the name of the sensor."""
ident = self._device['ident']

result = ident['deviceName']
if len(result) == 0:
return ident['type']['value_localized'] + ' ' + self._ha_key
Expand All @@ -86,8 +89,8 @@ def device_class(self):
else:
return 'problem'

async def async_update(self):
async def async_update(self):
if not self.device_id in self._hass.data[MIELE_DOMAIN][DATA_DEVICES]:
_LOGGER.error('Miele device not found: {}'.format(self.device_id))
_LOGGER.debug('Miele device not found: {}'.format(self.device_id))
else:
self._device = self._hass.data[MIELE_DOMAIN][DATA_DEVICES][self.device_id]
22 changes: 12 additions & 10 deletions miele/light.py → custom_components/miele/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from datetime import timedelta

from homeassistant.helpers.entity import Entity
from homeassistant.components.light import Light
from homeassistant.components.light import LightEntity

from custom_components.miele import DOMAIN as MIELE_DOMAIN, DATA_CLIENT, DATA_DEVICES

Expand All @@ -13,11 +13,11 @@

ALL_DEVICES = []

SUPPORTED_TYPES = [ 17, 18, 32, 33, 34, 68 ]
SUPPORTED_TYPES = [17, 18, 32, 33, 34, 68]


# pylint: disable=W0612
def setup_platform(hass, config, add_devices, discovery_info=None):

global ALL_DEVICES

devices = hass.data[MIELE_DOMAIN][DATA_DEVICES]
Expand All @@ -31,11 +31,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(light_devices)
ALL_DEVICES = ALL_DEVICES + light_devices


def update_device_state():
for device in ALL_DEVICES:
device.async_schedule_update_ha_state(True)

class MieleLight(Light):

class MieleLight(LightEntity):
def __init__(self, hass, device):
self._hass = hass
self._device = device
Expand All @@ -55,7 +57,7 @@ def unique_id(self):
def name(self):
"""Return the name of the light."""
ident = self._device['ident']

result = ident['deviceName']
if len(result) == 0:
return ident['type']['value_localized']
Expand All @@ -65,24 +67,24 @@ def name(self):
@property
def is_on(self):
"""Return the state of the light."""
return self._device['state']['light'] == 1
return self._device['state']['light'] == 1

def turn_on(self, **kwargs):
service_parameters = {
'device_id': self.device_id,
'body': { 'light': 1 }
'body': {'light': 1}
}
self._hass.services.call(MIELE_DOMAIN, 'action', service_parameters)

def turn_off(self, **kwargs):
service_parameters = {
'device_id': self.device_id,
'body': { 'light': 2 }
'body': {'light': 2}
}
self._hass.services.call(MIELE_DOMAIN, 'action', service_parameters)

async def async_update(self):
async def async_update(self):
if not self.device_id in self._hass.data[MIELE_DOMAIN][DATA_DEVICES]:
_LOGGER.error('Miele device not found: {}'.format(self.device_id))
_LOGGER.debug('Miele device not found: {}'.format(self.device_id))
else:
self._device = self._hass.data[MIELE_DOMAIN][DATA_DEVICES][self.device_id]
12 changes: 12 additions & 0 deletions custom_components/miele/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"domain": "miele",
"name": "Miele@home",
"documentation": "https://github.com/docbobo/home-assistant-miele",
"requirements": [
"requests_oauthlib"
],
"dependencies": [
"http"
],
"codeowners": ["docbobo, Kloknibor"]
}
38 changes: 21 additions & 17 deletions miele/miele_at_home.py → custom_components/miele/miele_at_home.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import json
import logging
import functools


from datetime import timedelta

Expand All @@ -12,20 +14,22 @@ class MieleClient(object):
DEVICES_URL = 'https://api.mcs3.miele.com/v1/devices'
ACTION_URL = 'https://api.mcs3.miele.com/v1/devices/{0}/actions'

def __init__(self, session):
def __init__(self, hass, session):
self._session = session
self.hass = hass

def _get_devices_raw(self, lang):
async def _get_devices_raw(self, lang):
_LOGGER.debug('Requesting Miele device update')
try:
devices = self._session._session.get(MieleClient.DEVICES_URL, params={'language':lang})
func = functools.partial(self._session._session.get, MieleClient.DEVICES_URL, params={'language':lang})
devices = await self.hass.async_add_executor_job(func)
if devices.status_code == 401:
_LOGGER.info('Request unauthorized - attempting token refresh')
if self._session.refresh_token():
return self._get_devices_raw(lang)
return self._get_devices_raw(lang)

if devices.status_code != 200:
_LOGGER.error('Failed to retrieve devices: {}'.format(devices.status_code))
_LOGGER.debug('Failed to retrieve devices: {}'.format(devices.status_code))
return None

return devices.json()
Expand All @@ -34,16 +38,16 @@ def _get_devices_raw(self, lang):
_LOGGER.error('Failed to retrieve Miele devices: {0}'.format(err))
return None

def get_devices(self, lang='en'):
home_devices = self._get_devices_raw(lang)
async def get_devices(self, lang='en'):
home_devices = await self._get_devices_raw(lang)
if home_devices is None:
return None

result = []
for home_device in home_devices:
result.append(home_devices[home_device])

return result
return result

def get_device(self, device_id, lang='en'):
devices = self._get_devices_raw(lang)
Expand All @@ -63,7 +67,7 @@ def action(self, device_id, body):
if result.status_code == 401:
_LOGGER.info('Request unauthorized - attempting token refresh')
if self._session.refresh_token():
return self.action(device_id, body)
return self.action(device_id, body)

if result.status_code == 200:
return result.json()
Expand Down Expand Up @@ -95,8 +99,8 @@ def __init__(self, client_id, client_secret, redirect_uri, cache_path=None):

self._session = OAuth2Session(self._client_id,
auto_refresh_url=MieleOAuth.OAUTH_TOKEN_URL,
redirect_uri=redirect_uri,
token=self._token,
redirect_uri=redirect_uri,
token=self._token,
token_updater=self._save_token)

if self.authorized:
Expand All @@ -109,8 +113,8 @@ def authorized(self):
@property
def authorization_url(self):
return self._session.authorization_url(MieleOAuth.OAUTH_AUTHORIZE_URL, state='login')[0]


def get_access_token(self, client_code):
token = self._session.fetch_token(
MieleOAuth.OAUTH_TOKEN_URL,
Expand All @@ -121,13 +125,13 @@ def get_access_token(self, client_code):

return token

def refresh_token(self):
async def refresh_token(self):
body = 'client_id={}&client_secret={}&'.format(self._client_id, self._client_secret)
self._token = self._session.refresh_token(MieleOAuth.OAUTH_TOKEN_URL,
self._token = await self._session.refresh_token(MieleOAuth.OAUTH_TOKEN_URL,
body=body,
refresh_token=self._token['refresh_token'])
self._save_token(self._token)

def _get_cached_token(self):
token = None
if self._cache_path:
Expand All @@ -139,7 +143,7 @@ def _get_cached_token(self):

except IOError:
pass

return token

def _save_token(self, token):
Expand Down
Loading