diff --git a/.gitignore b/.gitignore index 56b4b5c..e695ee4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ *.swp *.tgz *~ - +lib/ +package/ +package-to-manifest.py \ No newline at end of file diff --git a/README.md b/README.md index b2adccc..97f094e 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ After upgrade the addon the pages must be reloaded before the new attributes are After upgrade to version 1.0.0 check existing rules because some properties are changed. ## Release notes ## +1.0.1 + * Add Sunrise Offset Minutes and Sunset Offset Minutes config which if set, generates a Sunrise/Sunset event offset from the actual Sunrise/Sunset event. + * When creating rules simply Select your offset event i.e. Event "Sunset -n mins" + 1.0.0 * Added event 'Sunset'/'Sunrise' * Added property enum 'Weekday' diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..b5c881f --- /dev/null +++ b/manifest.json @@ -0,0 +1,80 @@ +{ + "author": "tomasy", + "description": "Date Time addon for Mozilla IoT Gateway\nBecause of a bug in rule engine after adding a 'Dark' or 'Weekend' to the rule, change state for them manually. See https://github.com/tomasy/date-time-adapter", + "gateway_specific_settings": { + "webthings": { + "exec": "python3 {path}/bootstrap.py", + "primary_type": "adapter", + "strict_max_version": "*", + "strict_min_version": "0.10.0" + } + }, + "homepage_url": "https://github.com/tomasy/date-time-adapter", + "id": "date-time-adapter", + "license": "MPL-2.0", + "manifest_version": 1, + "name": "DateTime Adapter", + "options": { + "default": { + "horizon": "-0:34", + "lat": "", + "lng": "", + "log_level": "INFO", + "sunrise_offset_mins": 0, + "sunset_offset_mins": 0, + "timezone": "UTC" + }, + "schema": { + "description": "Configuration for sunset and sunrise", + "properties": { + "horizon": { + "description": "Horizon example. '-0:34'=(just below horizon), -6=civil twilight, -12=nautical or -18=astronomical", + "type": "string" + }, + "lat": { + "description": "Latitude (e.g. 57.67 for Gothenburg, Sweden) ", + "type": "string" + }, + "lng": { + "description": "Longitude (e.g. 11.89 for Gothenburg, Sweden)", + "type": "string" + }, + "log_level": { + "description": "Log level. Use INFO as standard", + "enum": [ + "INFO", + "DEBUG" + ], + "type": "string" + }, + "sunrise_offset_mins": { + "description": "Create a secondary sunrise event that occurs n minutes before or after the adding actual sunset event.", + "maximum": 60, + "minimum": -60, + "type": "number" + }, + "sunset_offset_mins": { + "description": "Create a secondary sunset event that occurs n minutes before or after the adding actual sunset event.", + "maximum": 60, + "minimum": -60, + "type": "number" + }, + "timezone": { + "description": "Timezone. (e.g. Europe/Stockholm)", + "type": "string" + } + }, + "required": [ + "timezone", + "lat", + "lng", + "horizon", + "sunset_offset_mins", + "sunrise_offset_mins" + ], + "type": "object" + } + }, + "short_name": "DateTime Ada", + "version": "1.0.1" +} \ No newline at end of file diff --git a/package.json b/package.json index 3f469ed..4dd72f9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "date-time-adapter", "display_name": "DateTime Adapter", - "version": "1.0.0", + "version": "1.0.1", "description": "Date Time addon for Mozilla IoT Gateway\nBecause of a bug in rule engine after adding a 'Dark' or 'Weekend' to the rule, change state for them manually. See https://github.com/tomasy/date-time-adapter", "author": "tomasy", "main": "main.py", @@ -22,11 +22,11 @@ "url": "https://github.com/tomasy/date-time-adapter/issues" }, "files": [ - "LICENSE", - "SHA256SUMS", + "LICENSE", "bootstrap.py", "main.py", "package.json", + "manifest.json", "pkg/__init__.py", "pkg/config.py", "pkg/date_adapter.py", @@ -46,14 +46,24 @@ "plugin": true, "exec": "python3 {path}/bootstrap.py", "config": { - "timezone": "UTC", - "lat": "", - "lng": "", - "horizon": "-0:34", - "log_level": "INFO" + "timezone": "UTC", + "lat": "", + "lng": "", + "horizon": "-0:34", + "sunrise_offset_mins": 0, + "sunset_offset_mins": 0, + "log_level": "INFO" }, "schema": { "type": "object", + "required": [ + "timezone", + "lat", + "lng", + "horizon", + "sunset_offset_mins", + "sunrise_offset_mins" + ], "description": "Configuration for sunset and sunrise", "properties": { "timezone": { @@ -72,12 +82,27 @@ "type": "string", "description": "Horizon example. '-0:34'=(just below horizon), -6=civil twilight, -12=nautical or -18=astronomical" }, + "sunset_offset_mins": { + "type": "number", + "description": "Create a secondary sunset event that occurs n minutes before or after the adding actual sunset event.", + "minimum": -60, + "maximum": 60 + }, + "sunrise_offset_mins": { + "type": "number", + "description": "Create a secondary sunrise event that occurs n minutes before or after the adding actual sunset event.", + "minimum": -60, + "maximum": 60 + }, "log_level": { - "type": "string", - "enum": [ "INFO", "DEBUG" ], + "type": "string", + "enum": [ + "INFO", + "DEBUG" + ], "description": "Log level. Use INFO as standard" - } + } } } } -} +} \ No newline at end of file diff --git a/package.sh b/package.sh index a5d724a..12094b3 100755 --- a/package.sh +++ b/package.sh @@ -18,7 +18,7 @@ pip3 install -r requirements.txt -t lib --no-binary pyHS100 --prefix "" # Put package together #cp -r lib pkg LICENSE README.md package.json *.py package/ -cp -r pkg LICENSE README.md package.json *.py requirements.txt setup.cfg package/ +cp -r pkg LICENSE README.md package.json manifest.json *.py requirements.txt setup.cfg package/ find package -type f -name '*.pyc' -delete find package -type d -empty -delete diff --git a/pkg/config.py b/pkg/config.py index caa8c1b..3a8a448 100644 --- a/pkg/config.py +++ b/pkg/config.py @@ -9,6 +9,8 @@ def __init__(self, package_name): self.lat = None self.lng = None self.horizon = None + self.sunset_offset_mins = None + self.sunrise_offset_mins = None self.log_level = None self.open() self.load() @@ -22,6 +24,8 @@ def load(self): self.lat = config['lat'] self.lng = config['lng'] self.horizon = config['horizon'] + self.sunset_offset_mins = config['sunset_offset_mins'] + self.sunrise_offset_mins = config['sunrise_offset_mins'] self.log_level = config['log_level'] except Exception as ex: logging.exception('Strange config', config) diff --git a/pkg/date_device.py b/pkg/date_device.py index 6fb26d9..b46d997 100644 --- a/pkg/date_device.py +++ b/pkg/date_device.py @@ -3,6 +3,7 @@ import logging import threading import time +import datetime from gateway_addon import Device, Event from .util import DT from .date_property import DateWeekendProperty, DateEvenHourProperty, DateEvenMinuteProperty, \ @@ -54,7 +55,7 @@ def poll(self): logging.error('THREAD ERR Exception %s', ex) logging.exception('Exception %s', ex) continue - logging.info('POLL STOPED for device: %s', self.name) + logging.info('POLL STOPPED for device: %s', self.name) class DateTimeDevice(DTDevice): @@ -67,9 +68,14 @@ def __init__(self, adapter, _id, _config): DTDevice.__init__(self, adapter, _id) self._context = 'https://iot.mozilla.org/schemas' self._type = ['BinarySensor', 'MultiLevelSensor'] - self.dt = DT(_config.timezone, _config.lat, _config.lng, _config.horizon) + self.dt = DT(_config.timezone, _config.lat, _config.lng, _config.horizon, _config.sunset_offset_mins, _config.sunrise_offset_mins) self.sunrise = self.dt.calc_sunrise() self.sunset = self.dt.calc_sunset() + self.sunset_offset_mins = _config.sunset_offset_mins + self.sunrise_offset_mins = _config.sunrise_offset_mins + self.sunset_offset_active = False; + self.sunrise_offset_active = False; + logging.info('sunset: %s sunrise: %s', self.sunset, self.sunrise) self.add_property(DateWeekendProperty(self, self.dt)) @@ -91,6 +97,23 @@ def __init__(self, adapter, _id, _config): 'description': 'An event for new sunrise', 'type': 'string', }) + + if self.sunset_offset_mins is not None and self.sunset_offset_mins is not 0: + title = 'Sunset offset ' + str(self.sunset_offset_mins) + ' mins' + self.add_event('sunset_offset', { + 'title': title, 'label': 'Sunset_Offset', + 'description': 'An event for new offset sunset', + 'type': 'string', + }) + + if self.sunrise_offset_mins is not None and self.sunrise_offset_mins is not 0: + title = 'Sunrise offset ' + str(self.sunset_offset_mins) + ' mins' + self.add_event('sunrise_offset', { + 'title': title, 'label': 'Sunrise_Offset', + 'description': 'An event for new offset sunrise', + 'type': 'string', + }) + self.name = 'DateTime' self.description = 'DateTime desc' self.init() @@ -99,16 +122,47 @@ def __init__(self, adapter, _id, _config): def check(self): self.check_sunrise() self.check_sunset() + self.check_offset_sunrise() + self.check_offset_sunset() def check_sunrise(self): if self.dt.now() > self.sunrise: self.check_send_event(self.sunrise, 'sunrise') self.sunrise = self.dt.calc_sunrise() + self.sunrise_offset_active = False def check_sunset(self): if self.dt.now() > self.sunset: self.check_send_event(self.sunset, 'sunset') self.sunset = self.dt.calc_sunset() + self.sunset_offset_active = False + + def check_offset_sunrise(self): + if self.sunrise_offset_mins is not None and self.sunrise_offset_active is False: + offset_sunrise = None + if self.sunrise_offset_mins < 0: + offset_sunrise = self.sunrise - datetime.timedelta(minutes=-self.sunrise_offset_mins) + if self.sunrise_offset_mins > 0: + offset_sunrise = self.sunrise + datetime.timedelta(minutes=self.sunrise_offset_mins) + + if offset_sunrise is not None: + if self.dt.now() > offset_sunrise: + self.check_send_event(self.sunrise, 'sunrise_offset') + self.sunrise_offset_active = True + + def check_offset_sunset(self): + if self.sunset_offset_mins is not None and self.sunset_offset_active is False: + offset_sunset = None + if self.sunset_offset_mins < 0: + offset_sunset = self.sunset - datetime.timedelta(minutes=-self.sunset_offset_mins) + if self.sunset_offset_mins > 0: + offset_sunset = self.sunset + datetime.timedelta(minutes=self.sunset_offset_mins) + + if offset_sunset is not None: + if self.dt.now() > offset_sunset: + self.check_send_event(self.sunset, 'sunset_offset') + self.sunset_offset_active = True; + """ Check if the sunset/sunrise time occured and if so send event """ def check_send_event(self, next_sunset_sunrise, event_name): @@ -127,7 +181,7 @@ def __init__(self, adapter, _id, _config): DTDevice.__init__(self, adapter, _id) self._context = 'https://iot.mozilla.org/schemas' self._type = ['BinarySensor', 'MultiLevelSensor'] - self.dt = DT(_config.timezone, _config.lat, _config.lng, _config.horizon) + self.dt = DT(_config.timezone, _config.lat, _config.lng, _config.horizon, _config.sunset_offset_mins, _config.sunrise_offset_mins) self.add_property(DTMinuteProperty(self,self.dt)) diff --git a/pkg/util.py b/pkg/util.py index a7ee9c0..4cd9b0b 100644 --- a/pkg/util.py +++ b/pkg/util.py @@ -6,7 +6,7 @@ import pytz class DT(): - def __init__(self, timezone, lat, lng, horizon): + def __init__(self, timezone, lat, lng, horizon, sunset_offset_mins, sunrise_offset_mins): self.timezone = timezone self.lat = lat self.lng = lng @@ -87,7 +87,7 @@ def calc_sunrise(self): sunrise = observer_today.next_rising(ephem.Sun()) sunrise_local = self.to_localtime(sunrise.datetime()) logging.info('CALC_SUNRISE today.utc: %s sunrise: %s sunrise_local: %s', observer_today.date, sunrise, sunrise_local) - logging.debug('DTSRISE lat: %s lng: %s observer_today: %s', self.lat, self.lng, observer_today) + logging.debug('DTSRISE lat: %s lng: %s observer_today: %s', self.lat, self.lng, observer_today) return sunrise_local def sunrise(self): @@ -101,7 +101,14 @@ def calc_sunset(self): sunset = observer_today.next_setting(ephem.Sun()) sunset_local = self.to_localtime(sunset.datetime()) logging.info('CALC_SUNSET today.utc: %s sunset: %s sunset_local: %s', observer_today.date, sunset, sunset_local) - logging.debug('DTSET lat: %s lng: %s observer_today: %s', self.lat, self.lng, observer_today) + + # if self.sunset_offset_mins is not None: + # if self.sunset_offset_mins < 0: + # sunset_local = sunset_local - datetime.timedelta(minutes=-self.sunset_offset_mins) + # if self.sunset_offset_mins > 0: + # sunset_local = sunset_local+ datetime.timedelta(minutes=self.sunset_offset_mins) + # logging.info('CALC_SUNSET_OFFSET override_mins: %s mins override_sunset: %s', self.sunset_offset_mins, sunset_local) + return sunset_local def sunset(self): @@ -117,4 +124,4 @@ def to_localtime(self, dt): dt_utc = dt.replace(tzinfo=utc) # convert to localtimezone dt_local = dt_utc.astimezone(local_timezone) - return dt_local + return dt_local \ No newline at end of file