Hello,
would it be possible to change the “SHAPE” part to 2 separate shapes? I have a detached garage i would like to include in the image, but have no idea about the needed syntax.
Thanks in advance!
Hello,
would it be possible to change the “SHAPE” part to 2 separate shapes? I have a detached garage i would like to include in the image, but have no idea about the needed syntax.
Thanks in advance!
can someone post a fully working script ive tried to grab bits from a few posts above but only have it “kind of” working
bump
Hi,
I struggle with the same error of missing requests library in pip other people mentioned before
import requests
ImportError: No module named requests
The thing is though request is already installed in python
Requirement already satisfied: requests in /usr/lib/python3/dist-packages (2.18.4)
How can I change the reference of request to pip?
My current ubuntu version is:
Ubuntu: 18.04.2 LTS
Version of python3: 3.6.8
Version of pip: pip 19.1.1 from /python3.6/site-packages/pip (python 3.6)
Any ideas?
Cheers
@Daniel_Hampson
it is because moon elevation is below 0
but there might be issue with pylunar itself as now (0:27) i can see moon on sky physically, but pylunar is saying elevation -9 degrees (which is below horizon)
at same time astro binding from OH is saying 8,95 degrees which would result of moon being displayed.
so I suspect pylunar is having wrong data.
edit: well no, it’s wrongly implemented in script. Because per documentation pylunar requires UTC time, but in here it uses local time.
Corrected and cleaned whole working code is:
(change LAT + LONG ALT + SHAPE and your time eg. Europe/Berlin )
from __future__ import print_function
import math
from datetime import datetime, timedelta, date, time
import sys
import pytz
import pylunar
from astral import Astral
from astral import Location
WIDTH = 100
HEIGHT = 100
PRIMARY_COLOR = '#1b3024'
LIGHT_COLOR = '#26bf75'
BG_COLOR = '#1a1919'
#BG_COLOR = '#555555'
SUN_COLOR = '#ffff66'
SUN_RADIUS = 5
MOON_COLOR = '#999999'
MOON_RADIUS = 3
STROKE_WIDTH = '1'
FILENAME = '/etc/openhab2/html/shaddow.svg'
LATITUDE = XXX
LONGITUDE = XXX
ALTITUDE = XXX
# Shape of the house in a 100 by 100 units square
SHAPE = [{'x': 16.37, 'y': 62.07}, \
{'x': 28.32, 'y': 40.16}, \
{'x': 29.57, 'y': 39.87}]
HOURS = 1
DEGS = []
class shadow(object):
"""
Shadow Object
"""
def __init__(self):
self.debug = False
self.astr = Astral()
timezone = pytz.timezone('Europe/Berlin') # Enter your time zone
self.l = Location(('HOME', 'YOUR TOWN', LATITUDE, LONGITUDE, 'Europe/Berlin', ALTITUDE))
self.sun = self.l.sun()
self.now = timezone.localize(datetime.now())
self.nowUTC = datetime.utcnow()
self.sun_azimuth = float(self.astr.solar_azimuth(self.now, LATITUDE, LONGITUDE))
print('Sun azimuth: ' + str(self.sun_azimuth))
self.sun_elevation = float(self.astr.solar_elevation(self.now, LATITUDE, LONGITUDE))
print('Sun elevation: ' + str(self.sun_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)])
self.moon_info = pylunar.MoonInfo(self.decdeg2dms(LATITUDE), self.decdeg2dms(LONGITUDE))
self.moon_info.update(self.nowUTC)
self.moon_azimuth = self.moon_info.azimuth()
print('Moon azimuth: ' + str(self.moon_azimuth))
self.moon_elevation = self.moon_info.altitude()
print('Moon elevation: ' + str(self.moon_elevation))
if (self.sun_elevation>0):
self.elevation = self.sun_elevation
else:
self.elevation = self.moon_elevation
#
#
#
def decdeg2dms(self,dd):
negative = dd < 0
dd = abs(dd)
minutes,seconds = divmod(dd*3600,60)
degrees,minutes = divmod(minutes,60)
if negative:
if degrees > 0:
degrees = -degrees
elif minutes > 0:
minutes = -minutes
else:
seconds = -seconds
return (degrees,minutes,seconds)
#
#
#
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,fill,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 (fill != None):
p = p + ' fill="' + fill + '" '
else:
p = p + ' fill="none" '
if (attrs != None):
p = p + ' ' + attrs + ' '
else:
p = p + ' stroke-width="' + STROKE_WIDTH + '"'
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_pos = self.degreesToPoint(self.sun_azimuth, 10000)
if self.debug:
print(realSun_pos)
sun_pos = self.degreesToPoint(self.sun_azimuth, WIDTH / 2)
moon_pos = self.degreesToPoint(self.moon_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_pos['y'],point['x']-sun_pos['x']))
#Angle of distant light source (e.g. sun_pos)
angle = -math.degrees(math.atan2(point['y']-realSun_pos['y'],point['x']-realSun_pos['x']))
distance = math.sqrt(math.pow(sun_pos['y']-point['y'],2) + math.pow(sun_pos['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">'
# background
svg = svg + '<circle cx="' + str(WIDTH/2) + '" cy="' + str(HEIGHT/2) + '" r="' + str(WIDTH/2-1) + '" fill="' + BG_COLOR + '"/>'
minPointShadowX = SHAPE[minPoint]['x'] + WIDTH * math.cos(math.radians(minAngle))
minPointShadowY = SHAPE[minPoint]['y'] - HEIGHT * math.sin(math.radians(minAngle))
maxPointShadowX = SHAPE[maxPoint]['x'] + WIDTH * math.cos(math.radians(maxAngle))
maxPointShadowY = SHAPE[maxPoint]['y'] - HEIGHT * math.sin(math.radians(maxAngle))
shadow = [ {'x': maxPointShadowX, 'y': maxPointShadowY } ] + \
side2 + \
[ {'x': minPointShadowX, 'y': minPointShadowY } ]
svg = svg + '<defs><mask id="shadowMask">'
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)
shadow_svg = self.generatePath('none','black',shadow,'mask="url(#shadowMask)" fill-opacity="0.5"')
if (self.elevation>0):
svg = svg + self.generatePath(LIGHT_COLOR,'none',side1)
else:
svg = svg + self.generatePath(PRIMARY_COLOR,'none',side2)
if (self.elevation>0): svg = svg + shadow_svg
svg = svg + self.generateArc(WIDTH/2,PRIMARY_COLOR,'none',self.sunset_azimuth,self.sunrise_azimuth)
svg = svg + self.generateArc(WIDTH/2,LIGHT_COLOR,'none',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,'none',DEGS[i],DEGS[j],'stroke-width="3" stroke-opacity="0.2"')
else:
svg = svg + self.generateArc(WIDTH/2+8,PRIMARY_COLOR,'none',DEGS[i],DEGS[j],'stroke-width="3" ')
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)])
# moon drawing: compute left and right arcs
phase = self.astr.moon_phase(self.now)
if self.debug:
print('phase: ' + str(phase))
left_radius=MOON_RADIUS
left_sweep=0
right_radius=MOON_RADIUS
right_sweep=0
if (phase > 14):
right_radius = MOON_RADIUS - (2.0*MOON_RADIUS* (1.0 - ((phase%14)*0.99 / 14.0)))
if (right_radius < 0):
right_radius = right_radius * -1.0
right_sweep = 0
else:
right_sweep = 1
if (phase < 14):
left_radius = MOON_RADIUS - (2.0*MOON_RADIUS* (1.0 - ((phase%14)*0.99 / 14.0)))
if (left_radius < 0):
left_radius = left_radius * -1.0
left_sweep = 1
if (self.moon_elevation>0):
svg = svg + '<path stroke="none" stroke-width="0" fill="' + MOON_COLOR \
+ '" d="M ' + str(moon_pos['x']) + ' ' + str(moon_pos['y']-MOON_RADIUS) \
+ ' A ' + str(left_radius) + ' ' + str(MOON_RADIUS) + ' 0 0 ' + str(left_sweep) + ' ' + str(moon_pos['x']) + ' ' + str(moon_pos['y']+MOON_RADIUS) \
+ ' ' + str(right_radius) + ' ' + str(MOON_RADIUS) + ' 0 0 ' + str(right_sweep) + ' ' + str(moon_pos['x']) + ' ' + str(moon_pos['y']-MOON_RADIUS) + ' z" />'
# sun drawing
if (self.sun_elevation>0):
svg = svg + '<circle cx="' + str(sun_pos['x']) + '" cy="' + str(sun_pos['y']) + '" r="' + str(SUN_RADIUS) + '" stroke="none" stroke-width="0" fill="' + SUN_COLOR + '55" />'
svg = svg + '<circle cx="' + str(sun_pos['x']) + '" cy="' + str(sun_pos['y']) + '" r="' + str(SUN_RADIUS -1) + '" stroke="none" stroke-width="0" fill="' + SUN_COLOR + '99" />'
svg = svg + '<circle cx="' + str(sun_pos['x']) + '" cy="' + str(sun_pos['y']) + '" r="' + str(SUN_RADIUS -2) + '" stroke="' + SUN_COLOR + '" stroke-width="0" fill="' + SUN_COLOR + '" />'
svg = svg + '</svg>'
if self.debug:
print(svg)
f = open(FILENAME, 'w')
f.write(svg)
f.close()
def main():
t1 = datetime.now()
s = shadow()
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()
I might update that script so moon will cast proper shadow when sun is below horizon… later
so here is a script which after SUN is under horizon (elevation < 0) shows house shadow cast by moon
it’s quick hack and it’s already dark in here so it needs some testing during the day as well
from __future__ import print_function
import math
from datetime import datetime, timedelta, date, time
import sys
import pytz
import pylunar
from astral import Astral
from astral import Location
WIDTH = 100
HEIGHT = 100
PRIMARY_COLOR = '#1b3024'
LIGHT_COLOR = '#26bf75'
BG_COLOR = '#1a1919'
SUN_COLOR = '#ffff66'
SUN_RADIUS = 5
MOON_COLOR = '#999999'
MOON_RADIUS = 3
STROKE_WIDTH = '1'
FILENAME = '/etc/openhab2/html/shaddow.svg'
LATITUDE = XXX
LONGITUDE = XXX
ALTITUDE = XXX
# Shape of the house in a 100 by 100 units square
SHAPE = [{'x': 16.37, 'y': 62.07}, \
{'x': 28.32, 'y': 40.16}, \
{'x': 38.68, 'y': 74.68}]
HOURS = 1
DEGS = []
class shadow(object):
"""
Shadow Object
"""
def __init__(self):
self.debug = False
self.astr = Astral()
timezone = pytz.timezone('Europe/Prague') # Enter your time zone
self.l = Location(('HOME', 'YOUR TOWN', LATITUDE, LONGITUDE, 'Europe/Prague', ALTITUDE))
self.sun = self.l.sun()
self.now = timezone.localize(datetime.now())
self.nowUTC = datetime.utcnow()
self.sun_azimuth = float(self.astr.solar_azimuth(self.now, LATITUDE, LONGITUDE))
print('Sun azimuth: ' + str(self.sun_azimuth))
self.sun_elevation = float(self.astr.solar_elevation(self.now, LATITUDE, LONGITUDE))
print('Sun elevation: ' + str(self.sun_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)])
self.moon_info = pylunar.MoonInfo(self.decdeg2dms(LATITUDE), self.decdeg2dms(LONGITUDE))
self.moon_info.update(self.nowUTC)
self.moon_azimuth = self.moon_info.azimuth()
print('Moon azimuth: ' + str(self.moon_azimuth))
self.moon_elevation = self.moon_info.altitude()
print('Moon elevation: ' + str(self.moon_elevation))
if (self.sun_elevation>0):
self.elevation = self.sun_elevation
else:
self.elevation = self.moon_elevation
#
#
#
def decdeg2dms(self,dd):
negative = dd < 0
dd = abs(dd)
minutes,seconds = divmod(dd*3600,60)
degrees,minutes = divmod(minutes,60)
if negative:
if degrees > 0:
degrees = -degrees
elif minutes > 0:
minutes = -minutes
else:
seconds = -seconds
return (degrees,minutes,seconds)
#
#
#
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,fill,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 (fill != None):
p = p + ' fill="' + fill + '" '
else:
p = p + ' fill="none" '
if (attrs != None):
p = p + ' ' + attrs + ' '
else:
p = p + ' stroke-width="' + STROKE_WIDTH + '"'
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_pos = self.degreesToPoint(self.sun_azimuth, 10000)
realMoon_pos = self.degreesToPoint(self.moon_azimuth, 10000)
if self.debug:
print(realSun_pos)
sun_pos = self.degreesToPoint(self.sun_azimuth, WIDTH / 2)
moon_pos = self.degreesToPoint(self.moon_azimuth, WIDTH / 2)
minPoint = -1
maxPoint = -1
i = 0
minAngle = 999
maxAngle = -999
if(self.sun_elevation>0):
angle_pos = sun_pos
real_pos = realSun_pos
else:
angle_pos = moon_pos
real_pos = realMoon_pos
for point in SHAPE:
#Angle of close light source
angle = -math.degrees(math.atan2(point['y']-angle_pos['y'],point['x']-angle_pos['x']))
#Angle of distant light source (e.g. sun_pos)
angle = -math.degrees(math.atan2(point['y']-real_pos['y'],point['x']-real_pos['x']))
distance = math.sqrt(math.pow(angle_pos['y']-point['y'],2) + math.pow(angle_pos['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">'
# background
svg = svg + '<circle cx="' + str(WIDTH/2) + '" cy="' + str(HEIGHT/2) + '" r="' + str(WIDTH/2-1) + '" fill="' + BG_COLOR + '"/>'
minPointShadowX = SHAPE[minPoint]['x'] + WIDTH * math.cos(math.radians(minAngle))
minPointShadowY = SHAPE[minPoint]['y'] - HEIGHT * math.sin(math.radians(minAngle))
maxPointShadowX = SHAPE[maxPoint]['x'] + WIDTH * math.cos(math.radians(maxAngle))
maxPointShadowY = SHAPE[maxPoint]['y'] - HEIGHT * math.sin(math.radians(maxAngle))
shadow = [ {'x': maxPointShadowX, 'y': maxPointShadowY } ] + \
side2 + \
[ {'x': minPointShadowX, 'y': minPointShadowY } ]
svg = svg + '<defs><mask id="shadowMask">'
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)
shadow_svg = self.generatePath('none','black',shadow,'mask="url(#shadowMask)" fill-opacity="0.5"')
if (self.elevation>0):
svg = svg + self.generatePath(LIGHT_COLOR,'none',side1)
else:
svg = svg + self.generatePath(PRIMARY_COLOR,'none',side2)
if (self.elevation>0): svg = svg + shadow_svg
svg = svg + self.generateArc(WIDTH/2,PRIMARY_COLOR,'none',self.sunset_azimuth,self.sunrise_azimuth)
svg = svg + self.generateArc(WIDTH/2,LIGHT_COLOR,'none',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,'none',DEGS[i],DEGS[j],'stroke-width="3" stroke-opacity="0.2"')
else:
svg = svg + self.generateArc(WIDTH/2+8,PRIMARY_COLOR,'none',DEGS[i],DEGS[j],'stroke-width="3" ')
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)])
# moon drawing: compute left and right arcs
phase = self.astr.moon_phase(self.now)
if self.debug:
print('phase: ' + str(phase))
left_radius=MOON_RADIUS
left_sweep=0
right_radius=MOON_RADIUS
right_sweep=0
if (phase > 14):
right_radius = MOON_RADIUS - (2.0*MOON_RADIUS* (1.0 - ((phase%14)*0.99 / 14.0)))
if (right_radius < 0):
right_radius = right_radius * -1.0
right_sweep = 0
else:
right_sweep = 1
if (phase < 14):
left_radius = MOON_RADIUS - (2.0*MOON_RADIUS* (1.0 - ((phase%14)*0.99 / 14.0)))
if (left_radius < 0):
left_radius = left_radius * -1.0
left_sweep = 1
if (self.moon_elevation>0):
svg = svg + '<path stroke="none" stroke-width="0" fill="' + MOON_COLOR \
+ '" d="M ' + str(moon_pos['x']) + ' ' + str(moon_pos['y']-MOON_RADIUS) \
+ ' A ' + str(left_radius) + ' ' + str(MOON_RADIUS) + ' 0 0 ' + str(left_sweep) + ' ' + str(moon_pos['x']) + ' ' + str(moon_pos['y']+MOON_RADIUS) \
+ ' ' + str(right_radius) + ' ' + str(MOON_RADIUS) + ' 0 0 ' + str(right_sweep) + ' ' + str(moon_pos['x']) + ' ' + str(moon_pos['y']-MOON_RADIUS) + ' z" />'
# sun drawing
if (self.sun_elevation>0):
svg = svg + '<circle cx="' + str(sun_pos['x']) + '" cy="' + str(sun_pos['y']) + '" r="' + str(SUN_RADIUS) + '" stroke="none" stroke-width="0" fill="' + SUN_COLOR + '55" />'
svg = svg + '<circle cx="' + str(sun_pos['x']) + '" cy="' + str(sun_pos['y']) + '" r="' + str(SUN_RADIUS -1) + '" stroke="none" stroke-width="0" fill="' + SUN_COLOR + '99" />'
svg = svg + '<circle cx="' + str(sun_pos['x']) + '" cy="' + str(sun_pos['y']) + '" r="' + str(SUN_RADIUS -2) + '" stroke="' + SUN_COLOR + '" stroke-width="0" fill="' + SUN_COLOR + '" />'
svg = svg + '</svg>'
if self.debug:
print(svg)
f = open(FILENAME, 'w')
f.write(svg)
f.close()
def main():
t1 = datetime.now()
s = shadow()
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()
I stuck with the convert error of Sunrise_Azimuth Item
16:11:35.640 [INFO ] [clipse.smarthome.model.script.Shaddow] - Successfully got state from OpenHab: Sun_Azimuth
Successfully got state from OpenHab: Sun_Elevation
Successfully got state from OpenHab: Sunrise_Azimuth
Traceback (most recent call last):
File “/etc/openhab2/scripts/shaddow.py”, line 249, in
main()
File “/etc/openhab2/scripts/shaddow.py”, line 235, in main
s = shaddow()
File “/etc/openhab2/scripts/shaddow.py”, line 46, in init
self.sunrise_azimuth = float(self.oh.getState(‘Sunrise_Azimuth’))
ValueError: could not convert string to float: NULL
values from influxdb looks good, since the script is running for a few days now:
select * from “Sun_Azimuth”
name: Sun_Azimuth
time value
…
…
…
1563058800302000000 353.4056682666371
1563062400012000000 7.785783228662439
1563066000012000000 21.899547402426805
the Sun_Azimuth ItemChangedEvent returns:
16:11:35.060 [INFO ] [smarthome.event.ItemStateChangedEvent] - Sun_Azimuth changed from 268.235249080293 to 269.24698230499183
astro.items
Number Sun_Azimuth “Azimuth” (Astro) { channel=“astro:sun:home:position#azimuth” }
Number Sunrise_Azimuth
Number Sunset_Azimuth
astro.things
astro:sun:home [ geolocation=“53.385087, 10.549860,100”, interval=300 ]
astro:moon:home [ geolocation=“53.385087, 10.549860”, interval=300 ]
My guess the sunset and sunrise rule are not triggert, but don’t know what’s wrong here??
rule “Sunset Rule”
when
Channel ‘astro:sun:local:set#event’ triggered START
thenpostUpdate(Sunset_Azimuth,Sun_Azimuth.state) logInfo("Shaddow","Setting Sunset Azimuth.")
end
rule “Sunrise Rule”
when
Channel ‘astro:sun:local:rise#event’ triggered START
thenpostUpdate(Sunrise_Azimuth,Sun_Azimuth.state) logInfo("Shaddow","Setting Sunrise Azimuth.")
end
Hope some one could help me with this
Cheers
astro.things file content?
Astro binding is loaded (karaf bundle list -s | grep astro)?
use latest code, which does not need any calculations from persistence
I’m going to do the same thing as soon as possible!!
So your work is really really useful for me!
In final, my goal is to make OH2 alerts people in my home that the system is about to close or open the blinds …
Bye!
astro.things:
astro:sun:home [ geolocation=“XXX, XXX”, interval=300 ]
astro:moon:home [ geolocation=“XXX, XXX”, interval=300 ]
the astro binding is active
openhab> bundle:list
START LEVEL 100 , List Threshold: 50
ID │ State │ Lvl │ Version │ Name196 │ Active │ 80 │ 0.10.0.oh240 │ Eclipse SmartHome Astro Binding
weird I didn’t get it
hope you guys see something I’m missing here
really appreciate your help, thanks so far
@kriznik
yes this works but it makes my whole work with influxdb and grafana needless:unamused:
With this script I’m 2 hours behind astral.
My local time is UTC + 2, any ideas how to change the shaddow.py
@kriznik
I use the script from this post and filled the constants with values from SunCalc pointing to my location. I changed the timezone to
timezone = pytz.timezone(‘Europe/Berlin’)
But the sun position in the svg is UTC and not TZ: Europe/Berlin DST CEST. It should be UTC + 2 (due to the dst). The timezone object is localized, this should be working
for comparison
svg.
14:36:35.992 [INFO ] [clipse.smarthome.model.script.Shaddow] - 2019-07-21 14:36:35.793169
Sun azimuth: 209.804400066
Sun elevation: 54.1718247517
Moon azimuth: 354.069520686
Moon elevation: -45.2328082501
2019-07-21 14:36:35.923135
Done in 0:00:00.129966 seconds
and what exactly is wrong?
be aware that yellow dot is not representing sun position in relation to your house, but to the sky sunrise/sunset and hours.
especially at the evening it might look off, but it’s not. Shadow cast is calculated from distant light source so it’s very precise, sun is calculated from close light source because of the circle (and we know that earth is not doing perfect circle around the sun, right? )
have you changed as well
self.l = Location(('HOME', 'YOUR TOWN', LATITUDE, LONGITUDE, 'Europe/Berlin', ALTITUDE))
?
yes I have changed the parameter in the location function to my timezone (‘Europe/Berlin’).
Now checked the position of my house on SunCalc and it is not quite in the correct position as I coordinated it in shaddow.py. Ahh just 6-10 degree to the right and the rest is owed to the earth wobbling orbital ellipse.
I copied the position from google maps and it is not the same like on SunCalc.
Sorry for the trouble and thanks for physics extra lesson, I greatly appreciate it.
Keep going here, i love your great stuff
@kriznik can you please verify the self.now
varaible in shaddow.py.
self.now = timezone.localize(datetime.now(),is_dst=True)
print(self.now)
due to the fact that datetime is server datetime (utc) the varibale self.now
will always be in utc.
I guess the datetime.now() needs the timezone argument
dt_utc = datetime.datetime.now(datetime.timezone.berlin)
but I didn’t get it to work
why you have OH server in UTC?
self.now = timezone.localize(datetime.now())
self.nowUTC = datetime.utcnow()
I guess you have to adjust above code to something like
TimeZone timeZone = TimeZone.getTimeZone(“Europe/Berlin”)
self.setTimeZone(timeZone)
…
…
Are the files from the first post already updated with the new version without the need of any persistence (influxdb)?
this is the latest updated version I guess
why you have OH server in UTC?
I dont’t know, after I had setted up the system I adjusted the regional settings in Paper UI.
Seems that this isn’t enough.
Now I added to EXTRA_JAVA_OPTS = -Duser.timezone=Europe/Berlin
without success after reboot
TimeZone timeZone = TimeZone.getTimeZone(“Europe/Berlin”)
^
SyntaxError: invalid syntax
I also tried some datetime and timezone converting I found in the python libraires, without success.
I’m lost here, …again