1
0
mirror of https://github.com/no2chem/wideq.git synced 2025-05-16 23:30:10 -07:00

Merge pull request #43 from dermotduffy/master

Add dishwasher device to wideq
This commit is contained in:
Adrian Sampson 2019-08-12 09:35:03 -04:00 committed by GitHub
commit 0610d0f0b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 1459 additions and 2 deletions

File diff suppressed because it is too large Load Diff

58
tests/test_dishwasher.py Normal file
View File

@ -0,0 +1,58 @@
import json
import unittest
from wideq.client import Client, DeviceInfo
from wideq.dishwasher import DishWasherDevice, DishWasherState, DishWasherStatus
POLL_DATA = {
"16~19": "0",
"21~22": "0",
"Course": "2",
"CourseType": "1",
"CurDownload": "2",
"Error": "0",
"Initial_Time_H": "3",
"Initial_Time_M": "14",
"Option1": "208",
"Option2": "8",
"Option3": "0",
"Process": "2",
"Remain_Time_H": "1",
"Remain_Time_M": "59",
"Reserve_Time_H": "0",
"Reserve_Time_M": "0",
"RinseLevel": "2",
"SmartCourse": "2",
"SofteningLevel": "0",
"State": "2",
}
class DishWasherStatusTest(unittest.TestCase):
def setUp(self):
super().setUp()
with open('./tests/fixtures/client.json') as fp:
state = json.load(fp)
self.client = Client.load(state)
self.device_info = DeviceInfo({
'alias': 'DISHWASHER',
'deviceId': '33330ba80-107d-11e9-96c8-0051ede8ad3c',
'deviceType': 204,
'modelJsonUrl': (
'https://aic.lgthinq.com:46030/api/webContents/modelJSON?'
'modelName=D3210&countryCode=WW&contentsId='
'JS0719082250749334&authKey=thinq'),
'modelNm': 'D3210',
})
self.dishwasher = DishWasherDevice(self.client, self.device_info)
def test_properties(self):
status = DishWasherStatus(self.dishwasher, POLL_DATA)
self.assertEqual(DishWasherState.RUNNING, status.state)
self.assertTrue(status.is_on)
self.assertEqual(119, status.remaining_time)
self.assertEqual(194, status.initial_time)
self.assertEqual('Heavy', status.course)
self.assertEqual('Casseroles', status.smart_course)
self.assertEqual('No Error', status.error)

157
wideq/dishwasher.py Normal file
View File

@ -0,0 +1,157 @@
import enum
from typing import Optional
from .client import Device
from .util import lookup_enum, lookup_reference
class DishWasherState(enum.Enum):
"""The state of the dishwasher device."""
INITIAL = '@DW_STATE_INITIAL_W'
RUNNING = '@DW_STATE_RUNNING_W'
PAUSED = "@DW_STATE_PAUSE_W"
OFF = '@DW_STATE_POWER_OFF_W'
COMPLETE = '@DW_STATE_COMPLETE_W'
POWER_FAIL = "@DW_STATE_POWER_FAIL_W"
DISHWASHER_STATE_READABLE = {
'INITIAL': 'Standby',
'RUNNING': 'Running',
'PAUSED': 'Paused',
'OFF': 'Off',
'COMPLETE': 'Complete',
'POWER_FAIL': 'Power Failed'
}
class DishWasherProcess(enum.Enum):
"""The process within the dishwasher state."""
RESERVE = '@DW_STATE_RESERVE_W'
RUNNING = '@DW_STATE_RUNNING_W'
RINSING = '@DW_STATE_RINSING_W'
DRYING = '@DW_STATE_DRYING_W'
COMPLETE = '@DW_STATE_COMPLETE_W'
NIGHT_DRYING = '@DW_STATE_NIGHTDRY_W'
CANCELLED = '@DW_STATE_CANCEL_W'
DISHWASHER_PROCESS_READABLE = {
'RESERVE': 'Delayed Start',
'RUNNING': DISHWASHER_STATE_READABLE['RUNNING'],
'RINSING': 'Rinsing',
'DRYING': 'Drying',
'COMPLETE': DISHWASHER_STATE_READABLE['COMPLETE'],
'NIGHT_DRYING': 'Night Drying',
'CANCELLED': 'Cancelled',
}
# Provide a map to correct typos in the official course names.
DISHWASHER_COURSE_MAP = {
'Haeavy': 'Heavy',
}
class DishWasherDevice(Device):
"""A higher-level interface for a dishwasher."""
def poll(self) -> Optional['DishWasherStatus']:
"""Poll the device's current state.
Monitoring must be started first with `monitor_start`.
:returns: Either a `DishWasherStatus` instance or `None` if the status
is not yet available.
"""
# Abort if monitoring has not started yet.
if not hasattr(self, 'mon'):
return None
data = self.mon.poll()
if data:
res = self.model.decode_monitor(data)
return DishWasherStatus(self, res)
else:
return None
class DishWasherStatus(object):
"""Higher-level information about a dishwasher's current status.
:param dishwasher: The DishWasherDevice instance.
:param data: Binary data from the API.
"""
def __init__(self, dishwasher: DishWasherDevice, data: dict):
self.dishwasher = dishwasher
self.data = data
@property
def state(self) -> DishWasherState:
"""Get the state of the dishwasher."""
return DishWasherState(
lookup_enum('State', self.data, self.dishwasher))
@property
def readable_state(self) -> str:
"""Get a human readable state of the dishwasher."""
return DISHWASHER_STATE_READABLE[self.state.name]
@property
def process(self) -> DishWasherProcess:
"""Get the process of the dishwasher."""
process = lookup_enum('Process', self.data, self.dishwasher)
if process and process != '-':
return DishWasherProcess(process)
else:
return None
@property
def readable_process(self) -> str:
"""Get a human readable process of the dishwasher."""
if self.process:
return DISHWASHER_PROCESS_READABLE[self.process.name]
else:
return None
@property
def is_on(self) -> bool:
"""Check if the dishwasher is on or not."""
return self.state != DishWasherState.OFF
@property
def remaining_time(self) -> int:
"""Get the remaining time in minutes."""
return (int(self.data['Remain_Time_H']) * 60 +
int(self.data['Remain_Time_M']))
@property
def initial_time(self) -> int:
"""Get the initial time in minutes."""
return (
int(self.data['Initial_Time_H']) * 60 +
int(self.data['Initial_Time_M']))
@property
def reserve_time(self) -> int:
"""Get the reserve time in minutes."""
return (
int(self.data['Reserve_Time_H']) * 60 +
int(self.data['Reserve_Time_M']))
@property
def course(self) -> str:
"""Get the current course."""
course = lookup_reference('Course', self.data, self.dishwasher)
if course in DISHWASHER_COURSE_MAP:
return DISHWASHER_COURSE_MAP[course]
else:
return course
@property
def smart_course(self) -> str:
"""Get the current smart course."""
return lookup_reference('SmartCourse', self.data, self.dishwasher)
@property
def error(self) -> str:
"""Get the current error."""
return lookup_reference('Error', self.data, self.dishwasher)