github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/plugins/triggers/time_trigger.go (about) 1 // This file is part of the Smart Home 2 // Program complex distribution https://github.com/e154/smart-home 3 // Copyright (C) 2016-2023, Filippov Alex 4 // 5 // This library is free software: you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 3 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Library General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library. If not, see 17 // <https://www.gnu.org/licenses/>. 18 19 package triggers 20 21 import ( 22 "fmt" 23 "reflect" 24 "sync" 25 "time" 26 27 "github.com/e154/smart-home/system/bus" 28 "github.com/e154/smart-home/system/scheduler" 29 ) 30 31 const ( 32 // TimeName ... 33 TimeName = "time" 34 // TimeFunctionName ... 35 TimeFunctionName = "automationTriggerTime" 36 // TimeQueueSize ... 37 TimeQueueSize = 10 38 ) 39 40 var _ ITrigger = (*TimeTrigger)(nil) 41 42 type subscribe struct { 43 callback reflect.Value 44 entryID scheduler.EntryID 45 } 46 47 // TimeTrigger ... 48 type TimeTrigger struct { 49 baseTrigger 50 scheduler *scheduler.Scheduler 51 sync.Mutex 52 subscribers map[string][]*subscribe 53 } 54 55 // NewTimeTrigger ... 56 func NewTimeTrigger(eventBus bus.Bus, 57 scheduler *scheduler.Scheduler) ITrigger { 58 59 return &TimeTrigger{ 60 scheduler: scheduler, 61 subscribers: make(map[string][]*subscribe), 62 baseTrigger: baseTrigger{ 63 eventBus: eventBus, 64 msgQueue: bus.NewBus(), 65 functionName: TimeFunctionName, 66 name: TimeName, 67 }, 68 } 69 } 70 71 // AsyncAttach ... 72 func (t *TimeTrigger) AsyncAttach(wg *sync.WaitGroup) { 73 74 wg.Done() 75 } 76 77 // Subscribe ... 78 func (t *TimeTrigger) Subscribe(options Subscriber) error { 79 if options.Payload == nil { 80 return fmt.Errorf("payload is nil") 81 } 82 if _, ok := options.Payload[CronOptionTrigger]; !ok { 83 return fmt.Errorf("cron attribute is nil") 84 } 85 schedule := options.Payload[CronOptionTrigger].String() 86 if schedule == "" { 87 return fmt.Errorf("error static cast to string %v", options.Payload) 88 } 89 callback := reflect.ValueOf(options.Handler) 90 entryID, err := t.scheduler.AddFunc(schedule, func() { 91 callback.Call([]reflect.Value{reflect.ValueOf(""), reflect.ValueOf(time.Now())}) 92 }) 93 94 if err != nil { 95 return err 96 } 97 98 sub := &subscribe{ 99 callback: callback, 100 entryID: entryID, 101 } 102 t.Lock() 103 t.subscribers[schedule] = append(t.subscribers[schedule], sub) 104 t.Unlock() 105 106 return nil 107 } 108 109 // Unsubscribe ... 110 func (t *TimeTrigger) Unsubscribe(options Subscriber) error { 111 if options.Payload == nil { 112 return fmt.Errorf("payload is nil") 113 } 114 schedule := options.Payload[CronOptionTrigger].String() 115 if schedule == "" { 116 return fmt.Errorf("error static cast to string %v", options.Payload) 117 } 118 rv := reflect.ValueOf(options.Handler) 119 120 t.Lock() 121 defer t.Unlock() 122 123 var indexesToDelete []int 124 125 for i, sub := range t.subscribers[schedule] { 126 if sub.callback == rv || sub.callback.Pointer() == rv.Pointer() { 127 indexesToDelete = append(indexesToDelete, i) 128 } 129 } 130 131 for i := len(indexesToDelete) - 1; i >= 0; i-- { 132 index := indexesToDelete[i] 133 t.scheduler.Remove(t.subscribers[schedule][index].entryID) 134 t.subscribers[schedule] = append(t.subscribers[schedule][:index], t.subscribers[schedule][index+1:]...) 135 } 136 137 return nil 138 }