Closed NickWaterton closed 2 years ago
This is my workaround for missing is_running
in my createTimer
class;
class createTimer(HABApp.Rule):
'''
General timer class
Restartable Timer, with cancel and reschedule functions
accepts float as seconds parameter (not claiming that the timer is that accurate though)
'''
def __init__(self, seconds=0, exec_func=None, *args, **kwargs):
super().__init__()
self.log = logging.getLogger('MyRule.'+self.__class__.__name__)
self.t = seconds
self.exec_func = exec_func
run = kwargs.pop('run', False)
self.args = args
self.kwargs = kwargs
if self.exec_func is None:
self.exec_func = self.dummy
self._task = self.run.at(self.get_milliseconds, self.exec_func, *args, **kwargs)
if not run:
self.cancel()
@property
def get_milliseconds(self):
return datetime.timedelta(milliseconds=int(self.t*1000))
@property
def is_running(self):
return bool(self._task.get_next_run() >= datetime.datetime.now())
#was
return not self._task.is_finished
@property
def time_till_run(self):
if self._task.get_next_run() > datetime.datetime.now():
return (self._task.get_next_run() - datetime.datetime.now()).total_seconds()
return 0
#was
if not self._task.is_finished:
return (self._task.get_next_run() - datetime.datetime.now()).total_seconds()
return 0
def dummy(self):
self.log.warning('This timer has no exec_func set')
def reschedule(self, seconds=None):
self.start(seconds)
def start(self, seconds=None, *args, **kwargs):
if seconds is not None:
self.t = seconds
if self.t >= 0:
self.cancel()
if not args:
args = self.args
if not kwargs:
kwargs = self.kwargs
self._task = self.run.at(self.get_milliseconds, self.exec_func, *args, **kwargs)
def cancel(self):
try:
self._task.cancel()
except Exception:
pass
I'm hoping this will work.
First sorry for the inconvenience! I changed so many things some have slipped the change log.
The first issue is that HABApp now requires obsolete versions of packages, and if you do not have the exact version required, will not start. Had to downgrade many of my packages, just to get it to run.
HABApp will start fine with the wrong package version, but if you do a pip install HABApp
it will always try and install the pinned versions. I'll bump the versions with the next smaller update.
When I finally was able to start, there are many breaking changes not mentioned.
self.run.at
has had the propertyis_finished
removed, which breaks mycreateTimer
class, and causes most of my rules to fail.I am still trying to find a workaround for this. If a job is scheduled, how can I tell if it is pending, running, or has already run? I'm trying to use
.get_next_run()
but I am not sure what this returns if the job has completed.
Once you create a timer you get the object. On run/cancel you set this object to None. If the variable is None nothing is scheduled,
def __init__()
self._task = self.run.at(self.get_milliseconds, self.exec_wrap, *args, **kwargs) # <-- exec_wrap
def is_running():
return self.task is not None
def exec_wrap(*args, **kwargs):
self.task = None
self.exec_func(*args, **kwargs)
def cancel(self):
if self.task is not None:
self.task.cancel()
self.task = None
But there is a new self.run.countdown(...)
option on the scheduler which does basically what your class is doing.
Have you already taken a look at it?
Apparently
on_sun
has been silently dropped (not a big deal, I don't use it).
It's still there, I just updated the migration description:
self.run_on_sun('sunrise'
and replace with self.run.on_sunrise(
self.run_on_sun('sunset'
and replace with self.run.on_sunset(
When I create a new
run.every(None,)
task, I get an exceptionFirstRunInThePastError()
which causes the entire rule not to load. What is the point of this exception? why not just run the task immediately? if the task is scheduled now (ie not in the past) this exception breaks the rule.
run.every(None,)
should work and this is likely a bug.
HABApp will start fine with the wrong package version, but if you do a pip install HABApp it will always try and install the pinned versions. I'll bump the versions with the next smaller update.
This was not the case, I ran a git pull (HABApp setup as develop
). HABApp would then not start with various error of the type:
package ujson==4.0.2 not found
And it wasn’t until I reinstalled each package with the exact versions required in requirements.txt
That HABApp would start up.
Try upgrading ujson to 4.0.3 and see what happens.
I took a quick look at the countdown
timer, maybe I can adapt my code and do that instead in ‘createTimer`.
Thanks.
And it wasn’t until I reinstalled each package with the exact versions required in
requirements.txt
That HABApp would start up.
I just installed watchdog==2.0.3
(HABApp requires 2.0.2) and it launches fine. How do you start HABApp?
Anyway you can always just do pip install -r requirements.txt
and should be good to go.
ujson 4.0.3 is not available yet, I don't know how you can install it.
I run it as a systemd service
[Unit]
Description=HABApp
After=network-online.target
[Service]
Type=simple
User=openhab
Group=openhab
ExecStart=/usr/local/bin/habapp -c /etc/openhab2/HABApp
[Install]
WantedBy=multi-user.target
Sorry, I was wrong about ujson. Maybe it was aiohttp. I’ll check tomorrow.
Sorry, I was wrong about ujson. Maybe it was aiohttp. I’ll check tomorrow.
No worries, I was just confused and tried with something else.
When I create a new
run.every(None,)
task, I get an exceptionFirstRunInThePastError()
which causes the entire rule not to load.
Are you sure you are using eascheduler 0.1.1? I can't reproduce the issue.
This is the whole rule:
class LinkTap(HABApp.Rule):
'''
LinkTap Irrigation system control
Controlled by rate limited REST API calls (to LinkTap's servers, no local LAN API yet (Sept 2020)
username : Nick_W
login : ---
password : ---
apiKey : ---
see https://www.link-tap.com/#!/api-for-developers
'''
username = '---'
apiKey = '---'
base_item = 'LinkTap_Devices' #item name for self.devices
base_valve = 'LinkTap_Valve' #base item name for taplink's eg LinkTap_Valve_1_xxx, where xxx is Name, ID, Status etc.
watch_items = []
change_items = []
command_item = [#valve items, these are per-valve properties (eg LinkTap_Valve_1_SetMode)
'SetMode',
'SetWatertime']
items = ['LinkTap_Gateway_ID',
'LinkTap_Gateway_Status',
'LinkTap_Gateway_Message',
'config_lastCall',
#valve items, these are per-valve properties (eg LinkTap_Valve_1_Name)
'instant_lastCall',
'interval_lastCall',
'oddeven_lastCall',
'sevenday_lastCall',
'month_lastCall',
'query_lastCall',
'Name',
'ID',
'Status',
'signal',
'Battery',
'Mode_Name',
'current_flow',
'vol',
'last_vol',
'last_flow',
'watering',
'fall',
'broken',
'noWater']
rest = 'https://www.link-tap.com/api/'
api_calls = {'config' : {'rest' : 'getAllDevices', 'type' : 'POST', 'limit' : 300, 'code' : None},
'instant' : {'rest' : 'activateInstantMode', 'type' : 'POST', 'limit' : 15, 'code' : 'M'},
'interval' : {'rest' : 'activateIntervalMode', 'type' : 'POST', 'limit' : 15, 'code' : 'I'},
'oddeven' : {'rest' : 'activateOddEvenMode', 'type' : 'POST', 'limit' : 15, 'code' : 'O'},
'sevenday' : {'rest' : 'activateSevenDayMode', 'type' : 'POST', 'limit' : 15, 'code' : 'T'},
'month' : {'rest' : 'activateMonthMode', 'type' : 'POST', 'limit' : 15, 'code' : 'Y'},
'query' : {'rest' : 'getWateringStatus', 'type' : 'POST', 'limit' : 30, 'code' : None},
}
modes = { 'N' : 'None',
'M' : 'Instant',
'I' : 'Interval',
'O' : 'Odd Even',
'T' : '7 Day',
'Y' : 'Month'}
devices = [] #contains all devices (by gateway, hence list), updated by api call, saved in OH as 'LinkTap_Devices'
valves = {} #contains all linktaps, by linktap ID, populated from self.devices above in 'update_items' (not saved in OH)
queue = [] #used for rate limiting api calls. If an api call is already in the queue (pending call due to rate limiting), it is dropped if called twice
def __init__(self):
super().__init__()
self.log = logging.getLogger('MyRule.'+self.__class__.__name__)
#self.log.setLevel(logging.INFO)
self.current_mode = {} #current single letter mode for a taplink id, normally None, unless previous mode has to be reset after instant_on
self.reset_mode = {} #future event for a taplink id
self.message = {} #message to be displayed in OH, used in update_watering_status
self.item = {}
self.log.debug('LinkTap Rule Started')
try:
#restore self.devices from OH
self.devices = json.loads(OpenhabItem.get_item(self.base_item).value)
self.update_items()
except TypeError:
#or call api to get configuration and initialize self.devices
self.get_configuration()
self.get_watering_status()
#run these two api calls at the rate limit
self.run.every(None, self.api_calls['config']['limit'], self.get_configuration)
self.run.every(None, self.api_calls['query']['limit'], self.get_watering_status)
def init(self):
'''
Called the first time update_items is run after a restart to initialize all OH items
Never called again after this.
Usually this would be called by __init__, but moved here so that self.valves gets populated
first as self.valves is used to determine the taplink item names
self.valves depends on self.devices, and this could be retrieved asynchronously,
we can't determine when self.devices gets populated, so can't just put this in __init__
'''
all_items = self.watch_items + self.change_items + self.command_item + self.items
all_items.append(self.base_item)
for base_item in all_items:
for v in self.valves.values():
item = '{}{}'.format(v['item'],base_item)
try:
self.item[item] = OpenhabItem.get_item(item)
if base_item in self.watch_items:
self.listen_event(self.item[item], self.update_received, ItemStateEvent)
if base_item in self.change_items:
self.listen_event(self.item[item], self.change_received, ItemStateChangedEvent)
if base_item in self.command_item:
self.listen_event(self.item[item], self.command_received, ItemCommandEvent)
except HABApp.core.Items.ItemNotFoundException:
try:
self.item[base_item] = OpenhabItem.get_item(base_item)
break
except HABApp.core.Items.ItemNotFoundException:
self.log.warning('item {} not found, please add to taplink.items file'.format(item))
def command_received(self, event):
self.log.debug('Item %s command received %s' % (event.name, event.value))
if 'SetMode' in event.name:
self.set_mode(event)
elif 'SetWatertime' in event.name:
self.instant_on(event)
else:
self.log.debug('%s not found' % event.name)
def change_received(self, event):
self.log.debug('Item %s changed to %s' % (event.name, event.value))
if event.name == '':
pass
else:
self.log.debug('%s not found' % event.name)
def update_received(self, event):
self.log.debug('Item %s received update %s' % (event.name, event.value))
try:
if event.name == '':
pass
else:
self.log.debug('%s not found' % event.name)
except Exception as e:
self.log.exception(e)
def get_valve_from_item(self, item):
'''
Returns taplink id for a taplink, from the item name
uses self.valves, so requires 'update_items' from self.devices to be processed first
returns None if not found
'''
for k, v in self.valves.items():
if v['item'] in item:
return k
return None
def get_command_from_mode(self, mode):
'''
Gets the api command, for a given single letter mode
returns None if a command for a mode is not found
'''
for k, v in self.api_calls.items():
if v.get('code','') == mode:
return k
return None
def get_taplink_value(self, taplinkerId, value):
'''
return 'value' for taplink from self.devices
return None if value not found, or taplink not found
'value' would be a taplink configuration like 'status'
'''
for device in self.devices:
taplinkers = device.get('taplinker', [])
for taplink in taplinkers:
if taplink['taplinkerId'] == taplinkerId:
return taplink.get(value, None)
return None
def get_mode(self, taplinkerId):
'''
Returns the current single letter mode for a taplink from self.devices.
'''
return self.get_taplink_value(taplinkerId, 'workMode')
def get_status(self, taplinkerId):
'''
Returns True if taplink is currently connected, False otherwise, from self.devices
'''
return self.get_taplink_value(taplinkerId, 'status') == 'Connected'
def update_mode(self, id, mode=None, update=True):
'''
Update OH mode item (single letter) for taplink (including mode_name item).
If no mode is given get it from self.devices.
If mode is explicitly given, and update is True, update self.devices first.
We do this as self.devices only updates every 5 minutes, so it would be out of sync
if me manually change mode, but don't update self.devices. The exception is during update_items
'''
if not id: return
if not mode:
mode = self.get_mode(id)
else:
for index, device in enumerate(self.devices):
taplinkers = device.get('taplinker', [])
for tp, taplink in enumerate(taplinkers):
if taplink['taplinkerId'] == id:
self.devices[index]['taplinker'][tp]['workMode'] = mode
break
self.post_update(('SetMode', id), mode)
self.post_update(('Mode_Name', id), self.modes[mode])
def instant_on(self, event, taplinkerId=None):
'''
Turn valve on/off instantly (ish)
username: Required. String type. Your LinkTap account's username
apiKey: Required. String type. Your API key
gatewayId: Required. String type. Your LinkTap Gateway's first 16-digits/letters ID, case insensitive, no dash symbol, e,g, 3F7A23FE004B1200
taplinkerId: Required. String type. Your LinkTap Taplinker's first 16-digits/letters ID, case insensitive, no dash symbol, e,g, 67ABCDEF004B1200
action: Required. String or Boolean type. "true" for Watering ON, "false" for Watering OFF.
duration: Required. Number type. The watering duration (unit is minute). For Watering OFF, this field is 0. For Watring ON, the range is from 1 minute to 1439 minutes.
eco: Optional. String or Boolean type. "true" for ECO enabled, "false" for ECO disabled.
ecoOn: Required when eco equals "true". Number type. The valve ON duration (unit is minute). This value needs to be less than duration.
ecoOff: Required when eco equals "true". Number type. The valve OFF duration (unit is minute).
autoBack: Optional. String or Boolean type. "true" for automatically re-activating the previous watering plan after watering in Instant Mode is completed.
rate_limit is rate limiting delay
'''
if taplinkerId:
duration = event
else:
taplinkerId = self.get_valve_from_item(event.name)
duration = int(event.value)
if taplinkerId:
command = 'instant'
action = 'true' if duration > 0 else 'false'
autoBack = 'true' if duration > 0 else 'false' #automatically return to previous mode after watering
t = self.rate_limit(command, taplinkerId)
if t >= 0:
self.run.at(5, self.post_update, 'LinkTap_Gateway_Message', 'Watering change pending in {}s'.format(t+30))
#remember previous mode, so it can be restored later
if not self.current_mode.get(taplinkerId):
self.current_mode[taplinkerId] = self.get_mode(taplinkerId)
self.run.at(t, self.sendHttpRequest, command, gatewayId=self.valves[taplinkerId]['gateway'], taplinkerId=taplinkerId, action=action, duration=duration, autoBack=autoBack)
if self.reset_mode.get(taplinkerId) and not self.reset_mode[taplinkerId].is_finished:
self.reset_mode[taplinkerId].cancel()
self.reset_mode[taplinkerId] = self.run.at((duration*60)+t, self.reset_previous_mode, taplinkerId)
def reset_previous_mode(self, taplinkerId):
'''
Revert to previous mode after Instant mode has been called with an 'On' time
'''
if self.item[self.get_item('current_flow', taplinkerId)].value != 0:
#wait 30 more seconds for watering to stop
self.post_update('LinkTap_Gateway_Message', 'Restore previous mode in 30s')
self.reset_mode[taplinkerId] = self.run.at(30, self.reset_previous_mode, taplinkerId)
return
if self.current_mode.get(taplinkerId) and self.current_mode[taplinkerId] != 'M':
self.log.info('resetting previous mode: {}'.format(self.current_mode[taplinkerId]))
self.set_mode(self.current_mode[taplinkerId], taplinkerId)
self.reset_mode[taplinkerId] = None
self.current_mode[taplinkerId] = None
def set_mode(self, event, taplinkerId=None):
'''
Change taplink mode (mode must have been previously configured in TapLink app).
Instant mode can't be set here, you have to set the 'On' time for Instant mode, so just revert
back to the previous mode and display error message
rate_limit is rate limiting delay
'''
if taplinkerId:
mode = event
command = self.get_command_from_mode(event)
else:
taplinkerId = self.get_valve_from_item(event.name)
mode = event.value
if mode == 'M':
self.post_update('LinkTap_Gateway_Message', "Can't select Instant mode directly, use timing buttons")
self.update_mode(taplinkerId) #restore previous mode display
return
command = self.get_command_from_mode(mode)
if command and taplinkerId:
t = self.rate_limit(command, taplinkerId)
if t >= 0:
self.current_mode[taplinkerId] = None
self.run.at(t, self.sendHttpRequest, command, gatewayId=self.valves[taplinkerId]['gateway'], taplinkerId=taplinkerId)
def get_watering_status(self, taplinkerId=None):
'''
get taplink watering status if taplink is 'Connected'
if no taplink id is given, return status for all defined taplinks in self.valves
rate_limit is rate limiting delay
'''
if not taplinkerId:
for taplinkerId in self.valves.keys():
self.get_watering_status(taplinkerId)
elif self.get_status(taplinkerId):
command = 'query'
t = self.rate_limit(command, taplinkerId)
if t >= 0:
self.run.at(t, self.sendHttpRequest, command, taplinkerId=taplinkerId)
def rate_limit(self, command, taplinkerId, update=False):
'''
for rate limited calls, return time until next call allowed
return -1 if call already pending
if update is True, remove command from queue (if it is in the queue) and update
the command last run time, return -1
'''
item = self.get_item(command, taplinkerId)
last_run_item = self.item['{}_lastCall'.format(item)]
if item in self.queue:
if update:
self.queue.remove(item)
self.post_update(last_run_item, datetime.datetime.now()) #update last run time for command
t = -1 # do not execute command
else:
if last_run_item.value:
#calculate whole seconds before command can be run
t = max(0, round(self.api_calls[command].get('limit', 0) - (datetime.datetime.now() - last_run_item.value).total_seconds(), 0))
else:
t = 0
self.queue.append(item)
return int(t)
def get_configuration(self):
self.sendHttpRequest('config')
def update_configuration(self):
self.devices = self.response.get('devices', [])
self.update_items()
def update_items(self):
'''
Update OH items from values in self.devices
self.valves is created/updated here.
'''
init = False
self.post_update('LinkTap_Devices', json.dumps(self.devices))
gateway=[]
status=[]
for device in self.devices:
gateway.append(device['gatewayId'])
status.append(device['status'])
taplinkers = device.get('taplinker', [])
for taplink in taplinkers:
id = taplink['taplinkerId']
if id not in self.valves:
self.valves[id] = {'name' : taplink['taplinkerName'], 'gateway' : device['gatewayId'], 'item' : '{}_{}_'.format(self.base_valve, len(self.valves.keys())+1)}
self.log.info('added {} to valves database : {}'.format(id, self.valves))
init=True
self.post_update(('Name',id), taplink['taplinkerName'])
self.post_update(('ID',id), taplink['taplinkerId'])
self.post_update(('Status',id), taplink['status'])
if taplink['signal']:
if isinstance(taplink['signal'], str):
taplink['signal'] = taplink['signal'].replace('%','')
self.post_update(('signal',id), taplink['signal'] if taplink['signal'] else None)
self.post_update(('Battery',id), taplink['batteryStatus'].replace('%','') if taplink['batteryStatus'] else None)
self.update_mode(id, taplink['workMode'], False)
self.post_update(('last_flow',id), taplink['vel']/1000)
self.post_update(('fall',id), 'OPEN' if taplink['fall'] else 'CLOSED')
self.post_update(('broken',id), 'OPEN' if taplink['valveBroken'] else 'CLOSED')
self.post_update(('noWater',id), 'OPEN' if taplink['noWater'] else 'CLOSED')
self.post_update('LinkTap_Gateway_ID', ','.join(gateway))
self.post_update('LinkTap_Gateway_Status', ','.join(status))
if init:
self.init()
self.update_items()
def update_watering_status(self, id):
'''
watering status update for taplink device 'id'
from self.response (api call response)
Explanation of relevant fields in the watering status:
total: the total watering duration of a watering slot.
onDuration: the remaining watering duration of a watering slot.
ecoTotal: the total watering duration of a watering plan when the ECO mode is enabled.
ecoOn: valve ON duration.
ecoOff: valve OFF duration.
vel: current flow rate (unit: ml per minute. For G2 only).
vol: volume dispensed
pbCnt: unknown (usually "0")
pcCnt: unknown (usually "0")
"status": {
"onDuration": "4",
"total": "5",
"onStamp": "1600798062158",
"firstDt": "1600798062158",
"vel": "3037",
"vol": "0"
}
"status": None for watering off.
'''
if not self.response: return
status = self.response.get('status', None)
if status:
self.post_update(('watering', id), '{}/{} m'.format(status['onDuration'], status['total']))
self.post_update(('SetWatertime', id), status['onDuration'])
self.post_update(('current_flow',id), int(status['vel'])/1000) #flow rate in ml/m
self.post_update(('vol',id), int(status['vol'])/1000) #volume delivered in ml
self.post_update(('last_vol',id), int(status['vol'])/1000) #volume delivered in ml
self.message[id] = self.valves[id]['name']
else:
self.post_update(('watering',id), 'Off')
self.post_update(('current_flow',id), 0)
self.post_update(('vol',id), 0)
self.message.pop(id, None)
if self.get_mode(id) != 'M': #M is instant mode, it takes a while to activate watering, so this avoids resetting the timer display to 0 while we wait...
self.post_update(('SetWatertime', id), 0)
if self.message:
message = 'Watering {}'.format(','.join(self.message.values()))
self.post_update('LinkTap_Gateway_Message', message)
def get_item(self, item, taplinkerId=None):
'''
construct item name from taplink id, if given, from self.valves
item could be a tuple or list (item, taplink_id)
'''
if isinstance(item, (tuple, list)):
item = '{}{}'.format(self.valves[item[1]]['item'], item[0])
elif taplinkerId:
item = self.get_item((item, taplinkerId))
return item
def post_update(self, orgitem, value):
'''
Update an OH item with value. item could be a tuple of (item, taplink_id), to be expanded to a full item name
or an actual OH item.
'''
item = self.get_item(orgitem)
if isinstance(item, str):
item = self.item.get(item)
if item is not None:
self.oh.post_update(item, value)
self.log.info('updated {} to {}'.format(item.name, value))
else:
self.log.warning('item: {} not found, not updating to {}'.format(orgitem, value))
def sendHttpRequest(self, command, **kwargs):
self.run.soon(self._sendHttpRequest, command, **kwargs)
async def _sendHttpRequest(self, command, **kwargs):
'''
make actual REST call to LinkTaps's servers
gateway id, taplink id plus other parameters (if required) are passed in **kwargs
so the keywords are important and must match the api expected names
eg an api call expects to be passed {'taplinkerId':'560B4221004B1200'} in the body of a POST,
so we have to pass **kwargs with taplinkerId defined.
API calls are rate limited, we get a '400 Bad Request' response if we call the API too soon
'''
url, mode, type, body = self.api_call(command, **kwargs)
id = kwargs.get('taplinkerId') #taplink id if one was passed
if url:
self.log.debug('{}ing url: {}, body {}'.format(type, url, body))
headers = {'Content-Type' : 'application/x-www-form-urlencoded'}
try:
if type == 'POST':
async with self.async_http.post(url, data=body, headers=headers) as resp:
self.log.debug(resp)
self.response = await resp.json()
elif type == 'GET':
async with self.async_http.get(url, params=body, headers=headers) as resp:
self.log.debug(resp)
self.response = await resp.json()
self.log.debug(self.response)
#process response
if resp.status == 200:
if self.response.get('result', 'None') == 'ok':
if mode: #if a mode is associated with a command (eg activateMode), update the mode (no mode for config or query)
self.update_mode(id, mode)
if command == 'config':
self.update_configuration()
elif command == 'query':
self.update_watering_status(id)
else:
self.log.warning('data error : {}'.format(self.response.get('message', 'No Message')))
else:
self.log.warning('connection error: {}, {}'.format(resp.status, self.response.get('message', 'No Message')))
if mode:
self.update_mode(id) #revert mode display, as command to change mode failed
except Exception as e:
self.log.error('Error connecting to LinkTap: {}'.format(e))
#rate limiting - remove command from queue and update timestamp
self.rate_limit(command, id, update=True)
if self.response.get('message'):
self.post_update('LinkTap_Gateway_Message', self.response['message'])
else:
self.log.warning('command: {} not found'.format(command))
self.response = {}
def api_call(self, command, **kwargs):
'''
return rest api call, mode, type (GET/POST) from 'command'
return body from authentication and kwargs
'''
http_command = mode = type = body = None
if command in self.api_calls.keys():
http_command = "{}{}".format(self.rest, self.api_calls[command]['rest'])
mode = self.api_calls[command].get('code')
type = self.api_calls[command].get('type', 'POST') #Default is POST
body = {'username' : self.username, 'apiKey' : self.apiKey}
body.update(kwargs)
return http_command, mode, type, body
Similar error as @NickWaterton with self.run.every(None,...)
:
2021-05-03 13:14:57.491 [ERROR] [HABApp ] - OverflowError: timestamp out of range for platform time_t
2021-05-03 13:14:57.719 [ERROR] [HABApp.Rules ] - Error "timestamp out of range for platform time_t" in load:
2021-05-03 13:14:57.720 [ERROR] [HABApp.Rules ] - Could not load /etc/openhab/habapp/rules/UPS.py!
I changed all my None
to timedelta(seconds=5)
which works. Setting to 0 seconds gave the same error as Nick:
2021-05-03 13:20:32.790 [ERROR] [HABApp.Rules ] - FirstRunInThePastError: First run must be in the future! Now: 2021-05-03T13:20:32.541628+02:00, run: 2021-05-03T13:20:32.541628+02:00
2021-05-03 13:20:32.794 [WARN ] [HABApp.Rules ] - Failed to load /etc/openhab/habapp/rules/UPS.py!
Impressive rule. I deleted your traceback and edited your post so your credentials are not available in plain text here.
I was searching for the problem where self.run.at(None, ...
would throw an error, but here it's the case for self.run.at(0, ...
.
0
as a value. I'll try to provide a fix which will allow 0 as a value.
@magnuslsjoberg Your rules seemed to also generate another error. Could you post the example?
Thanks, I forgot my credentials were in the file.
I just tried run.every(None,)
again, and it's working now - I think I'm just getting lost in the huge amount of error messages I'm trying to parse....
Please ignore this. Thanks for the quick response.
I also tried self.run.on_sunset(self.sunset_func)
which has an error as well (as I say, not a big deal, as I don't use it).
The error is ValueError: Sun is always below the horizon on this day, at this location.
2021-05-03 10:46:30,742[ HABApp.Rules]ERROR| __exit__: ..................................................
2021-05-03 10:46:30,742[ HABApp.Rules]ERROR| __exit__: observer = Observer(latitude=-79.70, longitude=-79.70, elevation=0.0)
2021-05-03 10:46:30,742[ HABApp.Rules]ERROR| __exit__: Observer = <class 'astral.Observer'>
2021-05-03 10:46:30,742[ HABApp.Rules]ERROR| __exit__: date = Date(2021, 5, 3)
2021-05-03 10:46:30,743[ HABApp.Rules]ERROR| __exit__: Optional = typing.Optional
2021-05-03 10:46:30,743[ HABApp.Rules]ERROR| __exit__: datetime.date = <class 'datetime.date'>
2021-05-03 10:46:30,743[ HABApp.Rules]ERROR| __exit__: tzinfo = Timezone('UTC')
2021-05-03 10:46:30,743[ HABApp.Rules]ERROR| __exit__: Union = typing.Union
2021-05-03 10:46:30,744[ HABApp.Rules]ERROR| __exit__: datetime.tzinfo = <class 'datetime.tzinfo'>
2021-05-03 10:46:30,744[ HABApp.Rules]ERROR| __exit__: pytz.utc = <UTC>
2021-05-03 10:46:30,744[ HABApp.Rules]ERROR| __exit__: datetime.datetime = <class 'datetime.datetime'>
2021-05-03 10:46:30,745[ HABApp.Rules]ERROR| __exit__: z = 95.53522333943233
2021-05-03 10:46:30,745[ HABApp.Rules]ERROR| __exit__: msg = 'Sun is always below the horizon on this day, at this location.'
2021-05-03 10:46:30,745[ HABApp.Rules]ERROR| __exit__: ..................................................
I see that the longitude is incorrect (ie it the same as the latitude), location set in config.yaml
is:
location:
latitude: 43.60
longitude: -79.70
elevation: 0.0
and I'm not convinced that the timezone is correct either.
Having said that, I thought that astral
was no longer required?
Here is a minimum example that fails with None:
import HABApp
import logging
class RunEveryTest(HABApp.Rule):
def __init__(self):
super().__init__()
self.run.every(None,10,self.test)
def test(self):
logging.info('in test()')
log = logging.getLogger('HABApp')
RunEveryTest()
I run HABApp on openhabian inside a virtual environment:
(habapp) openhabian@openhab:~ $ pip freeze
aiohttp==3.7.4
aiohttp-sse-client==0.2.1
astral==2.2
async-timeout==3.0.1
attrs==20.3.0
bidict==0.21.2
cffi==1.14.4
chardet==3.0.4
cryptography==3.3.1
EAScheduler==0.1.1
EasyCo==0.2.3
HABApp==0.30.0
idna==3.1
ifaddr==0.1.7
multidict==5.1.0
netifaces==0.10.9
paho-mqtt==1.5.1
pendulum==2.1.2
pkg-resources==0.0.0
protobuf==3.14.0
pyatv==0.7.7
pycparser==2.20
pydantic==1.8.1
python-dateutil==2.8.1
pytz==2020.5
pytzdata==2020.1
ruamel.yaml==0.16.12
ruamel.yaml.clib==0.2.2
six==1.15.0
srptools==1.0.1
stackprinter==0.2.5
typing-extensions==3.7.4.3
tzlocal==2.1
ujson==4.0.2
voluptuous==0.12.1
watchdog==2.0.2
yarl==1.6.3
zeroconf==0.28.8
(habapp) openhabian@openhab:~ $
Error log:
2021-05-03 17:17:35.690 [ERROR] [HABApp ] - Error timestamp out of range for platform time_t in HABApp.scheduler:
2021-05-03 17:17:35.690 [ERROR] [HABApp ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/schedulers/scheduler_async.py", line 89, in __run_next
2021-05-03 17:17:35.691 [ERROR] [HABApp ] - 65 async def __run_next(self):
2021-05-03 17:17:35.691 [ERROR] [HABApp ] - (...)
2021-05-03 17:17:35.691 [ERROR] [HABApp ] - 85
2021-05-03 17:17:35.691 [ERROR] [HABApp ] - 86 assert old_job is job, f'Job changed unexpectedly\n{old_job}\n{job}'
2021-05-03 17:17:35.691 [ERROR] [HABApp ] - 87
2021-05-03 17:17:35.692 [ERROR] [HABApp ] - 88 try:
2021-05-03 17:17:35.692 [ERROR] [HABApp ] - --> 89 job._execute()
2021-05-03 17:17:35.692 [ERROR] [HABApp ] - 90 except Exception as e:
2021-05-03 17:17:35.692 [ERROR] [HABApp ] - ..................................................
2021-05-03 17:17:35.692 [ERROR] [HABApp ] - self = <eascheduler.schedulers.scheduler_asyncthread.ThreadSafeAsyncScheduler object at 0x6173d850>
2021-05-03 17:17:35.693 [ERROR] [HABApp ] - old_job = <ReoccurringJob next_run: 2021-05-03T17:17:35.587000>
2021-05-03 17:17:35.693 [ERROR] [HABApp ] - job = <ReoccurringJob next_run: 2021-05-03T17:17:35.587000>
2021-05-03 17:17:35.693 [ERROR] [HABApp ] - job._execute = <method 'ScheduledJobBase._execute' of <ReoccurringJob next_run: 2021-05-03T17:17:35.587000> job_base.py:26>
2021-05-03 17:17:35.693 [ERROR] [HABApp ] - e = OverflowError('timestamp out of range for platform time_t')
2021-05-03 17:17:35.693 [ERROR] [HABApp ] - ..................................................
2021-05-03 17:17:35.694 [ERROR] [HABApp ] -
2021-05-03 17:17:35.694 [ERROR] [HABApp ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/jobs/job_base.py", line 27, in _execute
2021-05-03 17:17:35.694 [ERROR] [HABApp ] - 26 def _execute(self):
2021-05-03 17:17:35.694 [ERROR] [HABApp ] - --> 27 self._schedule_next_run()
2021-05-03 17:17:35.695 [ERROR] [HABApp ] - 28 self._func.execute()
2021-05-03 17:17:35.695 [ERROR] [HABApp ] - ..................................................
2021-05-03 17:17:35.695 [ERROR] [HABApp ] - self = <ReoccurringJob next_run: 2021-05-03T17:17:35.587000>
2021-05-03 17:17:35.695 [ERROR] [HABApp ] - self._schedule_next_run = <method 'ReoccurringJob._schedule_next_run' of <ReoccurringJob next_run: 2021-05-03T17:17:35.587000> job_reoccuring.py:22>
2021-05-03 17:17:35.695 [ERROR] [HABApp ] - self._func.execute = <method 'WrappedFunctionExecutor.execute' of <HABApp.rule.habappscheduler.WrappedFunctionExecutor object at 0x6173d970> habappscheduler.py:21>
2021-05-03 17:17:35.696 [ERROR] [HABApp ] - ..................................................
2021-05-03 17:17:35.696 [ERROR] [HABApp ] -
2021-05-03 17:17:35.696 [ERROR] [HABApp ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/jobs/job_reoccuring.py", line 27, in _schedule_next_run
2021-05-03 17:17:35.696 [ERROR] [HABApp ] - 22 def _schedule_next_run(self):
2021-05-03 17:17:35.697 [ERROR] [HABApp ] - 23 now = get_now(UTC).timestamp()
2021-05-03 17:17:35.697 [ERROR] [HABApp ] - 24 while self._next_run_base < now:
2021-05-03 17:17:35.697 [ERROR] [HABApp ] - 25 self._next_run_base += self._interval
2021-05-03 17:17:35.697 [ERROR] [HABApp ] - 26
2021-05-03 17:17:35.697 [ERROR] [HABApp ] - --> 27 self._apply_boundaries()
2021-05-03 17:17:35.698 [ERROR] [HABApp ] - ..................................................
2021-05-03 17:17:35.698 [ERROR] [HABApp ] - self = <ReoccurringJob next_run: 2021-05-03T17:17:35.587000>
2021-05-03 17:17:35.698 [ERROR] [HABApp ] - now = 1620055055.592741
2021-05-03 17:17:35.698 [ERROR] [HABApp ] - get_now = <function 'now' __init__.py:197>
2021-05-03 17:17:35.698 [ERROR] [HABApp ] - UTC = Timezone('UTC')
2021-05-03 17:17:35.699 [ERROR] [HABApp ] - self._next_run_base = 3767538702.5874157
2021-05-03 17:17:35.699 [ERROR] [HABApp ] - self._interval = 2147483647.0
2021-05-03 17:17:35.699 [ERROR] [HABApp ] - self._apply_boundaries = <method 'DateTimeJobBase._apply_boundaries' of <ReoccurringJob next_run: 2021-05-03T17:17:35.587000> job_base_datetime.py:144>
2021-05-03 17:17:35.699 [ERROR] [HABApp ] - ..................................................
2021-05-03 17:17:35.699 [ERROR] [HABApp ] -
2021-05-03 17:17:35.700 [ERROR] [HABApp ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/jobs/job_base_datetime.py", line 148, in _apply_boundaries
2021-05-03 17:17:35.700 [ERROR] [HABApp ] - 144 def _apply_boundaries(self):
2021-05-03 17:17:35.700 [ERROR] [HABApp ] - 145 self._adjusting = True
2021-05-03 17:17:35.700 [ERROR] [HABApp ] - 146
2021-05-03 17:17:35.700 [ERROR] [HABApp ] - 147 # Starting point is always the next call in local time
2021-05-03 17:17:35.701 [ERROR] [HABApp ] - --> 148 next_run_local: DateTime = from_timestamp(self._next_run_base, local_tz)
2021-05-03 17:17:35.701 [ERROR] [HABApp ] - 149
2021-05-03 17:17:35.701 [ERROR] [HABApp ] - ..................................................
2021-05-03 17:17:35.701 [ERROR] [HABApp ] - self = <ReoccurringJob next_run: 2021-05-03T17:17:35.587000>
2021-05-03 17:17:35.701 [ERROR] [HABApp ] - self._adjusting = True
2021-05-03 17:17:35.702 [ERROR] [HABApp ] - DateTime = <class 'pendulum.datetime.DateTime'>
2021-05-03 17:17:35.702 [ERROR] [HABApp ] - self._next_run_base = 3767538702.5874157
2021-05-03 17:17:35.702 [ERROR] [HABApp ] - local_tz = Timezone('Europe/Stockholm')
2021-05-03 17:17:35.702 [ERROR] [HABApp ] - ..................................................
2021-05-03 17:17:35.702 [ERROR] [HABApp ] -
2021-05-03 17:17:35.703 [ERROR] [HABApp ] - File "/opt/habapp/lib/python3.7/site-packages/pendulum/__init__.py", line 272, in from_timestamp
2021-05-03 17:17:35.703 [ERROR] [HABApp ] - 266 def from_timestamp(
2021-05-03 17:17:35.703 [ERROR] [HABApp ] - 267 timestamp, tz=UTC
2021-05-03 17:17:35.703 [ERROR] [HABApp ] - 268 ): # type: (Union[int, float], Union[str, _Timezone]) -> DateTime
2021-05-03 17:17:35.703 [ERROR] [HABApp ] - 269 """
2021-05-03 17:17:35.704 [ERROR] [HABApp ] - 270 Create a DateTime instance from a timestamp.
2021-05-03 17:17:35.704 [ERROR] [HABApp ] - 271 """
2021-05-03 17:17:35.704 [ERROR] [HABApp ] - --> 272 dt = _datetime.datetime.utcfromtimestamp(timestamp)
2021-05-03 17:17:35.704 [ERROR] [HABApp ] - 273
2021-05-03 17:17:35.704 [ERROR] [HABApp ] - ..................................................
2021-05-03 17:17:35.705 [ERROR] [HABApp ] - timestamp = 3767538702.5874157
2021-05-03 17:17:35.705 [ERROR] [HABApp ] - tz = Timezone('Europe/Stockholm')
2021-05-03 17:17:35.705 [ERROR] [HABApp ] - UTC = Timezone('UTC')
2021-05-03 17:17:35.705 [ERROR] [HABApp ] - ..................................................
2021-05-03 17:17:35.705 [ERROR] [HABApp ] -
2021-05-03 17:17:35.706 [ERROR] [HABApp ] - ---- (full traceback above) ----
2021-05-03 17:17:35.706 [ERROR] [HABApp ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/schedulers/scheduler_async.py", line 89, in __run_next
2021-05-03 17:17:35.706 [ERROR] [HABApp ] - job._execute()
2021-05-03 17:17:35.706 [ERROR] [HABApp ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/jobs/job_base.py", line 27, in _execute
2021-05-03 17:17:35.706 [ERROR] [HABApp ] - self._schedule_next_run()
2021-05-03 17:17:35.707 [ERROR] [HABApp ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/jobs/job_reoccuring.py", line 27, in _schedule_next_run
2021-05-03 17:17:35.707 [ERROR] [HABApp ] - self._apply_boundaries()
2021-05-03 17:17:35.707 [ERROR] [HABApp ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/jobs/job_base_datetime.py", line 148, in _apply_boundaries
2021-05-03 17:17:35.707 [ERROR] [HABApp ] - next_run_local: DateTime = from_timestamp(self._next_run_base, local_tz)
2021-05-03 17:17:35.707 [ERROR] [HABApp ] - File "/opt/habapp/lib/python3.7/site-packages/pendulum/__init__.py", line 272, in from_timestamp
2021-05-03 17:17:35.708 [ERROR] [HABApp ] - dt = _datetime.datetime.utcfromtimestamp(timestamp)
2021-05-03 17:17:35.708 [ERROR] [HABApp ] -
2021-05-03 17:17:35.708 [ERROR] [HABApp ] - OverflowError: timestamp out of range for platform time_t
2021-05-03 17:17:35.907 [ERROR] [HABApp.Rules ] - Error "timestamp out of range for platform time_t" in load:
2021-05-03 17:17:35.908 [ERROR] [HABApp.Rules ] - Could not load /etc/openhab/habapp/rules/RunEveryTest.py!
2021-05-03 17:17:35.908 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/HABApp/rule_manager/rule_file.py", line 80, in load
2021-05-03 17:17:35.908 [ERROR] [HABApp.Rules ] - self.create_rules(created_rules)
2021-05-03 17:17:35.908 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/HABApp/rule_manager/rule_file.py", line 69, in create_rules
2021-05-03 17:17:35.908 [ERROR] [HABApp.Rules ] - '__HABAPP__RULES': created_rules,
2021-05-03 17:17:35.909 [ERROR] [HABApp.Rules ] - File "/usr/lib/python3.7/runpy.py", line 263, in run_path
2021-05-03 17:17:35.909 [ERROR] [HABApp.Rules ] - pkg_name=pkg_name, script_name=fname)
2021-05-03 17:17:35.909 [ERROR] [HABApp.Rules ] - File "/usr/lib/python3.7/runpy.py", line 96, in _run_module_code
2021-05-03 17:17:35.909 [ERROR] [HABApp.Rules ] - mod_name, mod_spec, pkg_name, script_name)
2021-05-03 17:17:35.910 [ERROR] [HABApp.Rules ] - File "/usr/lib/python3.7/runpy.py", line 85, in _run_code
2021-05-03 17:17:35.910 [ERROR] [HABApp.Rules ] - exec(code, run_globals)
2021-05-03 17:17:35.910 [ERROR] [HABApp.Rules ] - File "/etc/openhab/habapp/rules/RunEveryTest.py", line 21, in RunEveryTest.py
2021-05-03 17:17:35.910 [ERROR] [HABApp.Rules ] - 17
2021-05-03 17:17:35.910 [ERROR] [HABApp.Rules ] - 18 # Main init
2021-05-03 17:17:35.911 [ERROR] [HABApp.Rules ] - 19 log = logging.getLogger('HABApp')
2021-05-03 17:17:35.911 [ERROR] [HABApp.Rules ] - 20
2021-05-03 17:17:35.911 [ERROR] [HABApp.Rules ] - --> 21 RunEveryTest()
2021-05-03 17:17:35.911 [ERROR] [HABApp.Rules ] - 22
2021-05-03 17:17:35.911 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.912 [ERROR] [HABApp.Rules ] - log = <Logger HABApp (INFO)>
2021-05-03 17:17:35.912 [ERROR] [HABApp.Rules ] - logging.getLogger = <function 'getLogger' __init__.py:1930>
2021-05-03 17:17:35.912 [ERROR] [HABApp.Rules ] - RunEveryTest = <class '/etc/openhab/habapp/rules/RunEveryTest.py.RunEveryTest'>
2021-05-03 17:17:35.912 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.912 [ERROR] [HABApp.Rules ] -
2021-05-03 17:17:35.913 [ERROR] [HABApp.Rules ] - File "/etc/openhab/habapp/rules/RunEveryTest.py", line 12, in __init__
2021-05-03 17:17:35.913 [ERROR] [HABApp.Rules ] - 10 def __init__(self):
2021-05-03 17:17:35.913 [ERROR] [HABApp.Rules ] - 11 super().__init__()
2021-05-03 17:17:35.913 [ERROR] [HABApp.Rules ] - --> 12 self.run.every(None,10,self.test)
2021-05-03 17:17:35.913 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.914 [ERROR] [HABApp.Rules ] - self = </etc/openhab/habapp/rules/RunEveryTest.py.RunEveryTest object at 0x6173dd50>
2021-05-03 17:17:35.914 [ERROR] [HABApp.Rules ] - self.run.every = <method 'HABAppScheduler.every' of <HABApp.rule.habappscheduler.HABAppScheduler object at 0x6173dcf0> habappscheduler.py:38>
2021-05-03 17:17:35.914 [ERROR] [HABApp.Rules ] - self.test = <method 'RunEveryTest.test' of </etc/openhab/habapp/rules/RunEveryTest.py.RunEveryTest object at 0x6173dd50> RunEveryTest.py:14>
2021-05-03 17:17:35.914 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.915 [ERROR] [HABApp.Rules ] -
2021-05-03 17:17:35.915 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/HABApp/rule/habappscheduler.py", line 41, in every
2021-05-03 17:17:35.915 [ERROR] [HABApp.Rules ] - 38 def every(self, start_time: Union[None, dt_datetime, dt_timedelta, dt_time, int],
2021-05-03 17:17:35.915 [ERROR] [HABApp.Rules ] - 39 interval: Union[int, float, dt_timedelta], callback, *args, **kwargs) -> ReoccurringJob:
2021-05-03 17:17:35.915 [ERROR] [HABApp.Rules ] - 40 callback = WrappedFunction(callback, name=self._rule._get_cb_name(callback))
2021-05-03 17:17:35.916 [ERROR] [HABApp.Rules ] - --> 41 return super().every(start_time, interval, callback, *args, **kwargs)
2021-05-03 17:17:35.916 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.916 [ERROR] [HABApp.Rules ] - self = <HABApp.rule.habappscheduler.HABAppScheduler object at 0x6173dcf0>
2021-05-03 17:17:35.916 [ERROR] [HABApp.Rules ] - start_time = None
2021-05-03 17:17:35.916 [ERROR] [HABApp.Rules ] - Union = typing.Union
2021-05-03 17:17:35.917 [ERROR] [HABApp.Rules ] - dt_datetime = <class 'datetime.datetime'>
2021-05-03 17:17:35.917 [ERROR] [HABApp.Rules ] - dt_timedelta = <class 'datetime.timedelta'>
2021-05-03 17:17:35.917 [ERROR] [HABApp.Rules ] - dt_time = <class 'datetime.time'>
2021-05-03 17:17:35.917 [ERROR] [HABApp.Rules ] - interval = 10
2021-05-03 17:17:35.917 [ERROR] [HABApp.Rules ] - callback = <HABApp.core.wrappedfunction.WrappedFunction object at 0x6173d550>
2021-05-03 17:17:35.918 [ERROR] [HABApp.Rules ] - args = ()
2021-05-03 17:17:35.918 [ERROR] [HABApp.Rules ] - kwargs = {}
2021-05-03 17:17:35.918 [ERROR] [HABApp.Rules ] - ReoccurringJob = <class 'eascheduler.jobs.job_reoccuring.ReoccurringJob'>
2021-05-03 17:17:35.918 [ERROR] [HABApp.Rules ] - WrappedFunction = <class 'HABApp.core.wrappedfunction.WrappedFunction'>
2021-05-03 17:17:35.918 [ERROR] [HABApp.Rules ] - self._rule._get_cb_name = <method 'Rule._get_cb_name' of </etc/openhab/habapp/rules/RunEveryTest.py.RunEveryTest object at 0x6173dd50> rule.py:251>
2021-05-03 17:17:35.919 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.919 [ERROR] [HABApp.Rules ] -
2021-05-03 17:17:35.919 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/scheduler_view.py", line 59, in every
2021-05-03 17:17:35.919 [ERROR] [HABApp.Rules ] - 46 def every(self, start_time: Union[None, dt_datetime, dt_timedelta, dt_time, int, float],
2021-05-03 17:17:35.920 [ERROR] [HABApp.Rules ] - 47 interval: Union[int, float, dt_timedelta], callback, *args, **kwargs) -> ReoccurringJob:
2021-05-03 17:17:35.920 [ERROR] [HABApp.Rules ] - (...)
2021-05-03 17:17:35.920 [ERROR] [HABApp.Rules ] - 55 :return: Created job
2021-05-03 17:17:35.920 [ERROR] [HABApp.Rules ] - 56 """
2021-05-03 17:17:35.920 [ERROR] [HABApp.Rules ] - 57 job = ReoccurringJob(self._scheduler, self._executor(callback, *args, **kwargs))
2021-05-03 17:17:35.921 [ERROR] [HABApp.Rules ] - 58 job._schedule_first_run(start_time)
2021-05-03 17:17:35.921 [ERROR] [HABApp.Rules ] - --> 59 job.interval(interval)
2021-05-03 17:17:35.921 [ERROR] [HABApp.Rules ] - 60 return job
2021-05-03 17:17:35.921 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.921 [ERROR] [HABApp.Rules ] - self = <HABApp.rule.habappscheduler.HABAppScheduler object at 0x6173dcf0>
2021-05-03 17:17:35.922 [ERROR] [HABApp.Rules ] - start_time = None
2021-05-03 17:17:35.922 [ERROR] [HABApp.Rules ] - Union = typing.Union
2021-05-03 17:17:35.922 [ERROR] [HABApp.Rules ] - dt_datetime = <class 'datetime.datetime'>
2021-05-03 17:17:35.922 [ERROR] [HABApp.Rules ] - dt_timedelta = <class 'datetime.timedelta'>
2021-05-03 17:17:35.922 [ERROR] [HABApp.Rules ] - dt_time = <class 'datetime.time'>
2021-05-03 17:17:35.923 [ERROR] [HABApp.Rules ] - interval = 10
2021-05-03 17:17:35.923 [ERROR] [HABApp.Rules ] - callback = <HABApp.core.wrappedfunction.WrappedFunction object at 0x6173d550>
2021-05-03 17:17:35.923 [ERROR] [HABApp.Rules ] - args = ()
2021-05-03 17:17:35.923 [ERROR] [HABApp.Rules ] - kwargs = {}
2021-05-03 17:17:35.923 [ERROR] [HABApp.Rules ] - ReoccurringJob = <class 'eascheduler.jobs.job_reoccuring.ReoccurringJob'>
2021-05-03 17:17:35.924 [ERROR] [HABApp.Rules ] - job = <ReoccurringJob next_run: 2021-05-03T17:17:35.587000>
2021-05-03 17:17:35.924 [ERROR] [HABApp.Rules ] - self._scheduler = <eascheduler.schedulers.scheduler_asyncthread.ThreadSafeAsyncScheduler object at 0x6173d850>
2021-05-03 17:17:35.924 [ERROR] [HABApp.Rules ] - self._executor = <class 'HABApp.rule.habappscheduler.WrappedFunctionExecutor'>
2021-05-03 17:17:35.924 [ERROR] [HABApp.Rules ] - job._schedule_first_run = <method 'DateTimeJobBase._schedule_first_run' of <ReoccurringJob next_run: 2021-05-03T17:17:35.587000> job_base_datetime.py:42>
2021-05-03 17:17:35.924 [ERROR] [HABApp.Rules ] - job.interval = <method 'ReoccurringJob.interval' of <ReoccurringJob next_run: 2021-05-03T17:17:35.587000> job_reoccuring.py:32>
2021-05-03 17:17:35.925 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.925 [ERROR] [HABApp.Rules ] -
2021-05-03 17:17:35.925 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/jobs/job_reoccuring.py", line 45, in interval
2021-05-03 17:17:35.925 [ERROR] [HABApp.Rules ] - 32 def interval(self, interval: Union[int, float, timedelta]) -> ReoccurringJob:
2021-05-03 17:17:35.926 [ERROR] [HABApp.Rules ] - (...)
2021-05-03 17:17:35.926 [ERROR] [HABApp.Rules ] - 41 interval = interval.total_seconds()
2021-05-03 17:17:35.926 [ERROR] [HABApp.Rules ] - 42 assert interval > 0, interval
2021-05-03 17:17:35.926 [ERROR] [HABApp.Rules ] - 43
2021-05-03 17:17:35.926 [ERROR] [HABApp.Rules ] - 44 self._interval = interval
2021-05-03 17:17:35.927 [ERROR] [HABApp.Rules ] - --> 45 self._schedule_next_run()
2021-05-03 17:17:35.927 [ERROR] [HABApp.Rules ] - 46 return self
2021-05-03 17:17:35.927 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.927 [ERROR] [HABApp.Rules ] - interval = 10
2021-05-03 17:17:35.927 [ERROR] [HABApp.Rules ] - self = <ReoccurringJob next_run: 2021-05-03T17:17:35.587000>
2021-05-03 17:17:35.928 [ERROR] [HABApp.Rules ] - Union = typing.Union
2021-05-03 17:17:35.928 [ERROR] [HABApp.Rules ] - timedelta = <class 'datetime.timedelta'>
2021-05-03 17:17:35.928 [ERROR] [HABApp.Rules ] - ReoccurringJob = <class 'eascheduler.jobs.job_reoccuring.ReoccurringJob'>
2021-05-03 17:17:35.928 [ERROR] [HABApp.Rules ] - interval.total_seconds = # AttributeError
2021-05-03 17:17:35.928 [ERROR] [HABApp.Rules ] - interval = 10
2021-05-03 17:17:35.929 [ERROR] [HABApp.Rules ] - self._interval = 10
2021-05-03 17:17:35.929 [ERROR] [HABApp.Rules ] - self._schedule_next_run = <method 'ReoccurringJob._schedule_next_run' of <ReoccurringJob next_run: 2021-05-03T17:17:35.587000> job_reoccuring.py:22>
2021-05-03 17:17:35.929 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.929 [ERROR] [HABApp.Rules ] -
2021-05-03 17:17:35.930 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/jobs/job_reoccuring.py", line 27, in _schedule_next_run
2021-05-03 17:17:35.930 [ERROR] [HABApp.Rules ] - 22 def _schedule_next_run(self):
2021-05-03 17:17:35.930 [ERROR] [HABApp.Rules ] - 23 now = get_now(UTC).timestamp()
2021-05-03 17:17:35.930 [ERROR] [HABApp.Rules ] - 24 while self._next_run_base < now:
2021-05-03 17:17:35.930 [ERROR] [HABApp.Rules ] - 25 self._next_run_base += self._interval
2021-05-03 17:17:35.931 [ERROR] [HABApp.Rules ] - 26
2021-05-03 17:17:35.931 [ERROR] [HABApp.Rules ] - --> 27 self._apply_boundaries()
2021-05-03 17:17:35.931 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.931 [ERROR] [HABApp.Rules ] - self = <ReoccurringJob next_run: 2021-05-03T17:17:35.587000>
2021-05-03 17:17:35.931 [ERROR] [HABApp.Rules ] - now = 1620055055.709704
2021-05-03 17:17:35.932 [ERROR] [HABApp.Rules ] - get_now = <function 'now' __init__.py:197>
2021-05-03 17:17:35.932 [ERROR] [HABApp.Rules ] - UTC = Timezone('UTC')
2021-05-03 17:17:35.932 [ERROR] [HABApp.Rules ] - self._next_run_base = 3767538702.5874157
2021-05-03 17:17:35.932 [ERROR] [HABApp.Rules ] - self._interval = 10
2021-05-03 17:17:35.932 [ERROR] [HABApp.Rules ] - self._apply_boundaries = <method 'DateTimeJobBase._apply_boundaries' of <ReoccurringJob next_run: 2021-05-03T17:17:35.587000> job_base_datetime.py:144>
2021-05-03 17:17:35.933 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.933 [ERROR] [HABApp.Rules ] -
2021-05-03 17:17:35.933 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/jobs/job_base_datetime.py", line 148, in _apply_boundaries
2021-05-03 17:17:35.933 [ERROR] [HABApp.Rules ] - 144 def _apply_boundaries(self):
2021-05-03 17:17:35.933 [ERROR] [HABApp.Rules ] - 145 self._adjusting = True
2021-05-03 17:17:35.934 [ERROR] [HABApp.Rules ] - 146
2021-05-03 17:17:35.934 [ERROR] [HABApp.Rules ] - 147 # Starting point is always the next call in local time
2021-05-03 17:17:35.934 [ERROR] [HABApp.Rules ] - --> 148 next_run_local: DateTime = from_timestamp(self._next_run_base, local_tz)
2021-05-03 17:17:35.934 [ERROR] [HABApp.Rules ] - 149
2021-05-03 17:17:35.934 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.935 [ERROR] [HABApp.Rules ] - self = <ReoccurringJob next_run: 2021-05-03T17:17:35.587000>
2021-05-03 17:17:35.935 [ERROR] [HABApp.Rules ] - self._adjusting = True
2021-05-03 17:17:35.935 [ERROR] [HABApp.Rules ] - DateTime = <class 'pendulum.datetime.DateTime'>
2021-05-03 17:17:35.935 [ERROR] [HABApp.Rules ] - self._next_run_base = 3767538702.5874157
2021-05-03 17:17:35.936 [ERROR] [HABApp.Rules ] - local_tz = Timezone('Europe/Stockholm')
2021-05-03 17:17:35.936 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.936 [ERROR] [HABApp.Rules ] -
2021-05-03 17:17:35.936 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/pendulum/__init__.py", line 272, in from_timestamp
2021-05-03 17:17:35.936 [ERROR] [HABApp.Rules ] - 266 def from_timestamp(
2021-05-03 17:17:35.937 [ERROR] [HABApp.Rules ] - 267 timestamp, tz=UTC
2021-05-03 17:17:35.937 [ERROR] [HABApp.Rules ] - 268 ): # type: (Union[int, float], Union[str, _Timezone]) -> DateTime
2021-05-03 17:17:35.937 [ERROR] [HABApp.Rules ] - 269 """
2021-05-03 17:17:35.937 [ERROR] [HABApp.Rules ] - 270 Create a DateTime instance from a timestamp.
2021-05-03 17:17:35.937 [ERROR] [HABApp.Rules ] - 271 """
2021-05-03 17:17:35.938 [ERROR] [HABApp.Rules ] - --> 272 dt = _datetime.datetime.utcfromtimestamp(timestamp)
2021-05-03 17:17:35.938 [ERROR] [HABApp.Rules ] - 273
2021-05-03 17:17:35.938 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.938 [ERROR] [HABApp.Rules ] - timestamp = 3767538702.5874157
2021-05-03 17:17:35.938 [ERROR] [HABApp.Rules ] - tz = Timezone('Europe/Stockholm')
2021-05-03 17:17:35.939 [ERROR] [HABApp.Rules ] - UTC = Timezone('UTC')
2021-05-03 17:17:35.939 [ERROR] [HABApp.Rules ] - ..................................................
2021-05-03 17:17:35.939 [ERROR] [HABApp.Rules ] -
2021-05-03 17:17:35.939 [ERROR] [HABApp.Rules ] - ---- (full traceback above) ----
2021-05-03 17:17:35.939 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/HABApp/rule_manager/rule_file.py", line 80, in load
2021-05-03 17:17:35.940 [ERROR] [HABApp.Rules ] - self.create_rules(created_rules)
2021-05-03 17:17:35.940 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/HABApp/rule_manager/rule_file.py", line 69, in create_rules
2021-05-03 17:17:35.940 [ERROR] [HABApp.Rules ] - '__HABAPP__RULES': created_rules,
2021-05-03 17:17:35.940 [ERROR] [HABApp.Rules ] - File "/usr/lib/python3.7/runpy.py", line 263, in run_path
2021-05-03 17:17:35.941 [ERROR] [HABApp.Rules ] - pkg_name=pkg_name, script_name=fname)
2021-05-03 17:17:35.941 [ERROR] [HABApp.Rules ] - File "/usr/lib/python3.7/runpy.py", line 96, in _run_module_code
2021-05-03 17:17:35.941 [ERROR] [HABApp.Rules ] - mod_name, mod_spec, pkg_name, script_name)
2021-05-03 17:17:35.941 [ERROR] [HABApp.Rules ] - File "/usr/lib/python3.7/runpy.py", line 85, in _run_code
2021-05-03 17:17:35.941 [ERROR] [HABApp.Rules ] - exec(code, run_globals)
2021-05-03 17:17:35.942 [ERROR] [HABApp.Rules ] - File "/etc/openhab/habapp/rules/RunEveryTest.py", line 21, in RunEveryTest.py
2021-05-03 17:17:35.942 [ERROR] [HABApp.Rules ] - RunEveryTest()
2021-05-03 17:17:35.942 [ERROR] [HABApp.Rules ] - File "/etc/openhab/habapp/rules/RunEveryTest.py", line 12, in __init__
2021-05-03 17:17:35.942 [ERROR] [HABApp.Rules ] - self.run.every(None,10,self.test)
2021-05-03 17:17:35.942 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/HABApp/rule/habappscheduler.py", line 41, in every
2021-05-03 17:17:35.943 [ERROR] [HABApp.Rules ] - return super().every(start_time, interval, callback, *args, **kwargs)
2021-05-03 17:17:35.944 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/scheduler_view.py", line 59, in every
2021-05-03 17:17:35.944 [ERROR] [HABApp.Rules ] - job.interval(interval)
2021-05-03 17:17:35.945 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/jobs/job_reoccuring.py", line 45, in interval
2021-05-03 17:17:35.945 [ERROR] [HABApp.Rules ] - self._schedule_next_run()
2021-05-03 17:17:35.945 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/jobs/job_reoccuring.py", line 27, in _schedule_next_run
2021-05-03 17:17:35.946 [ERROR] [HABApp.Rules ] - self._apply_boundaries()
2021-05-03 17:17:35.946 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/eascheduler/jobs/job_base_datetime.py", line 148, in _apply_boundaries
2021-05-03 17:17:35.946 [ERROR] [HABApp.Rules ] - next_run_local: DateTime = from_timestamp(self._next_run_base, local_tz)
2021-05-03 17:17:35.947 [ERROR] [HABApp.Rules ] - File "/opt/habapp/lib/python3.7/site-packages/pendulum/__init__.py", line 272, in from_timestamp
2021-05-03 17:17:35.947 [ERROR] [HABApp.Rules ] - dt = _datetime.datetime.utcfromtimestamp(timestamp)
2021-05-03 17:17:35.947 [ERROR] [HABApp.Rules ] -
2021-05-03 17:17:35.947 [ERROR] [HABApp.Rules ] - OverflowError: timestamp out of range for platform time_t
2021-05-03 17:17:35.953 [WARN ] [HABApp.Rules ] - Failed to load /etc/openhab/habapp/rules/RunEveryTest.py!
@magnuslsjoberg
I have self.run.every(None,...)
working now. I suspect the problem with your situation is something to do with the raspberry pi OS being 32 bit.
Not sure how to fix that though, as self.run.every(0,)
isn't working either. I guess we just wait and see what @spacemanspiff2007 comes up with.
UPDATE: Looks like it's this:
Unix and POSIX-compliant systems implement the time_t type as a signed integer (typically 32 or 64 bits wide) which represents the number of seconds since the start of the Unix epoch: midnight UTC of January 1, 1970 (not counting leap seconds). Some systems correctly handle negative time values, while others do not. Systems using a 32-bit time_t type are susceptible to the Year 2038 problem.
The maximum timestamp on a 32 bit system is 2147483647 (ie 2038), the next_base
given in your traceback is 3767538702.5874157, which is way too big to be a realistic timestamp.
now = 1620055055.592741
self._next_run_base = 3767538702.5874157
self._interval = 2147483647.0
From the traceback, it seems that self._interval
gets added to now
to give next_base
and self._interval
is MAX_INT
Don't know why it does this, but it's obviously a problem when you try to convert it back to a datetime as a timestamp (which is probably expecting an int).
Thanks, I forgot my credentials were in the file.
I always put them in a parameter file, that way I don't accidentally post them as a rule example. :wink:
The maximum timestamp on a 32 bit system is 2147483647 (ie 2038), the
next_base
given in your traceback is 3767538702.5874157, which is way too big to be a realistic timestamp.
I've already nailed the problem down to this, however I'll still have some digging to do and find a nice way to fix it
I guess we just wait and see what @spacemanspiff2007 comes up with.
I don't have much time the next couple of days, so in the meantime you can do
self.run.every(None if val <= 0 else val, interval)
or postpone the migration to HABApp 0.30 until we have further insight (I think I'll have something by Friday - Sunday).
I'll ping both of you once I have something. Thanks for reporting and the analysis!
@NickWaterton @spacemanspiff2007 Thank you both for the quick analysis!
To be honest, using timedelta(seconds=5) is not really any problem in my case. As a side note the old implementation of run_every(None,...
actually had a small bug since it didn't run until the interval time had passed. I therefore had a number of run_soon
just before each run_every
. I no longer need that.
@magnuslsjoberg I've updated the eascheduler to 0.1.2 and updated HABApp to use this. Would you mind updating from the dev branch and see if you issues disappear?
@spacemanspiff2007:
Sorry, I didn't have time to test the dev branch but 0.30.1 works! Thank you!
I still get the error on 0. I’m on 0.30.1.
I realised I only tested with None. Haven’t tested with 0 yet…
On 9 May 2021, at 20:36, Nick Waterton @.***> wrote:
I still get the error on 0. I’m on 0.30.1.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.
I still get the error on 0. I’m on 0.30.1.
Sorrry - I haven't had time to investigate that issue yet.
@NickWaterton @magnuslsjoberg I've made changes to the dev branch. Together with the new eascheduler 0 is now an allowed value. Could you check and see if it's working as expected?
This issue seems stale so I'm closing it.
I just upgraded to 0.30.0
I followed the instructions: • Search for self.run_ and replace with self.run. • Search for self.run.in and replace with self.run.at • Search for .get_next_call() and replace with .get_next_run() (But make sure it's a scheduled job) • Search for HABAppError and replace with HABAppException
But I am running into many errors. The first issue is that HABApp now requires obsolete versions of packages, and if you do not have the exact version required, will not start. Had to downgrade many of my packages, just to get it to run.
eg. HABApp requires ujson==4.0.2, I had 4.0.3 - so won't start. same for aiohttp==3.7.4 (I had 3.7.5). Why not allow aiohttp>=3.7.4 ?
When I finally was able to start, there are many breaking changes not mentioned.
self.run.at
has had the propertyis_finished
removed, which breaks mycreateTimer
class, and causes most of my rules to fail.I am still trying to find a workaround for this. If a job is scheduled, how can I tell if it is pending, running, or has already run? I'm trying to use
.get_next_run()
but I am not sure what this returns if the job has completed.When I cancel the task, if the task was previously cancelled, a new exception is raised
JobAlreadyCanceledException()
which I now have to trap. I see no point in raising this exception.When I create a new
run.every(None,)
task, I get an exceptionFirstRunInThePastError()
which causes the entire rule not to load. What is the point of this exception? why not just run the task immediately? if the task is scheduled now (ie not in the past) this exception breaks the rule.This is the problem:
Also this fails when t==0:
This errors are caused by "/usr/local/lib/python3.8/dist-packages/eascheduler/jobs/job_base.py", line 96:
This is a bug, as
now
is not in the past. The line should be:But again, why? why not just run immediately?
Apparently
on_sun
has been silently dropped (not a big deal, I don't use it).I am still working through these errors, the biggest problem being
task.is_finished
being dropped. Any suggestions on how to re-create this vital property?