Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task/formaldehyde refactor #12

Merged
merged 2 commits into from
May 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 15 additions & 18 deletions libdyson/__init__.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
"""Dyson Python library."""

from typing import Optional

from .const import (
DEVICE_TYPE_360_EYE,
DEVICE_TYPE_360_HEURIST,
DEVICE_TYPE_PURE_COOL,
DEVICE_TYPE_PURIFIER_COOL,
DEVICE_TYPE_PURE_COOL_FORMALDEHYDE,
DEVICE_TYPE_PURIFIER_COOL_E,
DEVICE_TYPE_PURIFIER_COOL_K,
DEVICE_TYPE_PURE_COOL_DESK,
DEVICE_TYPE_PURE_COOL_LINK,
DEVICE_TYPE_PURE_COOL_LINK_DESK,
DEVICE_TYPE_PURE_HOT_COOL,
DEVICE_TYPE_PURE_HOT_COOL_NEW,
DEVICE_TYPE_PURIFIER_HOT_COOL_E,
DEVICE_TYPE_PURIFIER_HOT_COOL_K,
DEVICE_TYPE_PURE_HOT_COOL_LINK,
DEVICE_TYPE_PURE_HUMIDIFY_COOL,
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL,
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_FORMALDEHYDE,
DEVICE_TYPE_PURIFIER_HOT_COOL,
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_E,
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_K,
)

from .const import CleaningMode # noqa: F401
from .const import CleaningType # noqa: F401
from .const import DEVICE_TYPE_NAMES # noqa: F401
Expand All @@ -33,11 +33,10 @@
from .dyson_360_heurist import Dyson360Heurist
from .dyson_device import DysonDevice
from .dyson_pure_cool import DysonPureCool
from .dyson_pure_cool import DysonPureCoolFormaldehyde
from .dyson_pure_cool_link import DysonPureCoolLink
from .dyson_pure_hot_cool import DysonPureHotCool
from .dyson_pure_hot_cool_link import DysonPureHotCoolLink
from .dyson_pure_humidify_cool import DysonPureHumidifyCool, DysonPurifierHumidifyCoolFormaldehyde
from .dyson_pure_humidify_cool import DysonPurifierHumidifyCool
from .utils import get_mqtt_info_from_wifi_info # noqa: F401


Expand All @@ -54,25 +53,23 @@ def get_device(serial: str, credential: str, device_type: str) -> Optional[Dyson
return DysonPureCoolLink(serial, credential, device_type)
if device_type in [
DEVICE_TYPE_PURE_COOL,
DEVICE_TYPE_PURIFIER_COOL,
DEVICE_TYPE_PURIFIER_COOL_K,
DEVICE_TYPE_PURIFIER_COOL_E,
DEVICE_TYPE_PURE_COOL_DESK,
]:
return DysonPureCool(serial, credential, device_type)
if device_type == DEVICE_TYPE_PURE_COOL_FORMALDEHYDE:
return DysonPureCoolFormaldehyde(serial, credential, device_type)
if device_type == DEVICE_TYPE_PURE_HOT_COOL_LINK:
return DysonPureHotCoolLink(serial, credential, device_type)
if device_type in [
DEVICE_TYPE_PURE_HOT_COOL,
DEVICE_TYPE_PURE_HOT_COOL_NEW,
DEVICE_TYPE_PURIFIER_HOT_COOL,
DEVICE_TYPE_PURIFIER_HOT_COOL_E,
DEVICE_TYPE_PURIFIER_HOT_COOL_K,
]:
return DysonPureHotCool(serial, credential, device_type)
if device_type in [
DEVICE_TYPE_PURE_HUMIDIFY_COOL,
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_K,
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_E,
]:
return DysonPureHumidifyCool(serial, credential, device_type)
if device_type == DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_FORMALDEHYDE:
return DysonPurifierHumidifyCoolFormaldehyde(serial, credential, device_type)
return DysonPurifierHumidifyCool(serial, credential, device_type)
return None
40 changes: 20 additions & 20 deletions libdyson/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,36 @@

DEVICE_TYPE_360_EYE = "N223"
DEVICE_TYPE_360_HEURIST = "276"
DEVICE_TYPE_PURE_COOL_LINK = "475"
DEVICE_TYPE_PURE_COOL_LINK_DESK = "469"
DEVICE_TYPE_PURE_COOL = "438"
DEVICE_TYPE_PURIFIER_COOL = "438K"
DEVICE_TYPE_PURE_COOL_FORMALDEHYDE = "438E"
DEVICE_TYPE_PURE_COOL_DESK = "520"
DEVICE_TYPE_PURE_HUMIDIFY_COOL = "358"
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL = "358K"
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_FORMALDEHYDE = "358E"
DEVICE_TYPE_PURE_HOT_COOL_LINK = "455"
DEVICE_TYPE_PURE_HOT_COOL = "527"
DEVICE_TYPE_PURE_HOT_COOL_NEW = "527E"
DEVICE_TYPE_PURIFIER_HOT_COOL = "527K"
DEVICE_TYPE_PURE_COOL_LINK_DESK = "469" # DP01? DP02? This one's a bit older, and scraping the Dyson website is unclear
DEVICE_TYPE_PURE_COOL_DESK = "520" # AM06? This one's also a bit older, and also hard to scrape off the Dyson website
DEVICE_TYPE_PURE_COOL_LINK = "475" # TP02
DEVICE_TYPE_PURE_COOL = "438" # TP04
DEVICE_TYPE_PURIFIER_COOL_K = "438K" # TP07 AND TP09
DEVICE_TYPE_PURIFIER_COOL_E = "438E" # TP07 AND TP09
DEVICE_TYPE_PURE_HUMIDIFY_COOL = "358" # PH01 probably, but maybe PH02? Not 100% certain
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_K = "358K" # PH03 AND PH04
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_E = "358E" # PH03 AND PH04
DEVICE_TYPE_PURE_HOT_COOL_LINK = "455" # HP02
DEVICE_TYPE_PURE_HOT_COOL = "527" # HP04
DEVICE_TYPE_PURIFIER_HOT_COOL_E = "527E" # HP07 AND HP09
DEVICE_TYPE_PURIFIER_HOT_COOL_K = "527K" # HP07 AND HP09

DEVICE_TYPE_NAMES = {
DEVICE_TYPE_360_EYE: "360 Eye robot vacuum",
DEVICE_TYPE_360_HEURIST: "360 Heurist robot vacuum",
DEVICE_TYPE_PURE_COOL: "Pure Cool",
DEVICE_TYPE_PURIFIER_COOL: "Purifier Cool",
DEVICE_TYPE_PURE_COOL_FORMALDEHYDE: "Pure Cool Formaldehyde",
DEVICE_TYPE_PURE_COOL_DESK: "Pure Cool Desk",
DEVICE_TYPE_PURIFIER_COOL_K: "Purifier Cool",
DEVICE_TYPE_PURIFIER_COOL_E: "Purifier Cool",
DEVICE_TYPE_PURE_COOL_DESK: "Pure Cool Link Desk",
DEVICE_TYPE_PURE_COOL_LINK: "Pure Cool Link",
DEVICE_TYPE_PURE_COOL_LINK_DESK: "Pure Cool Link Desk",
DEVICE_TYPE_PURE_HOT_COOL: "Pure Hot+Cool",
DEVICE_TYPE_PURE_HOT_COOL_NEW: "Pure Hot+Cool (New)",
DEVICE_TYPE_PURIFIER_HOT_COOL_E: "Pure Hot+Cool (New)",
DEVICE_TYPE_PURE_HOT_COOL_LINK: "Pure Hot+Cool Link",
DEVICE_TYPE_PURE_HUMIDIFY_COOL: "Pure Humidify+Cool",
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL: "Purifier Humidify+Cool",
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_FORMALDEHYDE: "Purifier Humidify+Cool Formaldehyde",
DEVICE_TYPE_PURIFIER_HOT_COOL: "Purifier Hot+Cool",
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_K: "Purifier Humidify+Cool",
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_E: "Purifier Humidify+Cool",
DEVICE_TYPE_PURIFIER_HOT_COOL_K: "Purifier Hot+Cool",
}

ENVIRONMENTAL_OFF = -1
Expand Down
30 changes: 22 additions & 8 deletions libdyson/dyson_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import json
import logging
import threading
from typing import Any, Optional
from typing import Any, Optional, List, Dict, Union

import paho.mqtt.client as mqtt

Expand All @@ -17,7 +17,7 @@
DysonConnectionRefused,
DysonConnectTimeout,
DysonInvalidCredential,
DysonNotConnected,
DysonNotConnected, DysonNoEnvironmentalData,
)
from .utils import mqtt_time

Expand Down Expand Up @@ -164,7 +164,7 @@ def _handle_message(self, payload: dict) -> None:
def _update_status(self, payload: dict) -> None:
"""Update the device status."""

def _send_command(self, command: str, data: Optional[dict] = None):
def _send_command(self, command: str, data: Optional[dict] = None) -> None:
if not self.is_connected:
raise DysonNotConnected
if data is None:
Expand All @@ -176,7 +176,7 @@ def _send_command(self, command: str, data: Optional[dict] = None):
payload.update(data)
self._mqtt_client.publish(self._command_topic, json.dumps(payload))

def request_current_status(self):
def request_current_status(self) -> None:
"""Request current status."""
if not self.is_connected:
raise DysonNotConnected
Expand All @@ -195,7 +195,7 @@ def __init__(self, serial: str, credential: str, device_type: str):
super().__init__(serial, credential)
self._device_type = device_type

self._environmental_data = None
self._environmental_data = {}
self._environmental_data_available = threading.Event()

@property
Expand Down Expand Up @@ -256,6 +256,15 @@ def warning_code(self) -> str:
"""Return warning code."""
return self._get_field_value(self._status, "wacd")

@property
def formaldehyde(self) -> Optional[int]:
"""Return formaldehyde reading."""
val = self._get_environmental_field_value("hcho")
if val is None:
return None

return int(val)

@property
def humidity(self) -> int:
"""Return humidity in percentage."""
Expand All @@ -277,17 +286,22 @@ def sleep_timer(self) -> int:
return self._get_environmental_field_value("sltm")

@staticmethod
def _get_field_value(state, field):
return state[field][1] if isinstance(state[field], list) else state[field]
def _get_field_value(state: Dict[str, Any], field: str):
try:
return state[field][1] if isinstance(state[field], list) else state[field]
except:
return None

def _get_environmental_field_value(self, field, divisor=1):
def _get_environmental_field_value(self, field, divisor=1) -> Optional[Union[int, float]]:
value = self._get_field_value(self._environmental_data, field)
if value == "OFF":
return ENVIRONMENTAL_OFF
if value == "INIT":
return ENVIRONMENTAL_INIT
if value == "FAIL":
return ENVIRONMENTAL_FAIL
if value == "NONE" or value is None:
return None
if divisor == 1:
return int(value)
return float(value) / divisor
Expand Down
17 changes: 0 additions & 17 deletions libdyson/dyson_pure_cool.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,20 +175,3 @@ def disable_oscillation(self) -> None:
else:
oson = "OFF"
self._set_configuration(oson=oson)


class DysonPureCoolFormaldehyde(DysonPureCool):
"""This model is compatible with PureCool but has one additional sensor."""

@property
def formaldehyde(self) -> Optional[int]:
"""Return formaldehyde reading."""
# Dyson documentation for Dyson Pure Cool Formaldehyde refers to
# the formaldehyde reading as HCHO (pp5).
# https://www.dyson.com/content/dam/dyson/maintenance/user-guides/en_US/airtreatment/purifiers/TP09/497043-01.pdf
#
# H-CHO is also a common way to refer to formaldehyde.
#
# This is part of environmental data as per:
# https://github.com/seanrees/prometheus-dyson/issues/13#issue-923525150
return self._get_environmental_field_value("hcho")
10 changes: 1 addition & 9 deletions libdyson/dyson_pure_humidify_cool.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
}


class DysonPureHumidifyCool(DysonPureCoolBase):
class DysonPurifierHumidifyCool(DysonPureCoolBase):
"""Dyson Pure Humidify+Cool device."""

@property
Expand Down Expand Up @@ -100,11 +100,3 @@ def set_water_hardness(self, water_hardness: WaterHardness) -> None:
"""Set water hardness."""
self._set_configuration(wath=WATER_HARDNESS_ENUM_TO_STR[water_hardness])


class DysonPurifierHumidifyCoolFormaldehyde(DysonPureHumidifyCool):
"""Dyson Purifier Humidify+Cool Formaldehyde device."""

@property
def formaldehyde(self):
"""Return formaldehyde reading."""
return int(self._get_environmental_field_value("hcho"))
4 changes: 4 additions & 0 deletions libdyson/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,7 @@ class DysonConnectionRefused(DysonException):

class DysonFailedToParseWifiInfo(DysonException):
"""Represents failed to parse WiFi information."""


class DysonNoEnvironmentalData(DysonException):
"""Represents mqtt not connected."""
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = libdyson-neon
version = 0.10.2
version = 0.11.0
author = The libdyson Working Group
author_email = [email protected]
license = MIT License
Expand Down
28 changes: 14 additions & 14 deletions tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,26 @@
DEVICE_TYPE_360_EYE,
DEVICE_TYPE_360_HEURIST,
DEVICE_TYPE_PURE_COOL,
DEVICE_TYPE_PURE_COOL_FORMALDEHYDE,
DEVICE_TYPE_PURIFIER_COOL_E,
DEVICE_TYPE_PURIFIER_COOL_K,
DEVICE_TYPE_PURE_COOL_DESK,
DEVICE_TYPE_PURE_COOL_LINK,
DEVICE_TYPE_PURE_COOL_LINK_DESK,
DEVICE_TYPE_PURE_HOT_COOL,
DEVICE_TYPE_PURE_HOT_COOL_NEW,
DEVICE_TYPE_PURIFIER_HOT_COOL_E,
DEVICE_TYPE_PURIFIER_HOT_COOL_K,
DEVICE_TYPE_PURE_HOT_COOL_LINK,
DEVICE_TYPE_PURE_HUMIDIFY_COOL,
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL,
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_FORMALDEHYDE,
DEVICE_TYPE_PURIFIER_HOT_COOL,
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_E,
DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_K,
Dyson360Eye,
Dyson360Heurist,
DysonDevice,
DysonPureCool,
DysonPureCoolFormaldehyde,
DysonPureCoolLink,
DysonPureHotCool,
DysonPureHotCoolLink,
DysonPureHumidifyCool,
DysonPurifierHumidifyCoolFormaldehyde,
DysonPurifierHumidifyCool,
get_device,
)

Expand All @@ -42,15 +41,16 @@
(DEVICE_TYPE_PURE_COOL_LINK_DESK, DysonPureCoolLink),
(DEVICE_TYPE_PURE_COOL_LINK, DysonPureCoolLink),
(DEVICE_TYPE_PURE_COOL, DysonPureCool),
(DEVICE_TYPE_PURE_COOL_FORMALDEHYDE, DysonPureCoolFormaldehyde),
(DEVICE_TYPE_PURIFIER_COOL_E, DysonPureCool),
(DEVICE_TYPE_PURIFIER_COOL_K, DysonPureCool),
(DEVICE_TYPE_PURE_COOL_DESK, DysonPureCool),
(DEVICE_TYPE_PURE_HOT_COOL_LINK, DysonPureHotCoolLink),
(DEVICE_TYPE_PURE_HOT_COOL, DysonPureHotCool),
(DEVICE_TYPE_PURE_HOT_COOL_NEW, DysonPureHotCool),
(DEVICE_TYPE_PURE_HUMIDIFY_COOL, DysonPureHumidifyCool),
(DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL, DysonPureHumidifyCool),
(DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_FORMALDEHYDE, DysonPurifierHumidifyCoolFormaldehyde),
(DEVICE_TYPE_PURIFIER_HOT_COOL, DysonPureHotCool),
(DEVICE_TYPE_PURIFIER_HOT_COOL_E, DysonPureHotCool),
(DEVICE_TYPE_PURIFIER_HOT_COOL_K, DysonPureHotCool),
(DEVICE_TYPE_PURE_HUMIDIFY_COOL, DysonPurifierHumidifyCool),
(DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_E, DysonPurifierHumidifyCool),
(DEVICE_TYPE_PURIFIER_HUMIDIFY_COOL_K, DysonPurifierHumidifyCool),
],
)
def test_get_device(device_type: str, class_type: Type[DysonDevice]):
Expand Down
8 changes: 4 additions & 4 deletions tests/test_pure_cool_formaldehyde.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
import pytest

from libdyson.const import (
DEVICE_TYPE_PURE_COOL_FORMALDEHYDE,
DEVICE_TYPE_PURIFIER_COOL_E,
ENVIRONMENTAL_FAIL,
ENVIRONMENTAL_INIT,
ENVIRONMENTAL_OFF,
)
from libdyson.dyson_pure_cool import DysonPureCoolFormaldehyde
from libdyson.dyson_pure_cool import DysonPureCool

from . import CREDENTIAL, HOST, SERIAL
from .mocked_mqtt import MockedMQTT
from .test_pure_cool import STATUS as TEST_PURE_COOL_STATUS

DEVICE_TYPE = DEVICE_TYPE_PURE_COOL_FORMALDEHYDE
DEVICE_TYPE = DEVICE_TYPE_PURIFIER_COOL_E

STATUS = TEST_PURE_COOL_STATUS
ENVIRONMENTAL_DATA = {
Expand All @@ -36,7 +36,7 @@

def test_properties(mqtt_client: MockedMQTT):
"""Test properties of Pure Cool Link Formaldehyde."""
device = DysonPureCoolFormaldehyde(SERIAL, CREDENTIAL, DEVICE_TYPE)
device = DysonPureCool(SERIAL, CREDENTIAL, DEVICE_TYPE)
device.connect(HOST)

# Environmental
Expand Down
Loading