There's IMO a missing feature in Domoticz: Ability to invalidate plannings by device. Combined with the fact that 1 planning in a list = 1 timer idx, there may be lot's to change.
I use this kind of stuff to swith off heaters & invalidate plannings when I use my chimney: In fact, heaters will regulate and heat less when another source of heat is on but I wanted them not to witch on at all as i's really a waste of eletric power.
So this python script (in domoticz/scripts, named scheduleMod.py) that lists all schedules from domoticz, filter those that must be invalidated by device name (wildcards accepted, so if you have a naming prefix convention like heatersXXX, you can invalidate all schedules for them using heater* as a name), and change planning active settings:
Code: Select all
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Schedule activation modification for device (with wildcards support)
# Date for state change, if an 'hour' parameter is given, is stored in
# a user var than can be checked by a lua time script that'll reverse current state...
#
# Changelog : 17/12/2016, YL, 1st version.
import getopt
import logging
import json
import sys
import requests
import datetime
from fnmatch import fnmatch
#####################
# EDITABLE SETTINGS #
#####################
logLevel='INFO' # DEBUG / INFO
# Domoticz json API url
dmtJurl = 'http://127.0.0.1:8080/json.htm?'
# Command parameters in json format (only change if API change!)
dmtJsonGetSched = {"type":"schedules"}
dmtJsonActTimer = {"type":"command", "param":"enabledisabletimer", "idx":"999"}
dmtJsonUpdVar = {"type":"command", "param":"updateuservariable",
"vname":"reverseSchedule", "vtype":2, "vvalue":0}
# Dmoticz time format on LUA side:
dmtLuaTimeFmt = "%Y-%m-%d %H:%M:%S"
#####################
def usage():
"""
Display usage
"""
sys.stderr.write( "Usage: ScheduleMod.py [-h] [-s<on/off>] [-t<timeInMn>] -n<device>\n")
sys.stderr.write( " If no 's', current state is reversed.\n")
sys.stderr.write( " If 't', a time for state reversal (mn) is stored for domoticz.\n")
#####################
def dmtJsonApi(url, jsonApiCmd, logger):
"""
Send Domoticz json command
"""
try:
# Connect to Domoticz via JSON API and send data
dmtRget=requests.get(url, params=jsonApiCmd)
except requests.exceptions.RequestException as dmtErr:
logger.log(logging.ERROR, "Unable to connect with URL=%s \nGet requests error %s" % (dmtRget.url, dmtErr))
finally:
logger.log(logging.DEBUG, "Sent data: [%s]" % (dmtRget.url))
return dmtRget.json()
#####################
def main(argv):
"""
Main
"""
logging.basicConfig()
logger = logging.getLogger()
handler = logging.StreamHandler(sys.stdout)
# Checks the parameters
try:
opts, args = getopt.getopt(argv, "h:s:n:t:",["help","state","name","time"])
except getopt.GetoptError:
usage()
sys.exit(2)
# Defaults
SchedActivate = 'reverse'
devName = 'nil'
reverseTimeMn = 0
for o, a in opts:
if o in ("-h", "--help"):
usage()
sys.exit()
if o in ("-s", "--state" ):
SchedActivate=('disabletimer','enabletimer')[a == 'on']
if o in ("-n", "--name" ):
devName=a
if o in ("-t", "--time" ):
reverseTimeMn=int(a)
# Configure the logger
handler.setFormatter(
logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger.setLevel(logLevel)
logger.log(logging.INFO, "Device(s)=%s, update schedules to '%s', reverse after %dmn." %(devName, SchedActivate, reverseTimeMn))
# Get all schedules
schedules = dmtJsonApi(dmtJurl, dmtJsonGetSched, logger)
#logger.log(logging.DEBUG, "Schedules: [%s]" % (schedules))
# Modify active setting for dev name...
schedNb=len(schedules['result'])
logger.log(logging.DEBUG, "Found %d schedules, extract those for %s" % (schedNb, devName))
# Change schedule(s) for device(s) matching name (wildcard support):
for sched in schedules['result']:
if (fnmatch(sched['DevName'], devName)):
logger.log(logging.DEBUG, 'Found : %s , state=%s, idx=%d',
sched['DevName'],
sched['Active'],
sched['TimerID'])
curState = ('disabletimer','enabletimer')[sched['Active'] == 'true']
revState = ('enabletimer','disabletimer')[sched['Active'] == 'true']
if (curState != SchedActivate):
# Update domoticz request parameters
dmtJsonActTimer['param'] = (SchedActivate, revState)[SchedActivate == 'reverse']
dmtJsonActTimer['idx'] = sched['TimerID']
# Send request...
dmtJsonApi(dmtJurl, dmtJsonActTimer, logger)
# Set user var to current date + time given for reversal (in mn), if any...
if (reverseTimeMn != 0):
revDate = datetime.datetime.now() + datetime.timedelta(minutes=reverseTimeMn)
revDate = devName + ';' + revDate.strftime(dmtLuaTimeFmt)
logger.log(logging.DEBUG, "Reverse time stored for Domoticz : %s", revDate)
dmtJsonUpdVar['vvalue'] = revDate
dmtJsonApi(dmtJurl, dmtJsonUpdVar, logger)
else:
dmtJsonUpdVar['vvalue'] = 'nil'
dmtJsonApi(dmtJurl, dmtJsonUpdVar, logger)
# Happy ending!
logger.log(logging.INFO, "DONE !")
if __name__ == "__main__":
main(sys.argv[1:])
It will store data in the form "device name";"date in same format as LUA".
Reason: Be able to reverse planning activations automagically, from a lua time script, when stored date is reached. This reverse date is set according to a -tTimeInMn parameter, if one is given.
This is done by this script (in domoticz/scripts/lua):
Code: Select all
-- Do stop action for reverseSchedule posted user variable (device ; date_to_reverse_schedule)
commandArray = {}
dev_date = uservariables['reverseSchedule']
dev, sT2 = dev_date:match("([^,]+);([^,]+)")
if (dev_date ~= 'nil') then
year = string.sub(sT2, 1, 4)
month = string.sub(sT2, 6, 7)
day = string.sub(sT2, 9, 10)
hour = string.sub(sT2, 12, 13)
minutes = string.sub(sT2, 15, 16)
seconds = string.sub(sT2, 18, 19)
-- print("sT2:"..sT2)
-- print("y:"..year.." m:"..month.." d:"..day.." h:"..hour.." m:"..minutes.." s:"..seconds)
t2 = os.time{year=year, month=month, day=day, hour=hour, min=minutes, sec=seconds}
t1 = os.time()
-- print("t1:"..t1)
-- print("t2:"..t2)
diffTime = os.difftime(t2, t1)
if (diffTime <= 0) then
print("Restart schedule(s) for "..dev)
commandArray['Heaters Temp Stop'] = 'Off'
commandArray['Variable:reverseSchedule'] = 'nil'
--os.execute("/home/pi/domoticz/scripts/scheduleMod.py -son -n"..dev)
end
end
return commandArray
As it's impossible to give a python in a script:// from a switch action, there's a batch script to encapsulate python script (heatersStopTime.sh):
Code: Select all
#!/bin/bash
if [ ${1} -ne 0 ];
then
/home/pi/domoticz/scripts/scheduleMod.py -nHeaters* -soff -t${1}
setLevel=5
else
/home/pi/domoticz/scripts/scheduleMod.py -nHeaters* -son
setLevel=15
fi
curl "http://127.0.0.1:8080/json.htm?type=command¶m=switchlight&idx=20&switchcmd=Set%20Level&level=${setLevel}"
curl "http://127.0.0.1:8080/json.htm?type=command¶m=switchlight&idx=5&switchcmd=Set%20Level&level=${setLevel}"
curl "http://127.0.0.1:8080/json.htm?type=command¶m=switchlight&idx=7&switchcmd=Set%20Level&level=${setLevel}"
curl "http://127.0.0.1:8080/json.htm?type=command¶m=switchlight&idx=70&switchcmd=Set%20Level&level=${setLevel}"
curl "http://127.0.0.1:8080/json.htm?type=command¶m=switchlight&idx=11&switchcmd=Set%20Level&level=${setLevel}"
curl "http://127.0.0.1:8080/json.htm?type=command¶m=switchlight&idx=24&switchcmd=Set%20Level&level=${setLevel}"
Last 'curl' lines are heater control specific (qubino modules seen as dimmers that gives up to 6 orders to heater). Don't forget to update /home/pi pathes in all scripts if your user is not pi (as usual on raspberry pi), as well as idx/curl lines if you have exactly the same use-case as mine.
After this just calling this script in the selector switch actions like this (for 3h00=180mn; use 0 for off action):
script:///home/pi/domoticz/scripts/heatersStopTime.sh 180
The huge limitation of this first version is only one "device;date for reverse" is stored by python script and can be used by lua side: So it's only one group of devices at a time that can have schedules invalidated and valid again after a time automagically. Feel free to update this, at the moment I have no need for this + I'm an early beginner in python and lua + not so good at bash! Indeed, having the possibility to do everything in python would have made it easier as well! There's the possibility to compile domoticz with python support but I did not compiled last stable myself.
So one use using -t parameter at a time.
Well, that's all folks. This may be useful to others even if solving this schedules group activations hassle directly from domoticz core would be nice!