1"""Smart tasks set timers to turn on/off lights in various ways. 2 3> Currently supporting wake up 4 5SmartTask # return top level info 6 TaskControl # Change top level values 7 StartAction # Get top level info on start action 8 StartActionItem # Get info on specific device in task 9 StartActionItemController # change values for task 10""" 11 12import datetime 13from datetime import datetime as dt 14 15from .command import Command 16from .const import ( 17 ATTR_DEVICE_STATE, 18 ATTR_ID, 19 ATTR_LIGHT_DIMMER, 20 ATTR_REPEAT_DAYS, 21 ATTR_SMART_TASK_LIGHTS_OFF, 22 ATTR_SMART_TASK_NOT_AT_HOME, 23 ATTR_SMART_TASK_TRIGGER_TIME_INTERVAL, 24 ATTR_SMART_TASK_TRIGGER_TIME_START_HOUR, 25 ATTR_SMART_TASK_TRIGGER_TIME_START_MIN, 26 ATTR_SMART_TASK_TYPE, 27 ATTR_SMART_TASK_WAKE_UP, 28 ATTR_START_ACTION, 29 ATTR_TRANSITION_TIME, 30 ROOT_SMART_TASKS, 31 ROOT_START_ACTION, 32) 33from .resource import ApiResource 34from .util import BitChoices 35 36WEEKDAYS = BitChoices( 37 ( 38 ("mon", "Monday"), 39 ("tue", "Tuesday"), 40 ("wed", "Wednesday"), 41 ("thu", "Thursday"), 42 ("fri", "Friday"), 43 ("sat", "Saturday"), 44 ("sun", "Sunday"), 45 ) 46) 47 48 49class SmartTask(ApiResource): 50 """Represent a smart task.""" 51 52 def __init__(self, gateway, raw): 53 """Initialize the class.""" 54 super().__init__(raw) 55 self._gateway = gateway 56 57 @property 58 def path(self): 59 """Return gateway path.""" 60 return [ROOT_SMART_TASKS, self.id] 61 62 @property 63 def state(self): 64 """Boolean representing the light state of the transition.""" 65 return self.raw.get(ATTR_DEVICE_STATE) == 1 66 67 @property 68 def task_type_id(self): 69 """Return type of task.""" 70 return self.raw.get(ATTR_SMART_TASK_TYPE) 71 72 @property 73 def task_type_name(self): 74 """Return the task type in plain text. 75 76 (Own interpretation of names.) 77 """ 78 if self.is_wake_up: 79 return "Wake Up" 80 if self.is_not_at_home: 81 return "Not At Home" 82 if self.is_lights_off: 83 return "Lights Off" 84 return None 85 86 @property 87 def is_wake_up(self): 88 """Boolean representing if this is a wake up task.""" 89 return self.raw.get(ATTR_SMART_TASK_TYPE) == ATTR_SMART_TASK_WAKE_UP 90 91 @property 92 def is_not_at_home(self): 93 """Boolean representing if this is a not home task.""" 94 return self.raw.get(ATTR_SMART_TASK_TYPE) == ATTR_SMART_TASK_NOT_AT_HOME 95 96 @property 97 def is_lights_off(self): 98 """Boolean representing if this is a lights off task.""" 99 return self.raw.get(ATTR_SMART_TASK_TYPE) == ATTR_SMART_TASK_LIGHTS_OFF 100 101 @property 102 def repeat_days(self): 103 """Return int (bit) for enabled weekdays.""" 104 return self.raw.get(ATTR_REPEAT_DAYS) 105 106 @property 107 def repeat_days_list(self): 108 """Binary representation of weekdays the event takes place.""" 109 return WEEKDAYS.get_selected_values(self.raw.get(ATTR_REPEAT_DAYS)) 110 111 @property 112 def task_start_parameters(self): 113 """Return hour and minute that task starts.""" 114 return self.raw.get(ATTR_SMART_TASK_TRIGGER_TIME_INTERVAL)[0] 115 116 @property 117 def task_start_time(self): 118 """Return the time the task starts. 119 120 Time is set according to iso8601. 121 """ 122 return datetime.time( 123 self.task_start_parameters[ATTR_SMART_TASK_TRIGGER_TIME_START_HOUR], 124 self.task_start_parameters[ATTR_SMART_TASK_TRIGGER_TIME_START_MIN], 125 ) 126 127 @property 128 def task_control(self): 129 """Control a task.""" 130 return TaskControl(self, self.state, self.path, self._gateway) 131 132 @property 133 def start_action(self): 134 """Return start action object.""" 135 return StartAction(self, self.path) 136 137 def __repr__(self): 138 """Return a readable name for smart task.""" 139 state = "on" if self.state else "off" 140 return f"<Task {self.id} - {self.task_type_name} - {state}>" 141 142 143class TaskControl: 144 """Class to control the tasks.""" 145 146 def __init__(self, task, state, path, gateway): 147 """Initialize TaskControl.""" 148 self._task = task 149 self.state = state 150 self.path = path 151 self._gateway = gateway 152 153 @property 154 def tasks(self): 155 """Return task objects of the task control.""" 156 return [ 157 StartActionItem(self._task, i, self.state, self.path, self.raw) 158 for i in range(len(self.raw)) 159 ] 160 161 def set_dimmer_start_time(self, hour, minute): 162 """Set start time for task (hh:mm) in iso8601. 163 164 NB: dimmer starts 30 mins before time in app 165 """ 166 # This is to calculate the difference between local time 167 # and the time in the gateway 168 d_now = self._gateway.get_gateway_info().current_time 169 d_utcnow = dt.utcnow() 170 diff = d_now - d_utcnow 171 newtime = dt(100, 1, 1, hour, minute, 00) - diff 172 173 command = { 174 ATTR_SMART_TASK_TRIGGER_TIME_INTERVAL: [ 175 { 176 ATTR_SMART_TASK_TRIGGER_TIME_START_HOUR: newtime.hour, 177 ATTR_SMART_TASK_TRIGGER_TIME_START_MIN: newtime.minute, 178 } 179 ] 180 } 181 return self._task.set_values(command) 182 183 @property 184 def raw(self): 185 """Return raw data that it represents.""" 186 return self._task.raw[ATTR_START_ACTION] 187 188 189class StartAction: 190 """Class to control the start action-node.""" 191 192 def __init__(self, start_action, path): 193 """Initialize StartAction class.""" 194 self.start_action = start_action 195 self.path = path 196 197 @property 198 def state(self): 199 """Return state of start action task.""" 200 return self.raw.get(ATTR_DEVICE_STATE) 201 202 @property 203 def devices(self): 204 """Return state of start action task.""" 205 return [ 206 StartActionItem(self.start_action, i, self.state, self.path, self.raw) 207 for i in range(len(self.raw[ROOT_START_ACTION])) 208 ] 209 210 @property 211 def raw(self): 212 """Return raw data that it represents.""" 213 return self.start_action.raw[ATTR_START_ACTION] 214 215 216class StartActionItem: 217 """Class to show settings for a task.""" 218 219 def __init__(self, task, index, state, path, raw): 220 """Initialize TaskInfo.""" 221 self.task = task 222 self.index = index 223 self.state = state 224 self.path = path 225 self._raw = raw 226 227 @property 228 def devices_dict(self): 229 """Return state of start action task.""" 230 json_list = {} 231 index = 0 232 for x_entry in self._raw[ROOT_START_ACTION]: 233 if index != self.index: 234 json_list.update(x_entry) 235 index = index + 1 236 return json_list 237 238 @property 239 def id(self): 240 """Return ID (device id) of task.""" 241 return self.raw.get(ATTR_ID) 242 243 @property 244 def item_controller(self): 245 """Control a task.""" 246 return StartActionItemController( 247 self, self.raw, self.state, self.path, self.devices_dict 248 ) 249 250 @property 251 def transition_time(self): 252 """Transition runs for this long from the time in task_start. 253 254 Value is in seconds x 10. Default to 0 if transition is missing. 255 """ 256 return self.raw.get(ATTR_TRANSITION_TIME, 0) / 60 / 10 257 258 @property 259 def dimmer(self): 260 """Return dimmer level.""" 261 return self.raw.get(ATTR_LIGHT_DIMMER) 262 263 @property 264 def raw(self): 265 """Return raw data that it represents.""" 266 return self._raw[ROOT_START_ACTION][self.index] 267 268 def __repr__(self): 269 """Return a readable name for this class.""" 270 return f"<StartActionItem (Device: {self.id} - Dimmer: {self.dimmer} - Time: {self.transition_time})>" 271 272 273class StartActionItemController: 274 """Class to edit settings for a task.""" 275 276 def __init__(self, item, raw, state, path, devices_dict): 277 """Initialize TaskControl.""" 278 self._item = item 279 self.raw = raw 280 self.state = state 281 self.path = path 282 self.devices_dict = devices_dict 283 284 def set_dimmer(self, dimmer): 285 """Set final dimmer value for task.""" 286 command = { 287 ATTR_START_ACTION: { 288 ATTR_DEVICE_STATE: self.state, 289 ROOT_START_ACTION: [ 290 { 291 ATTR_ID: self.raw[ATTR_ID], 292 ATTR_LIGHT_DIMMER: dimmer, 293 ATTR_TRANSITION_TIME: self.raw.get(ATTR_TRANSITION_TIME, 0), 294 }, 295 self.devices_dict, 296 ], 297 } 298 } 299 return self.set_values(command) 300 301 def set_transition_time(self, transition_time): 302 """Set time (mins) for light transition.""" 303 command = { 304 ATTR_START_ACTION: { 305 ATTR_DEVICE_STATE: self.state, 306 ROOT_START_ACTION: [ 307 { 308 ATTR_ID: self.raw[ATTR_ID], 309 ATTR_LIGHT_DIMMER: self.raw[ATTR_LIGHT_DIMMER], 310 ATTR_TRANSITION_TIME: transition_time * 10 * 60, 311 }, 312 self.devices_dict, 313 ], 314 } 315 } 316 return self.set_values(command) 317 318 def set_values(self, command): 319 """ 320 Set values on task control. 321 322 Returns a Command. 323 """ 324 return Command("put", self._item.path, command) 325