Skip to content

Commit

Permalink
Update to 1.1.4
Browse files Browse the repository at this point in the history
  • Loading branch information
Pythm committed Feb 25, 2024
1 parent 5fcd877 commit c5614b2
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 56 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ your_room_name:
- switch.toggle_bulb
toggle: 2 # On toggles to get desired dim
num_dim_steps: 3 # Number of dim steps in bulb
toggle_speed: 0.8 # Set time in seconds between each toggle
light_modes:
- mode: night
toggle: 3
Expand Down Expand Up @@ -532,3 +533,4 @@ key | optional | type | default | introduced in | description
`room_lux_turn_on` | True | int | | v1.0.0 | Room lux to turn on light if below
`room_lux_turn_off` | True | int | | v1.0.0 | Room lux to turn off light if above
`conditions` | True | list | | v1.0.0 | Conditions as if statement. Inherits Appdaemon Api as ADapi
`toggle_speed` | True | float | 1 | v1.1.4 | Set time in seconds between each toggle. Supports sub second with 0.x
183 changes: 127 additions & 56 deletions apps/Lightwand/lightwand.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
@Pythm / https://github.com/Pythm
"""

__version__ = "1.1.3"
__version__ = "1.1.4"

import hassapi as hass
import datetime
Expand Down Expand Up @@ -143,7 +143,6 @@ def initialize(self):
if 'rain_sensor' in self.args:
rain_sensor = self.args['rain_sensor']
self.listen_state(self.update_rain_amount, rain_sensor,
constrain_state=lambda x: float(x) > 0,
namespace = HASS_namespace
)
new_rain_amount = self.get_state(rain_sensor)
Expand Down Expand Up @@ -238,6 +237,7 @@ def initialize(self):
conditions = l.get('conditions', {'True'}),
toggle = l.get('toggle',3),
num_dim_steps = l.get('num_dim_steps',3),
toggle_speed = l.get('toggle_speed',1),
json_path = self.JSON_PATH,
usePersistentStorage = self.usePersistentStorage,
HASS_namespace = HASS_namespace
Expand Down Expand Up @@ -509,9 +509,16 @@ def state_changed(self, entity, attribute, old, new, kwargs):

# Lux / weather
def out_lux_state(self, entity, attribute, old, new, kwargs):
if self.outLux1 != float(new):
self.outLux1 = float(new)

try:
if self.outLux1 != float(new):
self.outLux1 = float(new)
except ValueError as ve:
self.log(f"Not able to get new outlux. ValueError: {ve}", level = 'DEBUG')
except TypeError as te:
self.log(f"Not able to get new outlux. TypeError: {te}", level = 'DEBUG')
except Exception as e:
self.log(f"Not able to get new outlux. Exception: {e}", level = 'WARNING')
else:
self.newOutLux()


Expand Down Expand Up @@ -648,21 +655,29 @@ def newRoomLux(self):


def update_rain_amount(self, entity, attribute, old, new, kwargs):
try:
if self.RAIN != float(new):
if new != old:
try:
self.RAIN = float(new)
except Exception as e:
self.log(f"Rain amount unavailable. Exception: {e}", level = 'DEBUG')
self.RAIN = 0.0
for light in self.roomlight:
light.rain_amount = self.RAIN
except ValueError as ve:
self.log(f"Rain amount unavailable. ValueError: {ve}", level = 'DEBUG')
self.RAIN = 0.0
except TypeError as te:
self.log(f"Rain amount unavailable. TypeError: {te}", level = 'DEBUG')
self.RAIN = 0.0
except Exception as e:
self.log(f"Not able to get new rain amount. Exception: {e}", level = 'WARNING')
self.RAIN = 0.0

for light in self.roomlight:
light.rain_amount = self.RAIN


# Media Player / sensors
def media_on(self, entity, attribute, old, new, kwargs):
if self.LIGHT_MODE == 'morning':
self.LIGHT_MODE = 'normal'
self.check_mediaplayers_off()
if self.LIGHT_MODE != 'night':
self.check_mediaplayers_off()


def media_off(self, entity, attribute, old, new, kwargs):
Expand Down Expand Up @@ -752,12 +767,21 @@ def __init__(self, api,
if string[:7] == 'switch.':
self.isON = self.ADapi.get_state(self.lights[0]) == 'on'

# Helpers to check if conditions to turn on/off light has changed
self.wereMotion = False
self.prev_checkOnConditions = None
self.prev_checkLuxConstraints = None


if self.automations:
""" Set up automations with times defined and check data
"""
test_time = self.ADapi.parse_time('00:00:00')
if test_time != self.ADapi.parse_time(self.automations[0]['time']):
if not 'time' in self.automations[0]:
self.automations[0].update(
{'time': '00:00:00'}
)
elif test_time != self.ADapi.parse_time(self.automations[0]['time']):
self.automations.insert(0,
{'time': '00:00:00', 'state': 'turn_off'}
)
Expand Down Expand Up @@ -821,7 +845,11 @@ def __init__(self, api,
"""
if type(self.motionlight) == list:
test_time = self.ADapi.parse_time('00:00:00')
if test_time != self.ADapi.parse_time(self.motionlight[0]['time']):
if not 'time' in self.motionlight[0]:
self.motionlight[0].update(
{'time': '00:00:00'}
)
elif test_time != self.ADapi.parse_time(self.motionlight[0]['time']):
self.motionlight.insert(0,
{'time': '00:00:00', 'state': 'turn_off'}
)
Expand Down Expand Up @@ -870,7 +898,11 @@ def __init__(self, api,
"""
if 'automations' in mode:
test_time = self.ADapi.parse_time('00:00:00')
if test_time != self.ADapi.parse_time(mode['automations'][0]['time']):
if not 'time' in mode['automations'][0]:
mode['automations'][0].update(
{'time': '00:00:00'}
)
elif test_time != self.ADapi.parse_time(mode['automations'][0]['time']):
mode['automations'].insert(0,
{'time': '00:00:00', 'state': 'turn_off'}
)
Expand Down Expand Up @@ -970,6 +1002,8 @@ def run_daily_lights(self, kwargs):
""" Updates light with new data based on times given in configuration
"""
if not self.motion:
self.prev_checkOnConditions = None
self.prev_checkLuxConstraints = None
self.setLightMode()
elif type(self.motionlight) == list:
target_num = self.find_time(automation = self.motionlight)
Expand Down Expand Up @@ -1021,6 +1055,24 @@ def checkLuxConstraints(self):
def setLightMode(self, lightmode = 'None'):
""" The main function/logic to handle turning on / off lights based on mode selected.
"""
# Checking if anything has changed.
if (
(lightmode == self.lightmode
or lightmode == 'None')
and not self.wereMotion
and self.prev_checkOnConditions == self.checkOnConditions()
and self.prev_checkLuxConstraints == self.checkLuxConstraints()
):
return
elif (
self.wereMotion
and not self.motion
):
self.wereMotion = False

self.prev_checkOnConditions = self.checkOnConditions()
self.prev_checkLuxConstraints = self.checkLuxConstraints()

if lightmode != self.lightmode:
if self.dimHandler:
if self.ADapi.timer_running(self.dimHandler):
Expand Down Expand Up @@ -1177,6 +1229,7 @@ def setMotion(self, lightmode = 'None'):
return

self.motion = True
self.wereMotion = True
if type(self.motionlight) == list:
self.setLightAutomation(automations = self.motionlight)

Expand Down Expand Up @@ -1245,6 +1298,12 @@ def setLightAutomation(self, automations, offset = 0 ):
# Brightness 0-255 for HA and Zigbee2mqtt control
if 'brightness' in target_light[target_num]['light_data']:

if (
self.motion
and self.brightness -2 >= int(target_light_data['brightness'] + offset)
):
return

if 'dimrate' in target_light[target_num]:
newbrightness = self.findBrightnessWhenDimRate(automation = target_light) + offset
target_light_data.update(
Expand All @@ -1266,12 +1325,6 @@ def setLightAutomation(self, automations, offset = 0 ):
target_light_data.update(
{'brightness' : 1}
)

elif (
self.motion
and self.brightness > target_light_data['brightness']
):
return

self.turn_on_lights(light_data = target_light_data)

Expand Down Expand Up @@ -1408,6 +1461,8 @@ def BrightnessUpdated(self, entity, attribute, old, new, kwargs):
self.brightness = int(new)
except TypeError:
self.brightness = 0
except Exception as e:
self.ADapi.log(f"Error getting new brightness: {new}. Exception: {e}", level = 'WARNING')


#Updates persistent storage for lights with adjust/manual modes
Expand Down Expand Up @@ -1565,15 +1620,19 @@ def light_event_MQTT(self, event_name, data, kwargs):
# self.ADapi.log(f"{data['topic']} Color in lux: {lux_data['color']}")

if 'brightness' in lux_data:
try:
self.brightness = int(lux_data['brightness'])
except (ValueError, TypeError):
self.isON = lux_data['state'] == 'ON'
if not self.isON:
self.brightness = 0
self.current_light_data = {}
else:
try:
self.brightness = int(lux_data['brightness'])
except (ValueError, TypeError):
self.brightness = 0

self.isON = self.brightness != 0
if 'brightness' in self.current_light_data:
if self.current_light_data['brightness'] != self.brightness:
self.current_light_data = {}
if 'brightness' in self.current_light_data:
if self.current_light_data['brightness'] != self.brightness:
self.current_light_data = {}

elif 'value' in lux_data:
if type(lux_data['value']) == bool:
Expand Down Expand Up @@ -1701,6 +1760,7 @@ def __init__(self, api,
conditions,
toggle,
num_dim_steps,
toggle_speed,
json_path,
usePersistentStorage,
HASS_namespace
Expand All @@ -1710,6 +1770,10 @@ def __init__(self, api,
self.current_toggle = 0
self.toggle_lightbulb = toggle * 2 - 1
self.fullround_toggle = num_dim_steps * 2
try:
self.toggle_speed = float(toggle_speed)
except(ValueError, TypeError):
self.toggle_speed = 1

# Persistent storage
if usePersistentStorage:
Expand Down Expand Up @@ -1779,13 +1843,14 @@ def setLightMode(self, lightmode = 'None'):

elif self.current_toggle > toggle_bulb:
self.current_toggle -= self.fullround_toggle
sec = 1
sec = 0.1

while self.current_toggle < toggle_bulb:
self.ADapi.run_in(self.toggle_light, sec)
self.current_toggle += 1
sec += 1

sec += self.toggle_speed


elif 'state' in mode:
if 'turn_off' in mode['state']:
# Turns off light
Expand Down Expand Up @@ -1835,20 +1900,21 @@ def setLightMode(self, lightmode = 'None'):

elif self.current_toggle > self.toggle_lightbulb:
self.current_toggle -= self.fullround_toggle
sec = 1
sec = 0.1

while self.current_toggle < 1:
self.ADapi.run_in(self.toggle_light, sec)
self.current_toggle += 1
sec += 1
sec += self.toggle_speed


# Persistent toggle
if self.usePersistentStorage:
with open(self.JSON_PATH, 'r') as json_read:
lightwand_data = json.load(json_read)

lightwand_data[self.lights[0]].update(
{"toggle" : self.current_toggle}
{"toggle" : 1}
)
with open(self.JSON_PATH, 'w') as json_write:
json.dump(lightwand_data, json_write, indent = 4)
Expand All @@ -1864,12 +1930,12 @@ def setLightMode(self, lightmode = 'None'):

elif self.current_toggle > self.toggle_lightbulb:
self.current_toggle -= self.fullround_toggle
sec = 1
sec = 0.1

while self.current_toggle < self.toggle_lightbulb:
self.ADapi.run_in(self.toggle_light, sec)
self.current_toggle += 1
sec += 1
sec += self.toggle_speed

else:
self.turn_off_lights()
Expand All @@ -1881,7 +1947,7 @@ def setLightMode(self, lightmode = 'None'):
lightwand_data = json.load(json_read)

lightwand_data[self.lights[0]].update(
{"toggle" : self.current_toggle}
{"toggle" : self.toggle_lightbulb}
)
with open(self.JSON_PATH, 'w') as json_write:
json.dump(lightwand_data, json_write, indent = 4)
Expand Down Expand Up @@ -1924,25 +1990,30 @@ def setMotion(self, lightmode = 'None'):

self.motion = True

if self.current_toggle == self.toggle_lightbulb:
return

elif self.current_toggle > self.toggle_lightbulb:
self.current_toggle -= self.fullround_toggle
sec = 1
if 'toggle' in self.motionlight[0]:
# Turns on light regardless of Lux and Conditions
toggle_bulb = self.motionlight[0]['toggle'] * 2 - 1

while self.current_toggle < self.toggle_lightbulb:
self.ADapi.run_in(self.toggle_light, sec)
self.current_toggle += 1
sec += 1
if self.current_toggle == toggle_bulb:
return

# Persistent toggle
if self.usePersistentStorage:
with open(self.JSON_PATH, 'r') as json_read:
lightwand_data = json.load(json_read)
elif self.current_toggle > toggle_bulb:
self.current_toggle -= self.fullround_toggle
sec = 0.1

lightwand_data[self.lights[0]].update(
{"toggle" : self.current_toggle}
)
with open(self.JSON_PATH, 'w') as json_write:
json.dump(lightwand_data, json_write, indent = 4)
while self.current_toggle < toggle_bulb:
self.ADapi.run_in(self.toggle_light, sec)
self.current_toggle += 1
sec += self.toggle_speed

# Persistent toggle
if self.usePersistentStorage:
with open(self.JSON_PATH, 'r') as json_read:
lightwand_data = json.load(json_read)

lightwand_data[self.lights[0]].update(
{"toggle" : self.current_toggle}
)
with open(self.JSON_PATH, 'w') as json_write:
json.dump(lightwand_data, json_write, indent = 4)
2 changes: 2 additions & 0 deletions info.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ your_room_name:
- switch.toggle_bulb
toggle: 2 # On toggles to get desired dim
num_dim_steps: 3 # Number of dim steps in bulb
toggle_speed: 0.8 # Set time in seconds between each toggle
light_modes:
- mode: night
toggle: 3
Expand Down Expand Up @@ -532,3 +533,4 @@ key | optional | type | default | introduced in | description
`room_lux_turn_on` | True | int | | v1.0.0 | Room lux to turn on light if below
`room_lux_turn_off` | True | int | | v1.0.0 | Room lux to turn off light if above
`conditions` | True | list | | v1.0.0 | Conditions as if statement. Inherits Appdaemon Api as ADapi
`toggle_speed` | True | float | 1 | v1.1.4 | Set time in seconds between each toggle. Supports sub second with 0.x

0 comments on commit c5614b2

Please sign in to comment.