def run(self): TimerHB.Entry('Start resettable: {}'.format(self.name)) while True: while self.interval == 0: self.changingevent.wait() # there is not event time set self.eventtopost = self.newevent self.interval = self.newdelta self.changingevent.clear( ) # new values copied up so assuming non-zero interval should proceed to wait in next statement self.changedone.set() while not self.changingevent.wait( self.interval): # enter while loop if interval ends TimerHB.Entry('Post resettable: {}'.format(self.eventtopost)) PostEvent(self.eventtopost) #print('loop exit changing event') # get here if changingevent got set - either new values ready or canceling timer if self.finished.is_set(): #print('finishing') break # shutting down requires cancel to set first finished then changing to insure this is set here self.eventtopost = self.newevent #print('Newdelta: {}'.format(self.newdelta)) self.interval = self.newdelta self.changingevent.clear() self.changedone.set() # otherwise back to waiting for a non-zero interval to set del TimerList[self.name] TimerHB.Entry('Exit resettable: {}'.format(self.name))
def VarChanged(storeitem, old, new, param, modifier): debug.debugPrint('DaemonCtl', 'Var changed ', storeitem.name, ' from ', old, ' to ', new) # noinspection PyArgumentList if old != new: PostEvent( ConsoleEvent(CEvent.ISYVar, hub='AlertTasksVarChange', alert=param))
def Update(self, **ns): oldst = self.state if 'attributes' in ns: self.attributes = ns['attributes'] self.state = ns['state'] newst = _NormalizeState(self.state) if newst != self.internalstate: logsupport.Logs.Log("Mediaplayer state change: ", self.Hub.Entities[self.entity_id].name, ' was ', self.internalstate, ' now ', newst, '(', self.state, ')', severity=ConsoleDetail) self.internalstate = newst if self.Sonos: if self.internalstate == -1: # unavailable logsupport.Logs.Log("Sonos room went unavailable: ", self.Hub.Entities[self.entity_id].name) return else: if oldst == -1: logsupport.Logs.Log("Sonos room became available: ", self.Hub.Entities[self.entity_id].name) self.sonos_group = self.attributes['sonos_group'] if 'source_list' in self.attributes: self.source_list = self.attributes['source_list'] self.muted = self.attributes[ 'is_volume_muted'] if 'is_volume_muted' in self.attributes else 'True' self.volume = self.attributes[ 'volume_level'] if 'volume_level' in self.attributes else 0 self.song = self.attributes[ 'media_title'] if 'media_title' in self.attributes else '' self.artist = self.attributes[ 'media_artist'] if 'media_artist' in self.attributes else '' self.album = self.attributes[ 'media_album_name'] if 'media_album_name' in self.attributes else '' if screens.DS.AS is not None: if self.Hub.name in screens.DS.AS.HubInterestList: if self.entity_id in screens.DS.AS.HubInterestList[ self.Hub.name]: debug.debugPrint( 'DaemonCtl', time.time() - config.sysStore.ConsoleStartTime, "HA reports node change(screen): ", "Key: ", self.Hub.Entities[self.entity_id].name) # noinspection PyArgumentList PostEvent( ConsoleEvent(CEvent.HubNodeChange, hub=self.Hub.name, node=self.entity_id, value=self.internalstate))
def touchhandler(event, touch): global evntcnt evntcnt += 1 slot = touch.slot if slot != 0: return # no multitouch events for now p = (touch.x, touch.y) if event == TS_PRESS: debug.debugPrint('Touch', 'Press pos: {} seq: {}'.format(p, evntcnt)) PostEvent(ConsoleEvent(CEvent.MouseDown, pos=p, seq=evntcnt)) # eventfix elif event == TS_RELEASE: debug.debugPrint('Touch', 'Repease pos: {} seq: {}'.format(p, evntcnt)) PostEvent(ConsoleEvent(CEvent.MouseUp, pos=p, seq=evntcnt)) elif event == TS_MOVE: debug.debugPrint('Touch', 'Motion pos: {} seq: {}'.format(p, evntcnt)) PostEvent(ConsoleEvent(CEvent.MouseMotion, pos=p, seq=evntcnt))
def run(self): TimerHB.Entry('Start once: {}'.format(self.name)) self.kwargs['TargetTime'] = time.time() + self.interval self.finished.wait(self.interval) if not self.finished.is_set(): TimerHB.Entry('Post once: {} diff: {} args: {}'.format( self.name, time.time() - self.kwargs['TargetTime'], self.kwargs)) PostEvent(ConsoleEvent(CEvent.SchedEvent, **self.kwargs)) self.finished.set() del TimerList[self.name] TimerHB.Entry('Exit once: {}'.format(self.name))
def run(self): TimerHB.Entry('Start counter: {}'.format(self.name)) targettime = time.time() + self.interval while not self.finished.wait(self.interval) and self.count > 0: self.kwargs['TargetTime'] = targettime self.kwargs['count'] = self.count self.count -= 1 TimerHB.Entry('Post counter: {} diff: {} args: {}'.format( self.name, time.time() - targettime, self.kwargs)) PostEvent(ConsoleEvent(CEvent.SchedEvent, **self.kwargs)) targettime += self.interval del TimerList[self.name] TimerHB.Entry('Exit counter: {}'.format(self.name))
def NoEventInjector(): logsupport.Logs.Log('Starting watchdog activity injector') while config.Running: # noinspection PyBroadException try: now = time.time() logsupport.Logs.Log('Inject: {}'.format(now), severity=logsupport.ConsoleDetail) #logsupport.DevPrint('Inject: {}'.format(now)) PostEvent(ConsoleEvent(CEvent.FailSafePing, inject=now)) time.sleep(FailsafeInterval / 2) except Exception as E: time.sleep(FailsafeInterval / 2) logsupport.DevPrint('Inject Exception {}'.format(repr(E))) # spurious exceptions during shutdown logsupport.DevPrint('Injector exiting')
def run(self): TimerHB.Entry('Start repeater: {}'.format(self.name)) targettime = time.time() + self.interval while not self.finished.wait(self.interval): if not self.finished.is_set(): if self.running.is_set(): self.kwargs['TargetTime'] = targettime diff = time.time() - targettime self.cumulativeslip += diff TimerHB.Entry( 'Post repeater: {} diff: {} cumm: {} args: {}'.format( self.name, diff, self.cumulativeslip, self.kwargs)) tt = ConsoleEvent(CEvent.SchedEvent, **self.kwargs) PostEvent(tt) targettime = time.time( ) + self.interval # don't accumulate errors else: self.running.wait() targettime = time.time() + self.interval del TimerList[self.name] TimerHB.Entry('Exit repeater: {}'.format(self.name))
def KeyWithVarChanged(storeitem, old, new, param, modifier): debug.debugPrint('DaemonCtl', 'Var changed for key ', storeitem.name, ' from ', old, ' to ', new) # noinspection PyArgumentList PostEvent(ConsoleEvent(CEvent.HubNodeChange, hub='*VARSTORE*', varinfo=param))
def MainControlLoop(self, InitScreen): TimerName = 0 config.sysStore.ErrorNotice = -1 # don't pester for errors during startup threadmanager.StartThreads() config.sysStore.LogStartTime = time.time( ) # MQTT will start tracking other console errors now # so we can start broadcasting our errors logsupport.LocalOnly = False self.ScreensDict = screens.SecondaryDict.copy() self.ScreensDict.update(screens.MainDict) for a in alerttasks.AlertItems.AlertsList.values(): a.state = 'Armed' logsupport.Logs.Log("Arming " + a.type + " alert " + a.name) logsupport.Logs.Log("->" + str(a), severity=ConsoleDetail) if a.type == 'Periodic': alerttasks.SchedulePeriodicEvent(a) elif a.type == 'NodeChange': a.trigger.node.Hub.SetAlertWatch(a.trigger.node, a) if a.trigger.IsTrue(): # noinspection PyArgumentList PostEvent( ConsoleEvent(CEvent.ISYAlert, hub='DS-NodeChange', alert=a)) elif a.type == 'VarChange': a.state = 'Init' # Note: VarChange alerts don't need setup because the store has an alert proc pass elif a.type == 'Init': a.Invoke() else: logsupport.Logs.Log("Internal error - unknown alert type: ", a.type, ' for ', a.name, severity=ConsoleError, tb=False) logsupport.Logs.livelog = False # turn off logging to the screen with open("{}/.ConsoleStart".format(config.sysStore.HomeDir), "a") as f: f.write(str(time.time()) + '\n') if config.Running: # allow for a very early restart request from things like autoversion self.SwitchScreen(InitScreen, 'Bright', 'Startup', newstate='Home') statusperiod = time.time() prevstatus = '' if config.sysStore.versionname in ('development'): TempThdList = threading.Thread(target=failsafe.TempThreadList, name='ThreadLister') TempThdList.daemon = True TempThdList.start() Injector = threading.Thread(target=failsafe.NoEventInjector, name='Injector') Injector.daemon = True Injector.start() Failsafe = multiprocessing.Process(target=failsafe.MasterWatchDog, name='Failsafe') Failsafe.daemon = True Failsafe.start() config.sysStore.SetVal('Watchdog_pid', Failsafe.pid) #if config.sysStore.versionname in ('development', 'homerelease'): topper.inittop() logsupport.Logs.Log('Starting master watchdog {} for {}'.format( config.sysStore.Watchdog_pid, config.sysStore.Console_pid)) event = None pcslist = '' for pcs in ('Console', 'Watchdog', 'AsyncLogger', 'Topper'): try: if config.sysStore.GetVal(pcs + '_pid') != 0: pcslist = pcslist + '{}: {} '.format( pcs, config.sysStore.GetVal(pcs + '_pid')) except: pass logsupport.Logs.Log('Console Up: {}'.format(pcslist)) perfdump = time.time() ckperf = time.time() dayord = time.localtime().tm_yday try: while config.Running: # Operational Control Loop logsupport.maincyclecnt += 1 if logsupport.maincyclecnt == 4: logsupport.NewDay(Report=False) # ignore startup delays if dayord != time.localtime().tm_yday: dayord = time.localtime().tm_yday logsupport.NewDay(Report=True) self.HBEvents.Entry('Start event loop iteration') StackCheck = traceback.format_stack() if len(StackCheck) != 4 and config.sysStore.versionname in ( 'development', 'homerelease'): logsupport.Logs.Log('Stack growth error', severity=ConsoleWarning, hb=True) for L in StackCheck: logsupport.Logs.Log(L.strip()) if time.time() - ckperf > 900: # todo 900: ckperf = time.time() if config.sysStore.versionname in ( 'development', 'homerelease') and (logsupport.queuedepthmax > 4 or logsupport.queuetimemax > 1): logsupport.Logs.Log( 'Console performance({}): maxq: {} maxwait: {}'. format(time.time() - perfdump, logsupport.queuedepthmax, logsupport.queuetimemax), severity=ConsoleWarning, hb=True, localonly=True) logsupport.queuetimemax = 0 logsupport.queuedepthmax = 0 perfdump = time.time() if not Failsafe.is_alive(): logsupport.DevPrint('Watchdog died') logsupport.Logs.Log('Watchdog died - restarting console', severity=ConsoleError, hb=True) config.terminationreason = 'watchdog died' exitutils.Exit(exitutils.ERRORRESTART) failsafe.KeepAlive.set() nowtime = time.time() if statusperiod <= nowtime or prevstatus != config.sysStore.consolestatus: ReportStatus(config.sysStore.consolestatus) prevstatus = config.sysStore.consolestatus statusperiod = nowtime + 60 if not threadmanager.Watcher.is_alive(): logsupport.Logs.Log("Threadmanager Failure", severity=ConsoleError, tb=False) config.terminationreason = 'watcher died' exitutils.Exit(exitutils.ERRORRESTART) logsupport.LoggerQueue.put( (logsupport.Command.Touch, "{}/.ConsoleStart".format(config.sysStore.HomeDir))) if debug.dbgStore.GetVal('StatesDump'): debug.dbgStore.SetVal('StatesDump', False) for h, hub in hubs.hubs.Hubs.items(): print('States dump for hub: ', h) hub.StatesDump() debug.dbgStore.SetVal('StatesDump', False) if self.Deferrals: # an event was deferred mid screen touches - handle now event = self.Deferrals.pop(0) self.HBEvents.Entry('Got deferred event: {} {}'.format( time.time(), repr(event))) debug.debugPrint('EventList', 'Deferred Event Pop', event) elif debug.dbgStore.GetVal('QDump'): # todo QDump with new event mechanism '''if events: debug.debugPrint('QDump', 'Time: ', time.time()) for e in events: self.Deferrals.append(e) debug.debugPrint('QDump', e, e.type) else: debug.debugPrint('QDump', "Empty queue") time.sleep(0.01) event = pygame.event.Event(NOEVENT, dict={'inject':time.time(),'defer':True}) #eventfix ''' pass else: needvalidevent = True while needvalidevent: event = GetEvent() self.HBEvents.Entry('Got event: {} {}'.format( time.time(), repr(event))) if event.type == CEvent.ACTIVITYTIMER: if event.seq == self.activityseq: needvalidevent = False else: if config.sysStore.versionname == 'development': logsupport.Logs.Log( 'Outdated activity {} {}'.format( event.seq, self.activityseq)) self.HBEvents.Entry( 'outdated activity {} {}'.format( event.seq, self.activityseq)) logsupport.DevPrint( 'outdated activity {} {}'.format( event.seq, self.activityseq)) else: needvalidevent = False self.HBEvents.Entry('Process at {} {}'.format( time.time(), repr(event))) postwaittime = time.time() if event.type == CEvent.FailSafePing: self.HBEvents.Entry( 'Saw NOEVENT {} after injection at {}'.format( time.time() - event.inject, event.inject)) pass # these appear to make sure loop is running elif event.type == CEvent.MouseDown: # pygame.MOUSEBUTTONDOWN: self.HBEvents.Entry('MouseDown' + str(event.pos)) debug.debugPrint( 'Touch', 'MouseDown' + str(event.pos) + repr(event)) # screen touch events; this includes touches to non-sensitive area of screen self.SetActivityTimer(self.AS.DimTO, 'Screen touch') # refresh non-dimming in all cases including non=sensitive areas # this refresh is redundant in some cases where the touch causes other activities if self.dim == 'Dim': # wake up the screen and if in a cover state go home config.sysStore.consolestatus = 'active' if self.state == 'Cover': self.SwitchScreen(screens.HomeScreen, 'Bright', 'Wake up from cover', newstate='Home') else: self.Brighten( ) # if any other screen just brighten continue # wakeup touches are otherwise ignored # Screen was not Dim so the touch was meaningful pos = event.pos tapcount = 1 pygame.time.delay(config.sysStore.MultiTapTime) while True: eventx = GetEventNoWait() if eventx is None: break elif eventx.type == CEvent.MouseDown: self.HBEvents.Entry('Follow MouseDown: {}'.format( repr(eventx))) debug.debugPrint( 'Touch', 'Follow MouseDown' + str(event.pos) + repr(event)) tapcount += 1 pygame.time.delay(config.sysStore.MultiTapTime ) # todo make general time call? else: if eventx.type in (CEvent.MouseUp, CEvent.MouseMotion): debug.debugPrint( 'Touch', 'Other event: {}'.format(repr(eventx))) self.HBEvents.Entry('Mouse Other: {}'.format( repr(eventx))) else: self.HBEvents.Entry('Defer' + repr(eventx)) self.Deferrals.append( eventx ) # defer the event until after the clicks are sorted out # Future add handling for hold here with checking for MOUSE UP etc. if tapcount == 3: # Switch screen chains if screens.HomeScreen != screens.HomeScreen2: # only do if there is a real secondary chain if self.Chain == 0: self.Chain = 1 self.SwitchScreen(screens.HomeScreen2, 'Bright', 'Chain switch to secondary', newstate='NonHome') else: self.Chain = 0 self.SwitchScreen(screens.HomeScreen, 'Bright', 'Chain switch to main', newstate='Home') continue elif tapcount > 3: # Go to maintenance # timers.StartLongOp( # 'maintenance') # todo a bit ugly - start long op here but end in gohome in maint screen self.SwitchScreen(maintscreen.MaintScreen, 'Bright', 'Tap to maintenance', newstate='Maint') continue if self.AS.Keys is not None: for K in self.AS.Keys.values(): if K.touched(pos): K.Pressed(tapcount) for K in self.AS.NavKeys.values(): if K.touched(pos): K.Proc() # todo make a goto key elif event.type in (CEvent.MouseUp, CEvent.MouseMotion): debug.debugPrint('Touch', 'Other mouse event {}'.format(event)) # ignore for now - handle more complex gestures here if ever needed elif event.type == CEvent.ACTIVITYTIMER: # ACTIVITYTIMER: debug.debugPrint('Dispatch', 'Activity timer fired State=', self.state, '/', self.dim) if self.dim == 'Bright': self.HBEvents.Entry( 'ActivityTimer(Bright) state: {}'.format( self.state)) config.sysStore.consolestatus = 'idle' self.Dim() self.SetActivityTimer(self.AS.PersistTO, 'Go dim and wait persist') else: self.HBEvents.Entry( 'ActivityTimer(non-Bright) state: {}'.format( self.state)) if self.state == 'NonHome': self.SwitchScreen(screens.HomeScreen, 'Dim', 'Dim nonhome to dim home', newstate='Home', clear=True) elif self.state == 'Home': self.SwitchScreen(screens.DimIdleList[0], 'Dim', 'Go to cover', newstate='Cover', AsCover=True, clear=True) # rotate covers - save even if only 1 cover screens.DimIdleList = screens.DimIdleList[1:] + [ screens.DimIdleList[0] ] screens.DimIdleTimes = screens.DimIdleTimes[1:] + [ screens.DimIdleTimes[0] ] elif self.state == 'Cover': if len(screens.DimIdleList) > 1: self.SwitchScreen(screens.DimIdleList[0], 'Dim', 'Go to next cover', newstate='Cover', AsCover=True, clear=True) screens.DimIdleList = screens.DimIdleList[ 1:] + [screens.DimIdleList[0]] screens.DimIdleTimes = screens.DimIdleTimes[ 1:] + [screens.DimIdleTimes[0]] else: # Maint or Alert - just ignore the activity action # logsupport.Logs.Log('Activity timer fired while in state: {}'.format(self.state),severity=ConsoleWarning) debug.debugPrint('Dispatch', 'TO while in: ', self.state) elif event.type == CEvent.GeneralRepaint: self.HBEvents.Entry('General Repaint: {}'.format( repr(event))) debug.debugPrint('Dispatch', 'General Repaint Event', event) self.AS.ReInitDisplay() elif event.type == CEvent.HubNodeChange: self.HBEvents.Entry('Hub Change: {}'.format(repr(event))) debug.debugPrint('Dispatch', 'Hub Change Event', event) if hasattr(event, 'node'): self.AS.NodeEvent(hub=event.hub, node=event.node, value=event.value) elif hasattr(event, 'varinfo'): self.AS.NodeEvent(hub=event.hub, varinfo=event.varinfo) else: debug.debugPrint('Dispatch', 'Bad Node Change Event: ', event) logsupport.Logs.Log('Bad Node Change Event ', event, severity=ConsoleWarning) elif event.type in (CEvent.ISYVar, CEvent.ISYAlert): self.HBEvents.Entry('Var or Alert' + repr(event)) evtype = 'variable' if event.type == CEvent.ISYVar else 'node' debug.debugPrint('Dispatch', 'ISY ', evtype, ' change', event) alert = event.alert if alert.state in ('Armed', 'Init'): if alert.trigger.IsTrue(): # alert condition holds if alert.trigger.delay != 0: # delay invocation alert.state = 'Delayed' debug.debugPrint('Dispatch', "Post with delay:", alert.name, alert.trigger.delay) TimerName += 1 alert.timer = timers.OnceTimer( alert.trigger.delay, start=True, name='MainLoop' + str(TimerName), proc=alerttasks.HandleDeferredAlert, param=alert) else: # invoke now alert.state = 'FiredNoDelay' debug.debugPrint('Dispatch', "Invoke: ", alert.name) alert.Invoke( ) # either calls a proc or enters a screen and adjusts alert state appropriately else: if alert.state == 'Armed': # condition cleared after alert rearmed - timing in the queue? logsupport.Logs.Log( 'Anomolous Trigger clearing while armed: ', repr(alert), severity=ConsoleDetail, hb=True) else: alert.state = 'Armed' logsupport.Logs.Log( 'Initial var value for trigger is benign: ', repr(alert), severity=ConsoleDetail) elif alert.state == 'Active' and not alert.trigger.IsTrue( ): # alert condition has cleared and screen is up debug.debugPrint('Dispatch', 'Active alert cleared', alert.name) alert.state = 'Armed' # just rearm the alert self.SwitchScreen(screens.HomeScreen, 'Dim', 'Cleared alert', newstate='Home') elif ((alert.state == 'Delayed') or (alert.state == 'Deferred')) and not alert.trigger.IsTrue(): # condition changed under a pending action (screen or proc) so just cancel and rearm if alert.timer is not None: alert.timer.cancel() alert.timer = None else: logsupport.DevPrint( 'Clear with no timer?? {}'.format(repr(alert))) debug.debugPrint( 'Dispatch', 'Delayed event cleared before invoke', alert.name) alert.state = 'Armed' # todo - verify this is correct. Issue is that the alert might have gotten here from a delay or from the # alert screen deferring. The screen uses it's own id for this alert to might be either. Probably should # distinguish based on if delay or defer but doing both should be same id(alert.actiontarget)) originally this was id-alert for some # reason I changed it to id-actiontarget don't know why but it was done while adding HASS this screwed up clearing deferred alerts # so switched it back in hopes to remember why the change todo else: logsupport.Logs.Log( "Anomolous change situation State: ", alert.state, " Alert: ", repr(alert), " Trigger IsTue: ", alert.trigger.IsTrue(), severity=ConsoleWarning, hb=True) debug.debugPrint('Dispatch', 'ISYVar/ISYAlert passing: ', alert.state, alert.trigger.IsTrue(), event, alert) # Armed and false: irrelevant report # Active and true: extaneous report # Delayed or deferred and true: redundant report elif event.type == CEvent.SchedEvent: self.HBEvents.Entry('Sched event {}'.format(repr(event))) eventnow = time.time() diff = eventnow - event.TargetTime if abs(diff) > controlevents.latencynotification: logsupport.Logs.Log( 'Timer late by {} seconds. Event: {}'.format( diff, repr(event)), severity=ConsoleWarning, hb=True, localonly=True, homeonly=True) self.HBEvents.Entry( 'Event late by {} target: {} now: {}'.format( diff, event.TargetTime, eventnow)) event.proc(event) elif event.type == CEvent.RunProc: self.HBEvents.Entry('Run procedure {}'.format(event.name)) event.proc() else: logsupport.Logs.Log("Unknown main event {}".format( repr(event)), severity=ConsoleError, hb=True, tb=False) if time.time( ) - postwaittime > controlevents.latencynotification and not timers.LongOpInProgress: # this loop took a long time logsupport.Logs.Log( "Slow loop at {} took {} for {}".format( time.time(), time.time() - postwaittime, event), severity=ConsoleWarning, hb=True, localonly=True, homeonly=True) self.HBEvents.Entry( 'End Event Loop took: {}'.format(time.time() - postwaittime)) except Exception as E: logsupport.Logs.Log('Main display loop had exception: {}'.format( repr(E))) traceback.print_exc() config.ecode = exitutils.ERRORRESTART print('Display Screen Exception: {}'.format(repr(E))) logsupport.Logs.Log('Main GUI loop exiting')
def DoDelayedRestart(evnt): PostEvent(ConsoleEvent(CEvent.RunProc, name='DelayedRestart', proc=DoRestart))
def on_message(client, userdata, msg): # command to force get: mosquitto_pub -t consoles/all/cmd -m getstable; mosquitto_pub -t consoles/all/cmd -m restart loopstart = time.time() var = [] for t, item in userdata.topicindex.items(): if t == msg.topic: var.extend(item) if msg.topic in ('consoles/all/cmd', 'consoles/' + hw.hostname + '/cmd'): cmd = msg.payload.decode('ascii') logsupport.Logs.Log('{}: Remote command received on {}: {}'.format(self.name, msg.topic, cmd)) cmdcalls = {'restart': DoRestart, 'getstable': GetStable, 'getbeta': GetBeta, 'usestable': UseStable, 'usebeta': UseBeta, 'hbdump': DumpHB, 'status': EchoStat, 'issueerror': functools.partial(LogItem, ConsoleError), 'issuewarning': functools.partial(LogItem, ConsoleWarning), 'issueinfo': functools.partial(LogItem, ConsoleInfo)} if cmd.lower() in cmdcalls: try: PostEvent(ConsoleEvent(CEvent.RunProc, name=cmd, proc=cmdcalls[cmd.lower()])) except Exception as E: logsupport.Logs.Log('Exc: {}'.format(repr(E))) else: logsupport.Logs.Log('{}: Unknown remote command request: {}'.format(self.name, cmd), severity=ConsoleWarning) return elif msg.topic == 'consoles/all/errors': d = json.loads(msg.payload.decode('ascii')) if d['node'] != hw.hostname: logsupport.Logs.LogRemote(d['node'], d['entry'], severity=d['sev'], etime=d['etime'] if 'etime' in d else 0) return elif msg.topic in ('consoles/all/set', 'consoles/' + hw.hostname + '/set'): d = json.loads(msg.payload.decode('ascii')) try: logsupport.Logs.Log('{}: set {} = {}'.format(self.name, d['name'], d['value'])) valuestore.SetVal(d['name'], d['value']) except Exception as E: logsupport.Logs.Log('Bad set via MQTT: {} Exc: {}'.format(repr(d), E), severity=ConsoleWarning) return # noinspection PySimplifyBooleanCheck if var == []: logsupport.Logs.Log('Unknown topic ', msg.topic, ' from broker ', self.name, severity=ConsoleWarning) else: for v in var: v.SetTime = time.time() if not v.jsonflds: v.Value = v.Type(msg.payload) # debug.debugPrint('StoreTrack', "Store(mqtt): ", self.name, ':', v, ' Value: ', v.Value) else: payload = '*bad json*' + msg.payload.decode('ascii') # for exception log below try: payload = json.loads(msg.payload.decode('ascii').replace('nan', 'null')) # work around bug in tasmota returning bad json for i in v.jsonflds: payload = payload[i] if payload is not None: v.Value = v.Type(payload) else: v.Value = None # debug.debugPrint('StoreTrack', "Store(mqtt): ", self.name, ':', v, ' Value: ', v.Value) except Exception as e: logsupport.Logs.Log('Error handling json MQTT item: ', v.name, str(v.jsonflds), msg.payload.decode('ascii'), str(e), repr(payload), severity=ConsoleWarning) loopend = time.time() self.HB.Entry('Processing time: {} Done: {}'.format(loopend - loopstart, repr(msg))) time.sleep(.1) # force thread to give up processor to allow response to time events