Hi Vincent,
Just updated the script with the “print (ts)”, and here’s the log output:
python /etc/openhab2/scripts/shaddow.py update
1522389308.06
120.558989318
21.6981129979
Done in 0.64107298851 seconds
Hi Vincent,
Just updated the script with the “print (ts)”, and here’s the log output:
python /etc/openhab2/scripts/shaddow.py update
1522389308.06
120.558989318
21.6981129979
Done in 0.64107298851 seconds
Ok, now compare the 1522389308
with the actual time
This number is a unix timestamp, you can paste it in
https://www.unixtimestamp.com/
Do it again with a “fresh” value
I see I learn new stuff everytime i visit this forum. It’s great!!
So, I pasted a fresh value in the link provided.
My value= 1522407311.87
Converted = 03/30/2018 @ 10:55am (UTC)
Local time when executed= 12:55am
So It seems I’m 2 hours behind in the astral setup.
Thanks
Chris
Cool, 2 hours…
Now change the script as follow a little bit further down in the code:
...
self.sunrise_azimuth = float(self.astr.solar_azimuth(self.sun['sunrise'], LATITUDE, LONGITUDE))
self.sunset_azimuth = float(self.astr.solar_azimuth(self.sun['sunset'], LATITUDE, LONGITUDE))
utc_offset = (datetime.fromtimestamp(ts) - datetime.utcfromtimestamp(ts)).total_seconds()/3600
print(utc_offset)
for h in xrange(0,24,HOURS):
t = datetime.combine(date.today(), datetime.min.time()) + timedelta(hours=-utc_offset+h-24)
...
add the print(utc_offset)
to check it. It should be 2 in your case but I suspect, it’s only 1
Here are my results after adding the “print(utc_offset)”
python /etc/openhab2/scripts/shaddow4.py update
1522421206.59
263.313925336
9.08693431761
2.0
Done in 0.615770101547 seconds
Very odd
I am starting to suspect there is a bug in astral
Let me sleep on it
okay. Thanks for your time and help.
Looking forward to hear from you, and possibly a clever solution
Hi Vincent. Not to push or anything, but I’m just curious if you’ve though some more on my little astral issue?
Sorry, easter week-end and all.
I was wondering, perhaps wrongly, but just to make sure…
From your post #84, there is 30d difference betwwen what astral gives you and what OH astro binding gives.
30 degrees is 2 hours of sun movement, give or take.
So either astral is wrong or OH is wrong…
So I have checked in suncalc.org for the 26/03/2018 at 19:38 and it appears that astral is wrong… Mmmmmh… By 2 hours… Mmmmmmmmmmhhhhh…
So let’s try to cheat astral
change that…
self.debug = False
self.oh = openhab()
self.astr = Astral()
self.l = Location(('HOME', 'WATFORD', LATITUDE, LONGITUDE, 'Europe/London', ALTITUDE))
self.sun = self.l.sun()
ts = time.time() - (2 * 86400)
self.azimuth = float(self.astr.solar_azimuth(datetime.fromtimestamp(ts), LATITUDE, LONGITUDE))
print(self.azimuth)
self.elevation = float(self.astr.solar_elevation(datetime.fromtimestamp(ts), LATITUDE, LONGITUDE))
print(self.elevation)
We basically take 2 hours off the time we inject into astral
What happens?
Yeah, easter holiday makes it much harder to get time, for my home automation hobbies But I have finally got around and tested your solution. Unfortunately there is no change though. I also tried to force reinstall the astral libraries. I can also confirm that OH2 has the right astro data, and astral is off by 2 hours.
It seems that whatever i put in the location parameters, there are no changes in the output data:
self.l = Location(('HOME', 'XXXX', LATITUDE, LONGITUDE, 'Europe/XXXXX', ALTITUDE))
So with your original location data, WATFORD and Europe/London, That should be 1 hour difference from Oslo. But no change if I use Oslo or London
Try that:
self.debug = False
self.oh = openhab()
self.astr = Astral()
self.l = Location(('HOME', 'WATFORD', LATITUDE, LONGITUDE, 'Europe/London', ALTITUDE))
self.sun = self.l.sun()
ts = time.time()
print(ts)
ts = ts - (2 * 86400)
print(ts)
self.azimuth = float(self.astr.solar_azimuth(datetime.fromtimestamp(ts), LATITUDE, LONGITUDE))
print(self.azimuth)
self.elevation = float(self.astr.solar_elevation(datetime.fromtimestamp(ts), LATITUDE, LONGITUDE))
print(self.elevation)
What does your log show?
I’m also using Astral now and I also encountered the issue with the wrong timezone.
I found in the Astral documentation that you need to localize your timestamps by using pytz.
(see https://astral.readthedocs.io/en/latest/#note-on-localized-timezones)
This is my init function:
def __init__(self):
self.debug = False
self.astr = Astral()
timezone = pytz.timezone('Europe/Brussels')
self.l = Location(('HOME', 'TOWN', LATITUDE, LONGITUDE, 'Europe/Brussels', ALTITUDE)) # The HOME and TOWN values are not important. Replace Europe/London with your local time string
self.sun = self.l.sun()
now = timezone.localize(datetime.now())
self.azimuth = float(self.astr.solar_azimuth(now, LATITUDE, LONGITUDE))
self.elevation = float(self.astr.solar_elevation(now, LATITUDE, LONGITUDE))
self.sunrise_azimuth = float(self.astr.solar_azimuth(self.sun['sunrise'], LATITUDE, LONGITUDE))
self.sunset_azimuth = float(self.astr.solar_azimuth(self.sun['sunset'], LATITUDE, LONGITUDE))
for i in range(0,24):
a = float(self.astr.solar_azimuth(timezone.localize(datetime.combine(date.today(), time(i))), LATITUDE, LONGITUDE))
if (a == None): a = 0
DEGS.append(float(a))
It is possible that you will need to import pytz and some parts of datetime (if not imported yet):
from datetime import datetime, date, time
import pytz
Maybe you also need to install pytz via pip.
By using the localize() the timestamp will be set to the correct timezone.
I compared the generated output and it matches the “old” method that uses OpenHAB data.
I hope this helps!
Greetings,
Frederic
Hi @vzorglub and @FredericMa,
I tried to replace my existing code with yours, Frederic. But now I get this error:
python /etc/openhab2/scripts/shaddow.py
Traceback (most recent call last):
File "/etc/openhab2/scripts/shaddow.py", line 37, in <module>
class shaddow(object):
File "/etc/openhab2/scripts/shaddow.py", line 46, in shaddow
self.l = Location(('HOME', 'TOWN', LATITUDE, LONGITUDE, 'Europe/Oslo', ALTITUDE))
NameError: name 'self' is not defined
Works for me! Thanks
But I had to make a few changes:
from __future__ import print_function
import math
# import time
from datetime import datetime, timedelta, date, time
import sys
import pytz
from astral import Astral
from astral import Location
def __init__(self):
self.debug = False
self.oh = openhab()
self.astr = Astral()
timezone = pytz.timezone('Europe/London')
self.l = Location(('HOME', 'WATFORD', LATITUDE, LONGITUDE, 'Europe/London', ALTITUDE))
self.sun = self.l.sun()
now = timezone.localize(datetime.now())
self.azimuth = float(self.astr.solar_azimuth(now, LATITUDE, LONGITUDE))
self.elevation = float(self.astr.solar_elevation(now, LATITUDE, LONGITUDE))
self.sunrise_azimuth = float(self.astr.solar_azimuth(self.sun['sunrise'], LATITUDE, LONGITUDE))
self.sunset_azimuth = float(self.astr.solar_azimuth(self.sun['sunset'], LATITUDE, LONGITUDE))
for i in xrange(0,24,HOURS):
a = float(self.astr.solar_azimuth(timezone.localize(datetime.combine(date.today(), time(i))), LATITUDE, LONGITUDE))
t1 = datetime.now()
...
t2 = datetime.now()
Note that I have removed the print statements
Change 'Europe/London'
with 'London/Olso'
as required
Did you check that pytz was installed?
sudo pip install pytz
Did you add the statement import pytz
at the top of the script?
Woeps, forgot those 2 indeed… I already got rid of the time import since I removed the duration calculation a while ago.
@sjef86 have you copied the exact code? Maybe something went wrong while copying and pasting?
@FredericMa
Hi there, thanks for you good find indeed!!
I copied the code manually, I don’t like copying and pasting because you don’t get inside the code and get to learn and understand it.
I got it to work after a few tries.
@sjef86
With python be very careful with trailing spaces at the end of lines and with your indents
Thanks again…
Thanks guys! I finally got it working here having some indent problems after copy/paste. I added:
t1 = datetime.now()
...
t2 = datetime.now()
And now everything is working great!!
Thanks once more
Br
Chris
diffToFasade was bedeuted das genau? Was muss ich eintragen ? Eine haus seite zeigt genau nach norden bei mir.
The whole code without persistence and timezone corrected
You will need the Astral library (pip install Astral) and the pytz library (sudo pip install pytz)
svg generated is in the shared html folder
from __future__ import print_function
import math
# import time
from datetime import datetime, timedelta, date, time
import sys
import pytz
from astral import Astral
from astral import Location
from myopenhab import openhab
from myopenhab import mapValues
from myopenhab import getJSONValue
WIDTH = 100
HEIGHT = 100
PRIMARY_COLOR = '#1b3024'
LIGHT_COLOR = '#26bf75'
STROKE_WIDTH = '1'
FILENAME = '/etc/openhab2/html/shaddow.svg'
LATITUDE = XX.XXXXXX # Your Latitude
LONGITUDE = XX.XXXXX # Your Longitude
ALTITUDE = XXX # Your Altitude
# Shape of the house in a 100 by 100 units square
SHAPE = [{'x': 23.18, 'y': 19.35}, \
{'x': 70.11, 'y': 15.53}, \
{'x': 75.32, 'y': 52.49}, \
{'x': 79.96, 'y': 52.38}, \
{'x': 80.65, 'y': 65.59}, \
{'x': 76.83, 'y': 66.05}, \
{'x': 78.68, 'y': 77.64}, \
{'x': 31.52, 'y': 82.16}, \
{'x': 30.13, 'y': 73.24}, \
{'x': 18.54, 'y': 74.63}, \
{'x': 14.72, 'y': 46.70}, \
{'x': 26.07, 'y': 44.61}]
HOURS = 1
DEGS = []
class shaddow(object):
"""
Shaddow Object
"""
def __init__(self):
self.debug = False
self.oh = openhab()
self.astr = Astral()
timezone = pytz.timezone('Europe/London') # Enter your time zone
self.l = Location(('HOME', 'TOWN', LATITUDE, LONGITUDE, 'Europe/London', ALTITUDE))
self.sun = self.l.sun()
now = timezone.localize(datetime.now())
self.azimuth = float(self.astr.solar_azimuth(now, LATITUDE, LONGITUDE))
print(self.azimuth)
self.elevation = float(self.astr.solar_elevation(now, LATITUDE, LONGITUDE))
print(self.elevation)
self.sunrise_azimuth = float(self.astr.solar_azimuth(self.sun['sunrise'], LATITUDE, LONGITUDE))
self.sunset_azimuth = float(self.astr.solar_azimuth(self.sun['sunset'], LATITUDE, LONGITUDE))
for i in xrange(0, 24, HOURS):
a = float(self.astr.solar_azimuth(timezone.localize(datetime.combine(date.today(), time(i))), LATITUDE, LONGITUDE))
if (a == None): a = 0
DEGS.extend([float(a)])
def generatePath(self,stroke,fill,points,attrs=None):
p = ''
p = p + '<path stroke="' + stroke + '" stroke-width="' + STROKE_WIDTH + '" fill="' + fill + '" '
if (attrs != None): p = p + ' ' + attrs + ' '
p = p + ' d="'
for point in points:
if (points.index(point) == 0):
p = p + 'M' + str(point['x']) + ' ' + str(point['y'])
else:
p = p + ' L' + str(point['x']) + ' ' + str(point['y'])
p = p + '" />'
return p
def generateArc(self,dist,stroke,start,end,attrs=None):
p = ''
try:
angle = end-start
if (angle<0):
angle = 360 + angle
p = p + '<path d="M' + str(self.degreesToPoint(start,dist)['x']) + ' ' + str(self.degreesToPoint(start,dist)['y']) + ' '
p = p + 'A' + str(dist) + ' ' + str(dist) + ' 0 '
if (angle<180):
p = p + '0 1 '
else:
p = p + '1 1 '
p = p + str(self.degreesToPoint(end,dist)['x']) + ' ' + str(self.degreesToPoint(end,dist)['y']) + '"'
p = p + ' stroke="' + stroke + '"'
if (attrs != None):
p = p + ' ' + attrs + ' '
else:
p = p + ' stroke-width="' + STROKE_WIDTH + '" fill="none" '
p = p + ' />'
except:
p = ''
return p
def degreesToPoint(self,d,r):
coordinates = {'x': 0, 'y': 0}
cx = WIDTH / 2
cy = HEIGHT / 2
d2 = 180 - d
coordinates['x'] = cx + math.sin(math.radians(d2))*r
coordinates['y'] = cy + math.cos(math.radians(d2))*r
return coordinates
def generateSVG(self):
realSun = self.degreesToPoint(self.azimuth, 10000)
if self.debug:
print(realSun)
sun = self.degreesToPoint(self.azimuth, WIDTH / 2)
minPoint = -1
maxPoint = -1
i = 0
minAngle = 999
maxAngle = -999
for point in SHAPE:
#Angle of close light source
angle = -math.degrees(math.atan2(point['y']-sun['y'],point['x']-sun['x']))
#Angle of distant light source (e.g. sun)
angle = -math.degrees(math.atan2(point['y']-realSun['y'],point['x']-realSun['x']))
distance = math.sqrt(math.pow(sun['y']-point['y'],2) + math.pow(sun['x']-point['x'],2))
if (angle<minAngle):
minAngle = angle
minPoint = i
if (angle>maxAngle):
maxAngle = angle
maxPoint = i
point['angle'] = angle
point['distance'] = distance
if self.debug:
print(str(i).ljust(10),":", str(point['x']).ljust(10), str(point['y']).ljust(10), str(round(angle,7)).ljust(10), str(round(distance)).ljust(10))
i = i + 1
if self.debug:
print("Min Point = ",minPoint)
print("Max Point = ",maxPoint)
print("")
i = minPoint
k = 0
side1Distance = 0
side2Distance = 0
side1Done = False
side2Done = False
side1 = []
side2 = []
while True:
if (side1Done == False):
side1Distance = side1Distance + SHAPE[i]['distance']
if(i != minPoint and i != maxPoint): SHAPE[i]['side'] = 1
if (i == maxPoint): side1Done = True
side1.append( { 'x': SHAPE[i]['x'], 'y': SHAPE[i]['y'] } )
if (side1Done == True):
side2Distance = side2Distance + SHAPE[i]['distance']
if(i != minPoint and i != maxPoint): SHAPE[i]['side'] = 2
if (i == minPoint): side2Done = True
side2.append( { 'x': SHAPE[i]['x'], 'y': SHAPE[i]['y'] } )
i = i + 1
if( i > len(SHAPE)-1): i = 0
if (side1Done and side2Done): break
k = k + 1
if (k == 20): break
svg = '<?xml version="1.0" encoding="utf-8"?>'
svg = svg + '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'
svg = svg + '<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="-10 -10 120 120" xml:space="preserve">'
minPointShaddowX = SHAPE[minPoint]['x'] + WIDTH * math.cos(math.radians(minAngle))
minPointShaddowY = SHAPE[minPoint]['y'] - HEIGHT * math.sin(math.radians(minAngle))
maxPointShaddowX = SHAPE[maxPoint]['x'] + WIDTH * math.cos(math.radians(maxAngle))
maxPointShaddowY = SHAPE[maxPoint]['y'] - HEIGHT * math.sin(math.radians(maxAngle))
shaddow = [ {'x': maxPointShaddowX, 'y': maxPointShaddowY } ] + \
side2 + \
[ {'x': minPointShaddowX, 'y': minPointShaddowY } ]
svg = svg + '<defs><mask id="shaddowMask">'
svg = svg + ' <rect width="100%" height="100%" fill="black"/>'
svg = svg + ' <circle cx="' + str(WIDTH/2) + '" cy="' + str(HEIGHT/2) + '" r="' + str(WIDTH/2-1) + '" fill="white"/>'
svg = svg + '</mask></defs>'
svg = svg + self.generatePath('none',PRIMARY_COLOR,SHAPE)
shaddow_svg = self.generatePath('none','black',shaddow,'mask="url(#shaddowMask)" fill-opacity="0.5"')
if (self.elevation>0):
svg = svg + self.generatePath(LIGHT_COLOR,'none',side1)
else:
svg = svg + self.generatePath(PRIMARY_COLOR,'none',side1)
if (self.elevation>0): svg = svg + shaddow_svg
svg = svg + self.generateArc(WIDTH/2,PRIMARY_COLOR,self.sunset_azimuth,self.sunrise_azimuth)
svg = svg + self.generateArc(WIDTH/2,LIGHT_COLOR,self.sunrise_azimuth,self.sunset_azimuth)
svg = svg + self.generatePath(LIGHT_COLOR,'none',[self.degreesToPoint(self.sunrise_azimuth,WIDTH/2-2), self.degreesToPoint(self.sunrise_azimuth,WIDTH/2+2)])
svg = svg + self.generatePath(LIGHT_COLOR,'none',[self.degreesToPoint(self.sunset_azimuth,WIDTH/2-2), self.degreesToPoint(self.sunset_azimuth,WIDTH/2+2)])
for i in range(0,len(DEGS)):
if (i == len(DEGS)-1):
j = 0
else:
j = i + 1
if (i % 2 == 0):
svg = svg + self.generateArc(WIDTH/2+8,PRIMARY_COLOR,DEGS[i],DEGS[j],'stroke-width="3" fill="none" stroke-opacity="0.2"')
else:
svg = svg + self.generateArc(WIDTH/2+8,PRIMARY_COLOR,DEGS[i],DEGS[j],'stroke-width="3" fill="none"')
svg = svg + self.generatePath(LIGHT_COLOR,'none',[self.degreesToPoint(DEGS[0],WIDTH/2+5), self.degreesToPoint(DEGS[0],WIDTH/2+11)])
svg = svg + self.generatePath(LIGHT_COLOR,'none',[self.degreesToPoint(DEGS[(len(DEGS))/2],WIDTH/2+5), self.degreesToPoint(DEGS[(len(DEGS))/2],WIDTH/2+11)])
svg = svg + '<circle cx="' + str(sun['x']) + '" cy="' + str(sun['y']) + '" r="3" stroke="' + LIGHT_COLOR + '" stroke-width="' + STROKE_WIDTH + '" fill="' + LIGHT_COLOR + '" />'
svg = svg + '</svg>'
if self.debug:
print(svg)
f = open(FILENAME, 'w')
f.write(svg)
f.close()
def main():
t1 = datetime.now()
s = shaddow()
args = sys.argv
if(len(args) == 1):
dummy = 0
#print('\033[91mNo parameters specified\033[0;0m')
else:
if(args[1] == "update"):
s.generateSVG()
t2 = datetime.now()
#print("Done in " + str(t2-t1) + " seconds")
if __name__ == '__main__':
main()