Restructure and add HTTP API
- Move check_action and run_action into actions.py - Add HTTP API - Minor formatting changes
This commit is contained in:
		
							parent
							
								
									e58b1b21ed
								
							
						
					
					
						commit
						96a25cf79b
					
				
					 3 changed files with 75 additions and 58 deletions
				
			
		
							
								
								
									
										48
									
								
								actions.py
									
										
									
									
									
								
							
							
						
						
									
										48
									
								
								actions.py
									
										
									
									
									
								
							|  | @ -1,4 +1,5 @@ | ||||||
| #!/usr/bin/python3 | #!/usr/bin/python3 | ||||||
|  | import inspect | ||||||
| 
 | 
 | ||||||
| ############### | ############### | ||||||
| # Action Tree # | # Action Tree # | ||||||
|  | @ -506,21 +507,54 @@ tree = { | ||||||
| 
 | 
 | ||||||
| def set_brightness(hass, device, brightness): | def set_brightness(hass, device, brightness): | ||||||
|     # Convert brightness from percent to 0-255 |     # Convert brightness from percent to 0-255 | ||||||
|     brightness = brightness * 255 / 100; |     brightness = brightness * 255 / 100 | ||||||
|     hass.turn_on(device, brightness=brightness); |     hass.turn_on(device, brightness=brightness) | ||||||
| 
 | 
 | ||||||
| def set_temperature(hass, device, temperature): | def set_temperature(hass, device, temperature): | ||||||
|     hass.call_service("climate/set_temperature",entity_id="climate."+device,temperature=temperature); |     hass.call_service("climate/set_temperature",entity_id="climate."+device,temperature=temperature) | ||||||
| 
 | 
 | ||||||
| def gree_set_mode(hass, device, mode): | def gree_set_mode(hass, device, mode): | ||||||
|     hass.call_service("climate/set_hvac_mode",entity_id="climate."+device,hvac_mode=mode); |     hass.call_service("climate/set_hvac_mode",entity_id="climate."+device,hvac_mode=mode) | ||||||
| 
 | 
 | ||||||
| def gree_set_fan_level(hass, device, level): | def gree_set_fan_level(hass, device, level): | ||||||
|     if (level == "quiet"): |     if (level == "quiet"): | ||||||
|         hass.turn_on(f"switch.{device}_quiet"); |         hass.turn_on(f"switch.{device}_quiet") | ||||||
|     else:  |     else:  | ||||||
|         hass.turn_off(f"switch.{device}_quiet"); |         hass.turn_off(f"switch.{device}_quiet") | ||||||
|         hass.call_service("climate/set_fan_mode",entity_id="climate."+device,fan_mode=level); |         hass.call_service("climate/set_fan_mode",entity_id="climate."+device,fan_mode=level) | ||||||
| 
 | 
 | ||||||
| def select_source(hass, device, source): | def select_source(hass, device, source): | ||||||
|     hass.call_service("media_player/select_source",entity_id=device,source=source) |     hass.call_service("media_player/select_source",entity_id=device,source=source) | ||||||
|  | 
 | ||||||
|  | ############################### | ||||||
|  | # Action Processing Functions # | ||||||
|  | ############################### | ||||||
|  | 
 | ||||||
|  | def check_action(sequence, tree=tree): | ||||||
|  |     if (len(sequence) > 0): | ||||||
|  |         # Deeper into the rabbit hole | ||||||
|  |         category = sequence.pop(0) | ||||||
|  |         if (category in tree): return check_action(sequence,tree[category]) | ||||||
|  |         else: return False | ||||||
|  |     else: | ||||||
|  |         # We've the end of the sequence thus far, check if this is the end of a branch | ||||||
|  |         # If any of {1..4} exist as keys in tree there's more options | ||||||
|  |         return not any(i in tree for i in range(1,4)) | ||||||
|  | 
 | ||||||
|  | def run_action(hass, sequence, tree=tree): | ||||||
|  |     if (len(sequence) > 0): | ||||||
|  |         # Deeper into the rabbit hole | ||||||
|  |         category = sequence.pop(0) | ||||||
|  |         if (category in tree): return run_action(hass,sequence,tree[category]) | ||||||
|  |         else: | ||||||
|  |             hass.log("No match") | ||||||
|  |             return False | ||||||
|  |     else: | ||||||
|  |         # Perform an action from here and log the description | ||||||
|  |         if ("function" in tree): tree["function"](hass) | ||||||
|  |         else: | ||||||
|  |             hass.log("No match") | ||||||
|  |             return False | ||||||
|  |         if ("description" in tree): hass.log(tree["description"]) | ||||||
|  |         else: hass.log(inspect.getsource(tree["function"]).strip()) | ||||||
|  |         return True | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								http_api.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								http_api.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | #!/usr/bin/python3 | ||||||
|  | import hassapi as hass | ||||||
|  | from actions import run_action | ||||||
|  | 
 | ||||||
|  | class HttpApi(hass.Hass): | ||||||
|  |     def initialize(self): | ||||||
|  |         self.register_endpoint(self.api_request, '4button') | ||||||
|  | 
 | ||||||
|  |     def api_request(self, data, kwargs): | ||||||
|  |         if 'sequence' not in data: return 'Missing sequence', 400 | ||||||
|  |         # Let AppDaemon handle API response on error | ||||||
|  |         if (run_action(self,data['sequence'])): | ||||||
|  |             return 'Success', 200 | ||||||
|  |         else: | ||||||
|  |             return 'No match', 400 | ||||||
							
								
								
									
										70
									
								
								wallmote.py
									
										
									
									
									
								
							
							
						
						
									
										70
									
								
								wallmote.py
									
										
									
									
									
								
							|  | @ -1,17 +1,12 @@ | ||||||
| #!/usr/bin/python3 | #!/usr/bin/python3 | ||||||
| import hassapi as hass | import hassapi as hass | ||||||
| import inspect |  | ||||||
| from threading import Timer | from threading import Timer | ||||||
| from actions import tree | from actions import check_action, run_action | ||||||
| 
 |  | ||||||
| ################## |  | ||||||
| # Sequence Logic # |  | ||||||
| ################## |  | ||||||
| 
 | 
 | ||||||
| # Short presses will be recorded into a sequence array. Long presses are reserved for | # Short presses will be recorded into a sequence array. Long presses are reserved for | ||||||
| # actions directly in hass for now; use KeyReleased event to capture them. A sequence  | # actions directly in hass for now; use KeyReleased event to capture them. A sequence  | ||||||
| # can be ended either immediately by recognizing a full sequence or by TIMEOUT seconds  | # can be ended either immediately by recognizing a full sequence or by TIMEOUT seconds  | ||||||
| # passing. (The former is NYI but the code is capable as is.) | # passing. | ||||||
| 
 | 
 | ||||||
| # - self.currentSequence contains an array of keys representing a sequence that is | # - self.currentSequence contains an array of keys representing a sequence that is | ||||||
| #   actively being entered. | #   actively being entered. | ||||||
|  | @ -21,58 +16,31 @@ TIMEOUT=1 | ||||||
| 
 | 
 | ||||||
| class WallmotePlumbing(hass.Hass): | class WallmotePlumbing(hass.Hass): | ||||||
|     def initialize(self): |     def initialize(self): | ||||||
|         self.currentSequence = []; |         self.currentSequence = [] | ||||||
|         self.listen_event(self.onevent, "zwave_js_value_notification"); |         self.listen_event(self.onevent, "zwave_js_value_notification") | ||||||
| 
 | 
 | ||||||
|     def onevent(self, _, event,  kwargs): |     def onevent(self, _, event,  kwargs): | ||||||
|         if (event.get('value') == 'KeyHeldDown'): return; |         if (event.get('value') == 'KeyHeldDown'): return | ||||||
|         if (event.get('device_id') != '3f7add36f94839b1446886b5dab35e6d'): return; |         if (event.get('device_id') != '3f7add36f94839b1446886b5dab35e6d'): return | ||||||
|         key = int(event.get('property_key')); |         key = int(event.get('property_key')) | ||||||
|         # We don't get a KeyPressed event at all for long presses |         # We don't get a KeyPressed event at all for long presses | ||||||
|         evt = event.get('value'); |         evt = event.get('value') | ||||||
|         if (evt == "KeyPressed"): self.shortpress(key); |         if (evt == "KeyPressed"): self.shortpress(key) | ||||||
| 
 | 
 | ||||||
|     def shortpress(self, key): |     def shortpress(self, key): | ||||||
|         self.currentSequence.append(key); |         self.currentSequence.append(key) | ||||||
|         self.reset_tot(); |         self.reset_tot() | ||||||
|         # Don't wait for tot to expire if we've already hit the end of a branch |         # Don't wait for tot to expire if we've already hit the end of a branch | ||||||
|         if (self.check_action(self.currentSequence.copy())): self.process_sequence(); |         if (check_action(self.currentSequence.copy())): self.process_sequence() | ||||||
| 
 | 
 | ||||||
|     def reset_tot(self): |     def reset_tot(self): | ||||||
|         if (hasattr(self,"tot")): self.tot.cancel(); |         if (hasattr(self,"tot")): self.tot.cancel() | ||||||
|         self.tot = Timer(TIMEOUT,self.process_sequence); |         self.tot = Timer(TIMEOUT,self.process_sequence) | ||||||
|         self.tot.start(); |         self.tot.start() | ||||||
| 
 | 
 | ||||||
|     def process_sequence(self): |     def process_sequence(self): | ||||||
|         self.tot.cancel(); # So process_sequence can be called directly |         self.tot.cancel(); # So process_sequence can be called directly | ||||||
|         sequence = self.currentSequence; |         sequence = self.currentSequence | ||||||
|         self.currentSequence = []; |         self.currentSequence = [] | ||||||
|         self.log(sequence); |         self.log(sequence) | ||||||
|         self.run_action(sequence); |         run_action(self,sequence) | ||||||
| 
 |  | ||||||
|     def check_action(self, sequence, tree=tree): |  | ||||||
|         if (len(sequence) > 0): |  | ||||||
|             # Deeper into the rabbit hole |  | ||||||
|             category = sequence.pop(0); |  | ||||||
|             if (category in tree): return self.check_action(sequence,tree[category]); |  | ||||||
|             else: return false; |  | ||||||
|         else: |  | ||||||
|             # We've the end of the sequence thus far, check if this is the end of a branch |  | ||||||
|             # If any of {1..4} exist as keys in tree there's more options |  | ||||||
|             return not any(i in tree for i in range(1,4)); |  | ||||||
| 
 |  | ||||||
|     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: self.log("No match"); |  | ||||||
|         else: |  | ||||||
|             # Perform an action from here and log the description |  | ||||||
|             if ("function" in tree): tree["function"](self); |  | ||||||
|             elif ("service" in tree): self.call_service(tree["service"]); |  | ||||||
|             else: |  | ||||||
|                 self.log("No match"); |  | ||||||
|                 return; |  | ||||||
|             if ("description" in tree): self.log(tree["description"]); |  | ||||||
|             else: self.log(inspect.getsource(tree["function"]).strip()); |  | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue