Further work on tap_dial. The buttons seem to work OK, however retrieving the current light brightness does not work. JSON dump of attributes shows it to be null. The "dial" changes worked until I tried to limit the brightness value, because of the null brightness value. Committing these changes so I don't lose them.

This commit is contained in:
Jim Lancaster
2025-01-20 17:24:41 -06:00
parent e7b9bef8c5
commit be632795a9
3 changed files with 73 additions and 44 deletions

View File

@@ -43,7 +43,7 @@ my_timer:
LightColorTest: LightColorTest:
module: color_test module: color_test
class: TapDial class: TapDial
entity: sensor.tapdial03_action entity: sensor.tapdial01a_action
init_brightness: 200 init_brightness: 200
max_brightness: 254 max_brightness: 254
step_size: 25 step_size: 25

View File

@@ -7,7 +7,7 @@ class TapDial(Hass):
def initialize(self): def initialize(self):
self.tap_dial.listen_state(self.handle_state_change) self.tap_dial.listen_state(self.handle_state_change)
self.log(f'0.0 Tap Dial entity: {self.tap_dial.entity_id}') self.log(f'010 Tap Dial entity: {self.tap_dial.entity_id}')
@property @property
def tap_dial(self) -> Entity: def tap_dial(self) -> Entity:
@@ -29,8 +29,6 @@ class TapDial(Hass):
def active_entity_is_on(self) -> bool: def active_entity_is_on(self) -> bool:
return self.active_entity.get_state() == 'on' return self.active_entity.get_state() == 'on'
# Experiment to read and write various properties to a light....
@property @property
def light_brightness(self) -> int: def light_brightness(self) -> int:
return self.active_entity.attributes['brightness'] return self.active_entity.attributes['brightness']
@@ -56,47 +54,48 @@ class TapDial(Hass):
return self.active_entity.turn_on(effect=val) return self.active_entity.turn_on(effect=val)
def handle_state_change(self, entity: str, attribute: str, old: str, new: str, **kwargs): def handle_state_change(self, entity: str, attribute: str, old: str, new: str, **kwargs):
self.log(f'0.1 {new}') self.log(f'059 {new}')
# Dial actions # Dial actions
if new.startswith('dial_rotate') and self.active_entity_is_on: if new.startswith('dial_rotate') and self.active_entity_is_on:
self.log('Ignore dial_rotate commands for now...') self.log('063 Ignore dial_rotate commands for now...')
# Button actions # Button actions
elif new.endswith('release'): elif new.endswith('release'):
_, n, typ, _ = new.split('_', 4) _, n, typ, _ = new.split('_', 4)
# type will be either press or hold # type will be either press or hold
self.log(f'2.1 Button {n} {typ}') self.log(f'069 Button {n} {typ}')
if eid := self.args.get(f'button{n}'): if eid := self.args.get(f'button{n}'):
self.active_entity = self.get_entity(eid) self.active_entity = self.get_entity(eid)
self.log(f'2.1.1 Set active entity to: {self.active_entity.name}') self.log(f'073 Set active entity to: {self.active_entity.name}')
self.log(json.dumps(self.active_entity.get_state('all'), indent=4))
domain, entity = eid.split('.') domain, entity = eid.split('.')
self.log(f'076 domain: {domain}, entity: {entity}')
match domain: match domain:
case 'light': case 'light':
# Set the light to maximum brightness if the button is held. # Set the light to maximum brightness if the button is held.
if typ == 'hold': if typ == 'hold':
self.log('Ignore button_hold command for now...') self.log('080 Ignore button_hold command for now...')
else: else:
if self.active_entity_is_on: if self.active_entity_is_on:
self.log(f'Current values for Brightness: {self.light_brightness}, RGB Color: {self.light_color}, Effect: {self.light_effect}') self.log(f'083 6Current values for Brightness: {self.light_brightness}, RGB Color: {self.light_color}, Effect: {self.light_effect}')
# self.log(f'New values for Color: {self.args.get(f"color{n}")} and Effect: {self.args.get(f"effect{n}")}') # self.log(f'New values for Color: {self.args.get(f"color{n}")} and Effect: {self.args.get(f"effect{n}")}')
hex = self.args.get(f"color{n}") hex = self.args.get(f"color{n}")
# self.log(f'Translate {self.args.get(f"color{n}")} to RGB {self.hex_to_rgb(hex)} ') # self.log(f'Translate {self.args.get(f"color{n}")} to RGB {self.hex_to_rgb(hex)} ')
br_val = self.args.get(f'brightness{n}') br_val = self.args.get(f'brightness{n}')
match n: match n:
case '1': # do nothing case '1': # do nothing
self.log(f'Made it to case{n}') self.log(f'090 Made it to case{n}')
# self.active_entity.turn_on(brightness=br_val,rgb_color=self.hex_to_rgb(hex),transition=10) # self.active_entity.turn_on(brightness=br_val,rgb_color=self.hex_to_rgb(hex),transition=10)
self.active_entity.turn_on(brightness=br_val,rgb_color=self.hex_to_rgb(hex)) self.active_entity.turn_on(brightness=br_val,rgb_color=self.hex_to_rgb(hex))
# self.active_entity.turn_on(rgb_color=self.hex_to_rgb('FFFFFF')) # self.active_entity.turn_on(rgb_color=self.hex_to_rgb('FFFFFF'))
# self.active_entity.turn_on(effect='candle') # self.active_entity.turn_on(effect='candle')
# self.light_color = self.hex_to_rgb(hex) # self.light_color = self.hex_to_rgb(hex)
# self.light_effect = self.args.get(f'effect{n}') # self.light_effect = self.args.get(f'effect{n}')
# self.log(json.dumps(self.active_entity.get_state('all'), indent=4)) self.log(json.dumps(self.active_entity.get_state('all'), indent=4))
case '2': # attempt to change brightness, color case '2': # attempt to change brightness, color
self.log(f'Made it to case{n}') self.log(f'099 Made it to case{n}')
# self.active_entity.turn_on(brightness=br_val,rgb_color=self.hex_to_rgb(hex),transition=10) # self.active_entity.turn_on(brightness=br_val,rgb_color=self.hex_to_rgb(hex),transition=10)
self.active_entity.turn_on(brightness=br_val,rgb_color=self.hex_to_rgb(hex)) self.active_entity.turn_on(brightness=br_val,rgb_color=self.hex_to_rgb(hex))
# self.active_entity.turn_on(rgb_color=self.hex_to_rgb('FFFFFF')) # self.active_entity.turn_on(rgb_color=self.hex_to_rgb('FFFFFF'))
@@ -105,13 +104,13 @@ class TapDial(Hass):
# self.light_effect = self.args.get(f'effect{n}') # self.light_effect = self.args.get(f'effect{n}')
# self.log(json.dumps(self.active_entity.get_state('all'), indent=4)) # self.log(json.dumps(self.active_entity.get_state('all'), indent=4))
case '3': # attempt to apply 'candle' effect case '3': # attempt to apply 'candle' effect
self.log(f'Made it to case{n}') self.log(f'108 Made it to case{n}')
self.active_entity.turn_on(effect='stop_hue_effect') self.active_entity.turn_on(effect='stop_hue_effect')
# self.active_entity.turn_on(effect='candle') # self.active_entity.turn_on(effect='candle')
self.active_entity.set_state(effect='fireplace') self.active_entity.set_state(effect='fireplace')
# self.log(json.dumps(self.active_entity.get_state('all'), indent=4)) # self.log(json.dumps(self.active_entity.get_state('all'), indent=4))
case '4': # json.dump case '4': # json.dump
self.log(f'Made it to case{n}') self.log(f'114 Made it to case{n}')
# self.active_entity.turn_on(brightness=br_val,rgb_color=self.hex_to_rgb(hex),transition=10) # self.active_entity.turn_on(brightness=br_val,rgb_color=self.hex_to_rgb(hex),transition=10)
# self.active_entity.turn_on(brightness=br_val,rgb_color=self.hex_to_rgb(hex)) # self.active_entity.turn_on(brightness=br_val,rgb_color=self.hex_to_rgb(hex))
# self.active_entity.turn_on(rgb_color=self.hex_to_rgb('FFFFFF')) # self.active_entity.turn_on(rgb_color=self.hex_to_rgb('FFFFFF'))
@@ -121,9 +120,9 @@ class TapDial(Hass):
self.log(json.dumps(self.active_entity.get_state('all'), indent=4)) self.log(json.dumps(self.active_entity.get_state('all'), indent=4))
else: else:
self.log('The light is off...') self.log('124 The light is off...')
case 'switch': case 'switch':
self.log('Ignore non-light entities for now...') self.log('126 Ignore non-light entities for now...')
# This function was written by chatgpt... # This function was written by chatgpt...
def hex_to_rgb(self, hex_color: str) -> list: def hex_to_rgb(self, hex_color: str) -> list:
@@ -133,7 +132,7 @@ class TapDial(Hass):
# Check if the string has a valid length # Check if the string has a valid length
if len(hex_color) != 6: if len(hex_color) != 6:
raise ValueError(f"Invalid hex color: {hex_color}. Must be 6 characters long.") raise ValueError(f"136 Invalid hex color: {hex_color}. Must be 6 characters long.")
try: try:
# Split the hex color into its RGB components and convert to integers # Split the hex color into its RGB components and convert to integers
@@ -142,4 +141,4 @@ class TapDial(Hass):
b = int(hex_color[4:6], 16) b = int(hex_color[4:6], 16)
return [r, g, b] return [r, g, b]
except ValueError: except ValueError:
raise ValueError(f"Invalid hex color: {hex_color}. Must contain only valid hex digits.") raise ValueError(f"145 Invalid hex color: {hex_color}. Must contain only valid hex digits.")

View File

@@ -1,12 +1,12 @@
from appdaemon.entity import Entity from appdaemon.entity import Entity
from appdaemon.plugins.hass.hassapi import Hass from appdaemon.plugins.hass.hassapi import Hass
import json
class TapDial(Hass): class TapDial(Hass):
active_entity: Entity active_entity: Entity
def initialize(self): def initialize(self):
self.tap_dial.listen_state(self.handle_state_change) self.tap_dial.listen_state(self.handle_state_change)
self.log(f'0.0 Tap Dial entity: {self.tap_dial.entity_id}')
@property @property
def tap_dial(self) -> Entity: def tap_dial(self) -> Entity:
@@ -29,16 +29,36 @@ class TapDial(Hass):
return self.active_entity.get_state() == 'on' return self.active_entity.get_state() == 'on'
@property @property
def current_brightness(self) -> int: def light_brightness(self) -> int:
return self.active_entity.attributes['brightness'] return self.active_entity.attributes['brightness']
@light_brightness.setter
def light_brightness(self, val: int):
return self.active_entity.turn_on(brightness=val)
@property
def light_color(self) -> list[int]:
return self.active_entity.get_state('rgb_color')
@light_color.setter
def light_color(self, val: list[int]):
return self.active_entity.turn_on(rgb_color=val)
@property
def light_effect(self) -> str:
return self.active_entity.get_state('effect')
@light_effect.setter
def light_effect(self, val: str):
return self.active_entity.turn_on(effect=val)
def handle_state_change(self, entity: str, attribute: str, old: str, new: str, **kwargs): def handle_state_change(self, entity: str, attribute: str, old: str, new: str, **kwargs):
self.log(f'0.1 {new}') self.log(f'056 State change: {new}')
# Dial actions # Dial actions
if new.startswith('dial_rotate') and self.active_entity_is_on: if new.startswith('dial_rotate') and self.active_entity_is_on:
_, _, dir, speed = new.split('_', 4) _, _, dir, speed = new.split('_', 4)
self.log(f'1.1 Direction {dir} speed {speed}') self.log(f'061 Direction {dir} speed {speed}')
match speed: match speed:
case 'step': case 'step':
rate = self.step_size rate = self.step_size
@@ -47,51 +67,61 @@ class TapDial(Hass):
case 'fast': case 'fast':
rate = 3 * self.step_size rate = 3 * self.step_size
val = self.active_entity.attributes['brightness'] val = self.active_entity.attributes['brightness']
self.log(f'1.2 Brightness value = {val} Change rate = {rate}') self.log(f'070 Brightness value = {val} Change rate = {rate}')
if dir == 'right': if dir == 'right':
val += rate if val < self.max_brightness:
self.active_entity.turn_on(brightness=val) if (val + rate) > self.max_brightness:
self.log(f'1.2.1 Brightness value = {val}') val = self.max_brightness
else:
val += rate
self.active_entity.turn_on(brightness=val)
self.log(f'078 Brightness value = {val}')
else: else:
val -= rate if val > 0:
self.active_entity.turn_on(brightness=val) if (val - rate) < 0:
self.log(f'1.2.2 Brightness value = {val}') val = 0
else:
val -= rate
self.active_entity.turn_on(brightness=val)
self.log(f'086 Brightness value = {val}')
# Button actions # Button actions
elif new.endswith('release'): elif new.endswith('release'):
_, n, typ, _ = new.split('_', 4) _, n, typ, _ = new.split('_', 4)
# type will be either press or hold # type will be either press or hold
self.log(f'2.1 Button {n} {typ}') self.log(f'092 Button {n} {typ}')
if eid := self.args.get(f'button{n}'): if eid := self.args.get(f'button{n}'):
self.active_entity = self.get_entity(eid) self.active_entity = self.get_entity(eid)
self.log(f'2.1.1 Set active entity to: {self.active_entity.name}') self.log(f'096 Set active entity to: {self.active_entity.name}')
self.log(json.dumps(self.active_entity.get_state('all'), indent=4))
domain, entity = eid.split('.') domain, entity = eid.split('.')
self.log(f'099 Curent domain: {domain}, entity: {entity}')
match domain: match domain:
case 'light': case 'light':
# Set the light to maximum brightness if the button is held. # Set the light to maximum brightness if the button is held.
if typ == 'hold': if typ == 'hold':
self.active_entity.turn_on(brightness=self.max_brightness) self.active_entity.turn_on(brightness=self.max_brightness)
self.log(f'2.1.1 Set {self.active_entity.friendly_name} to maximum brightness {self.max_brightness}') self.log(f'105 Set {self.active_entity.friendly_name} to maximum brightness {self.max_brightness}')
else: else:
if self.active_entity_is_on: if self.active_entity_is_on:
self.active_entity.turn_off() self.active_entity.turn_off()
self.log(f'2.1.2 Turn {self.active_entity.friendly_name} off') self.log(f'109 Turn off {self.active_entity.friendly_name}')
else: else:
self.active_entity.turn_on(brightness=self.init_brightness) self.active_entity.turn_on(brightness=self.init_brightness)
self.log(f'2.1.3 Turn {self.active_entity.friendly_name} on with brightness {self.init_brightness}') self.log(f'112 Turn on {self.active_entity.friendly_name} with brightness {self.init_brightness}')
case 'switch': case 'switch':
self.active_entity.toggle() self.active_entity.toggle()
self.log(f'2.1.4 Toggle on/off power to {self.active_entity.friendly_name}') self.log(f'115 Toggle on/off power to {self.active_entity.friendly_name}')
elif switch := self.args.get(f'button{n}'): elif switch := self.args.get(f'button{n}'):
self.active_entity = self.get_entity(switch) self.active_entity = self.get_entity(switch)
self.log(f'2.2 Set active entity to: {self.active_entity.friendly_name}') self.log(f'119 Set active entity to: {self.active_entity.friendly_name}')
onoff = self.active_entity.get_state() onoff = self.active_entity.get_state()
self.log(f'2.2 {self.active_entity.friendly_name} is currently {onoff}') self.log(f'114 {self.active_entity.friendly_name} is currently {onoff}')
self.active_entity.toggle() self.active_entity.toggle()
self.log(f'2.2 Toggle on/off power to {self.active_entity.friendly_name}') self.log(f'116 Toggle on/off power to {self.active_entity.friendly_name}')
# This function was written by chatgpt! # This function was written by chatgpt!
def hex_to_rgb(self, hex_color: str) -> list: def hex_to_rgb(self, hex_color: str) -> list:
@@ -101,7 +131,7 @@ class TapDial(Hass):
# Check if the string has a valid length # Check if the string has a valid length
if len(hex_color) != 6: if len(hex_color) != 6:
raise ValueError(f"Invalid hex color: {hex_color}. Must be 6 characters long.") raise ValueError(f"126 Invalid hex color: {hex_color}. Must be 6 characters long.")
try: try:
# Split the hex color into its RGB components and convert to integers # Split the hex color into its RGB components and convert to integers
@@ -110,5 +140,5 @@ class TapDial(Hass):
b = int(hex_color[4:6], 16) b = int(hex_color[4:6], 16)
return [r, g, b] return [r, g, b]
except ValueError: except ValueError:
raise ValueError(f"Invalid hex color: {hex_color}. Must contain only valid hex digits.") raise ValueError(f"135 Invalid hex color: {hex_color}. Must contain only valid hex digits.")