#!/usr/bin/python3 import hassapi as hass from threading import Timer ############### # Action Tree # ############### """ 1: States 1: Lights 1: Kitchen Lights 1: Off 2: 10% 3: 55% 4: 100% 2: Main Room Lights [...] 3: Living Room Lights [...] 4: Bedroom Lights [...] 2: A/C 2: Main Room AC 1: Power 1: On 2: On 3: Off 4: Off 2: Mode 1: Cool 2: Heat 3: Fan 3: Fan Level 1: Auto 2: Quiet 3: Medium 4: High 4: Temperature 1: 72 2: 74 3: 76 4: 78 3: Living Room AC [...] 4: Bedroom AC [...] 3: Fans 1: Kitchen Fan 1: Off 2: 1 3: 2 4: 3 3: Living Room Fan [...] 4: Bedroom Fan [...] 2: Miscellaneous 1: Effects 1: Clear light RGB strip notifications 2: Music 1: Join/unjoin speakers 1. Unjoin speakers # Select master 3. Living Room Sonos 4. Bedroom Sonos 2: Play Music 3: Play on Living Room Sonos 1: Spotify: Liked Songs 2: Spotify: Getting Things Done 4: Play on Bedroom Sonos 1: Spotify: Liked Songs 2: Spotify: Chill Sleep 3: Media Controls 3: Living Room Sonos 1. Play 2. Pause 3. Previous 4. Next 4: Bedroom Sonos [...] 4. Volume: TODO 3: Living Room Sonos 4: Bedroom Sonos 3: Reserved 4: Configuration """ tree = { 1: { # States 1: { # Lights 1: { # Kitchen 1: { "function": lambda hass: set_brightness(hass,"light.kitchen_lights",0) }, 2: { "function": lambda hass: set_brightness(hass,"light.kitchen_lights",10) }, 3: { "function": lambda hass: set_brightness(hass,"light.kitchen_lights",55) }, 4: { "function": lambda hass: set_brightness(hass,"light.kitchen_lights",100) } }, 2: { # Main Room 1: { "function": lambda hass: set_brightness(hass,"light.mainroom_lights",0) }, 2: { "function": lambda hass: set_brightness(hass,"light.mainroom_lights",10) }, 3: { "function": lambda hass: set_brightness(hass,"light.mainroom_lights",55) }, 4: { "function": lambda hass: set_brightness(hass,"light.mainroom_lights",100) } }, 3: { # Living Room 1: { "function": lambda hass: set_brightness(hass,"light.livingroom_lights",0) }, 2: { "function": lambda hass: set_brightness(hass,"light.livingroom_lights",10) }, 3: { "function": lambda hass: set_brightness(hass,"light.livingroom_lights",55) }, 4: { "function": lambda hass: set_brightness(hass,"light.livingroom_lights",100) } }, 4: { # Bedroom 1: { "function": lambda hass: set_brightness(hass,"light.bedroom_lights",0) }, 2: { "function": lambda hass: set_brightness(hass,"light.bedroom_lights",10) }, 3: { "function": lambda hass: set_brightness(hass,"light.bedroom_lights",55) }, 4: { "function": lambda hass: set_brightness(hass,"light.bedroom_lights",100) } } }, 2: { # A/C 2: { # Main Room 1: { # Power 1: { "function": lambda hass: hass.turn_on("climate.mainroom_ac") }, 2: { "function": lambda hass: hass.turn_on("climate.mainroom_ac") }, 3: { "function": lambda hass: hass.turn_off("climate.mainroom_ac") }, 4: { "function": lambda hass: hass.turn_off("climate.mainroom_ac") }, }, 2: { # Mode 1: { "function": lambda hass: gree_set_mode(hass,"mainroom_ac","cool") }, 2: { "function": lambda hass: gree_set_mode(hass,"mainroom_ac","heat") }, 3: { "function": lambda hass: gree_set_mode(hass,"mainroom_ac","fan_only") } }, 3: { # Fan Level 1: { "function": lambda hass: gree_set_fan_level(hass,"mainroom_ac","auto") }, 2: { "function": lambda hass: gree_set_fan_level(hass,"mainroom_ac","quiet") }, 3: { "function": lambda hass: gree_set_fan_level(hass,"mainroom_ac","medium") }, 4: { "function": lambda hass: gree_set_fan_level(hass,"mainroom_ac","high") } }, 4: { # Temperature 1: { "function": lambda hass: set_temperature(hass,"mainroom_ac",72) }, 2: { "function": lambda hass: set_temperature(hass,"mainroom_ac",74) }, 3: { "function": lambda hass: set_temperature(hass,"mainroom_ac",76) }, 4: { "function": lambda hass: set_temperature(hass,"mainroom_ac",78) } } }, 3: { # Living Room 1: { # Power 1: { "function": lambda hass: hass.turn_on("climate.livingroom_ac") }, 2: { "function": lambda hass: hass.turn_on("climate.livingroom_ac") }, 3: { "function": lambda hass: hass.turn_off("climate.livingroom_ac") }, 4: { "function": lambda hass: hass.turn_off("climate.livingroom_ac") }, }, 2: { # Mode 1: { "function": lambda hass: gree_set_mode(hass,"livingroom_ac","cool") }, 2: { "function": lambda hass: gree_set_mode(hass,"livingroom_ac","heat") }, 3: { "function": lambda hass: gree_set_mode(hass,"livingroom_ac","fan_only") } }, 3: { # Fan Level 1: { "function": lambda hass: gree_set_fan_level(hass,"livingroom_ac","auto") }, 2: { "function": lambda hass: gree_set_fan_level(hass,"livingroom_ac","quiet") }, 3: { "function": lambda hass: gree_set_fan_level(hass,"livingroom_ac","medium") }, 4: { "function": lambda hass: gree_set_fan_level(hass,"livingroom_ac","high") } }, 4: { # Temperature 1: { "function": lambda hass: set_temperature(hass,"livingroom_ac",72) }, 2: { "function": lambda hass: set_temperature(hass,"livingroom_ac",74) }, 3: { "function": lambda hass: set_temperature(hass,"livingroom_ac",76) }, 4: { "function": lambda hass: set_temperature(hass,"livingroom_ac",78) } } }, 4: { # Bedroom 1: { # Power 1: { "function": lambda hass: hass.turn_on("climate.bedroom_ac") }, 2: { "function": lambda hass: hass.turn_on("climate.bedroom_ac") }, 3: { "function": lambda hass: hass.turn_off("climate.bedroom_ac") }, 4: { "function": lambda hass: hass.turn_off("climate.bedroom_ac") }, }, 2: { # Mode 1: { "function": lambda hass: gree_set_mode(hass,"bedroom_ac","cool") }, 2: { "function": lambda hass: gree_set_mode(hass,"bedroom_ac","heat") }, 3: { "function": lambda hass: gree_set_mode(hass,"bedroom_ac","fan_only") } }, 3: { # Fan Level 1: { "function": lambda hass: gree_set_fan_level(hass,"bedroom_ac","auto") }, 2: { "function": lambda hass: gree_set_fan_level(hass,"bedroom_ac","quiet") }, 3: { "function": lambda hass: gree_set_fan_level(hass,"bedroom_ac","medium") }, 4: { "function": lambda hass: gree_set_fan_level(hass,"bedroom_ac","high") } }, 4: { # Temperature 1: { "function": lambda hass: set_temperature(hass,"bedroom_ac",72) }, 2: { "function": lambda hass: set_temperature(hass,"bedroom_ac",74) }, 3: { "function": lambda hass: set_temperature(hass,"bedroom_ac",76) }, 4: { "function": lambda hass: set_temperature(hass,"bedroom_ac",78) } } } }, 3: { # Fans 1: { # Kitchen 1: { "function": lambda hass: hass.set_value("input_number.kitchen_fan",0) }, 2: { "function": lambda hass: hass.set_value("input_number.kitchen_fan",1) }, 3: { "function": lambda hass: hass.set_value("input_number.kitchen_fan",2) }, 4: { "function": lambda hass: hass.set_value("input_number.kitchen_fan",3) } }, 3: { # Living Room 1: { "function": lambda hass: hass.set_value("input_number.livingroom_fan",0) }, 2: { "function": lambda hass: hass.set_value("input_number.livingroom_fan",1) }, 3: { "function": lambda hass: hass.set_value("input_number.livingroom_fan",2) }, 4: { "function": lambda hass: hass.set_value("input_number.livingroom_fan",3) } }, 4: { # Bedroom 1: { "function": lambda hass: hass.set_value("input_number.bedroom_fan",0) }, 2: { "function": lambda hass: hass.set_value("input_number.bedroom_fan",1) }, 3: { "function": lambda hass: hass.set_value("input_number.bedroom_fan",2) }, 4: { "function": lambda hass: hass.set_value("input_number.bedroom_fan",3) } } } }, 2: { # Miscellaneous 1: { # Effects 1: { "service": "script/clear_lightswitch_leds" } }, 2: { # Music 1: { # Join/unjoin 1: { "function": lambda hass: hass.call_service("sonos/unjoin", entity_id=["media_player.livingroom_sonos","media_player.bedroom_sonos"]) }, 3: { "function": lambda hass: hass.call_service("sonos/join", master="media_player.livingroom_sonos",entity_id="media_player.bedroom_sonos")}, 4: { "function": lambda hass: hass.call_service("sonos/join", master="media_player.livingroom_sonos",entity_id="media_player.bedroom_sonos")}, }, 2: { # Play Music 3: { # Living Room Sonos 1: { "function": lambda hass: select_source(hass,"media_player.livingroom_sonos","Songs") }, 2: { "function": lambda hass: select_source(hass,"media_player.livingroom_sonos","Getting Things Done") } }, 4: { # Bedroom Sonos 1: { "function": lambda hass: select_source(hass,"media_player.bedroom_sonos","Songs") }, 2: { "function": lambda hass: select_source(hass,"media_player.bedroom_sonos","Chill sleep") } } }, 3: { # Media Controls 3: { # Living Room Sonos 1: { "function": lambda hass: hass.call_service("media_player/media_pause", entity_id="media_player.livingroom_sonos")}, 2: { "function": lambda hass: hass.call_service("media_player/media_play", entity_id="media_player.livingroom_sonos")}, 3: { "function": lambda hass: hass.call_service("media_player/media_previous_track", entity_id="media_player.livingroom_sonos")}, 4: { "function": lambda hass: hass.call_service("media_player/media_next_track", entity_id="media_player.livingroom_sonos")} }, 4: { # Bedroom Sonos 1: { "function": lambda hass: hass.call_service("media_player/media_pause", entity_id="media_player.bedroom_sonos")}, 2: { "function": lambda hass: hass.call_service("media_player/media_play", entity_id="media_player.bedroom_sonos")}, 3: { "function": lambda hass: hass.call_service("media_player/media_previous_track", entity_id="media_player.bedroom_sonos")}, 4: { "function": lambda hass: hass.call_service("media_player/media_next_track", entity_id="media_player.bedroom_sonos")} } } } } } ########################### # Action Helper Functions # ########################### def set_brightness(hass, device, brightness): # Convert brightness from percent to 0-255 brightness = brightness * 255 / 100; hass.turn_on(device, brightness=brightness); def set_temperature(hass, device, temperature): hass.call_service("climate/set_temperature",entity_id="climate."+device,temperature=temperature); def gree_set_mode(hass, device, mode): hass.call_service("climate/set_hvac_mode",entity_id="climate."+device,hvac_mode=mode); def gree_set_fan_level(hass, device, level): if (level == "quiet"): hass.turn_on(f"switch.{device}_quiet"); else: hass.turn_off(f"switch.{device}_quiet"); hass.call_service("climate/set_fan_mode",entity_id="climate."+device,fan_mode=level); def select_source(hass, device, source): hass.call_service("media_player/select_source",entity_id=device,source=source) ################## # Sequence Logic # ################## # Long press cancels any existing sequence and begins a new one. The first element in a # sequence is always a long press, and all others are always short presses. # While a sequence is being entered, normal actions from short presses in hass are # disabled. A sequence can be ended either immediately by recognizing a full sequence # or by TIMEOUT seconds passing. # - self.currentSequence contains an array of keys representing a sequence that is # actively being entered. # - self.tot contains a timer for the process_sequence function, ending sequence entry. TIMEOUT=2 class WallmotePlumbing(hass.Hass): def initialize(self): self.turn_on("automation.wallmote"); self.currentSequence = []; self.listen_event(self.onevent, "zwave_js_value_notification"); def onevent(self, _, event, kwargs): if (event.get('value') == 'KeyHeldDown'): return; if (event.get('device_id') != '3f7add36f94839b1446886b5dab35e6d'): return; key = int(event.get('property_key')); # We don't get a KeyPressed event at all for long presses evt = event.get('value'); if (evt == "KeyPressed"): self.shortpress(key); elif (evt == "KeyReleased"): self.longpress(key); def shortpress(self, key): # Single presses are handled in hass; sequences must start with a long press if (len(self.currentSequence) == 0): return; self.currentSequence.append(key); self.reset_tot(); def longpress(self, key): self.turn_off("automation.wallmote"); self.currentSequence = [key]; self.reset_tot(); def reset_tot(self): if (hasattr(self,"tot")): self.tot.cancel(); self.tot = Timer(TIMEOUT,self.process_sequence); self.tot.start(); def process_sequence(self): self.tot.cancel(); # So process_sequence can be called directly self.turn_on("automation.wallmote"); # Clear self.currentSequence so future shortpresses are not # treated as part of a sequence sequence = self.currentSequence; self.currentSequence = []; self.log(sequence); self.run_action(sequence); def run_action(self, sequence, tree=tree): if (len(sequence) > 0): # Deeper into the rabbit hole category = sequence.pop(0); if (category in tree): self.run_action(sequence,tree[category]); else: # Perform an action from here if ("function" in tree): tree["function"](self); elif ("service" in tree): self.call_service(tree["service"]);