Various changes.
[python_utils.git] / light_utils.py
index 8c21b86312dcd25c5771fcd2be44c8ec0ca81dc4..63379af9b83059dfec9b0200b6a554c6f4e9e3f8 100644 (file)
@@ -14,9 +14,8 @@ from typing import Dict, List, Optional, Set
 
 import argparse_utils
 import config
-import logical_search
 import logging_utils
-import google_assistant as goog
+from google_assistant import ask_google, GoogleResponse
 from decorator_utils import timeout, memoized
 
 logger = logging.getLogger(__name__)
@@ -79,6 +78,18 @@ class Light(ABC):
     def turn_off(self) -> bool:
         pass
 
+    @abstractmethod
+    def is_on(self) -> bool:
+        pass
+
+    @abstractmethod
+    def is_off(self) -> bool:
+        pass
+
+    @abstractmethod
+    def get_dimmer_level(self) -> Optional[int]:
+        pass
+
     @abstractmethod
     def set_dimmer_level(self, level: int) -> bool:
         pass
@@ -106,29 +117,60 @@ class GoogleLight(Light):
         return name.replace("_", " ")
 
     @staticmethod
-    def parse_google_response(response: goog.GoogleResponse) -> bool:
+    def parse_google_response(response: GoogleResponse) -> bool:
         return response.success
 
     def turn_on(self) -> bool:
         return GoogleLight.parse_google_response(
-            goog.ask_google(f"turn {self.goog_name()} on")
+            ask_google(f"turn {self.goog_name()} on")
         )
 
     def turn_off(self) -> bool:
         return GoogleLight.parse_google_response(
-            goog.ask_google(f"turn {self.goog_name()} off")
+            ask_google(f"turn {self.goog_name()} off")
         )
 
+    def is_on(self) -> bool:
+        r = ask_google(f"is {self.goog_name()} on?")
+        if not r.success:
+            return False
+        return 'is on' in r.audio_transcription
+
+    def is_off(self) -> bool:
+        return not self.is_on()
+
+    def get_dimmer_level(self) -> Optional[int]:
+        if not self.has_keyword("dimmer"):
+            return False
+        r = ask_google(f'how bright is {self.goog_name()}?')
+        if not r.success:
+            return None
+
+        # the bookcase one is set to 40% bright
+        txt = r.audio_transcription
+        m = re.search(r"(\d+)% bright", txt)
+        if m is not None:
+            return int(m.group(1))
+        if "is off" in txt:
+            return 0
+        return None
+
     def set_dimmer_level(self, level: int) -> bool:
+        if not self.has_keyword("dimmer"):
+            return False
         if 0 <= level <= 100:
-            return GoogleLight.parse_google_response(
-                goog.ask_google(f"set {self.goog_name()} to {level} percent")
-            )
+            was_on = self.is_on()
+            r = ask_google(f"set {self.goog_name()} to {level} percent")
+            if not r.success:
+                return False
+            if not was_on:
+                self.turn_off()
+            return True
         return False
 
     def make_color(self, color: str) -> bool:
         return GoogleLight.parse_google_response(
-            goog.ask_google(f"make {self.goog_name()} {color}")
+            ask_google(f"make {self.goog_name()} {color}")
         )
 
 
@@ -177,6 +219,12 @@ class TPLinkLight(Light):
     def turn_off(self, child: str = None) -> bool:
         return self.command("off", child)
 
+    def is_on(self) -> bool:
+        return self.get_on_duration_seconds() > 0
+
+    def is_off(self) -> bool:
+        return not self.is_on()
+
     def make_color(self, color: str) -> bool:
         raise NotImplementedError
 
@@ -220,6 +268,14 @@ class TPLinkLight(Light):
                 return int(m.group(1)) * 60
         return None
 
+    def get_dimmer_level(self) -> Optional[int]:
+        if not self.has_keyword("dimmer"):
+            return False
+        self.info = self.get_info()
+        if self.info is None:
+            return None
+        return int(self.info.get("brightness", "0"))
+
     def set_dimmer_level(self, level: int) -> bool:
         if not self.has_keyword("dimmer"):
             return False
@@ -237,6 +293,7 @@ class LightingConfig(object):
             self,
             config_file: str = None,
     ) -> None:
+        import logical_search
         if config_file is None:
             config_file = config.config[
                 'light_utils_network_mac_addresses_location'