Skip to content
This repository has been archived by the owner on Aug 29, 2019. It is now read-only.

Update import for HA Version 0.91+ #36

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 69 additions & 2 deletions shabbat_times/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[Community Discussion](https://community.home-assistant.io/t/get-shabbat-times-from-hebcal-api-custom-sensor/32429)</br>

#### Component Description
The component works in a similar manner as the *rest* type sensor, it send an api request towards [Hebcal's Shabbat Times API](https://www.hebcal.com/home/197/shabbat-times-rest-api) and retrieves the **next** or **current** Shabbat start and end date and time, and sets them as attributes within a created sensor.</br>
The component works in a similar manner as the *rest* type sensor, it send an api request towards [Hebcal's Shabbat Times API](https://www.hebcal.com/home/197/shabbat-times-rest-api) and retrieves the **upcoming** or **current** Shabbat start and end date and time, and sets them as attributes within a created sensor.</br>
The component can create multiple sensors for multiple cities around the world, the selected city is identified by its geoname which can selected [here](https://github.com/hebcal/dotcom/blob/master/hebcal.com/dist/cities2.txt).

**Table Of Contents**
Expand All @@ -19,7 +19,7 @@ The component can create multiple sensors for multiple cities around the world,

## Installation

- Copy file [`custom_components/sensor/shabbat_times.py`](custom_components/sensor/shabbat_times.py) to your `ha_config_dir/custom_components/sensor` directory.
- Copy files [`custom_components/sensor/shabbat_times.py`](custom_components/sensor/shabbat_times.py) and [`custom_components/sensor/shabbat_times_util.py`](custom_components/sensor/shabbat_times_util.py) to your `ha_config_dir/custom_components/sensor` directory.
- Configure like instructed in the Configuration section below.
- Restart Home-Assistant.

Expand Down Expand Up @@ -60,5 +60,72 @@ These attributes are available for use within templates like so:
- *Error...*: the api has encountered an error.
- *Updated*: the sensor has finished updating.

## "Shabbat Mode" Sensors
A particularly useful application of this sensor is to be able to run automations when Shabbat/Yom Tov begins/ends. There are a few ways to do this, but a particularly handy way is to use a template sensor. This can be implemented as a regular sensor or as a binary sensor, but this example shows a regular sensor. Be sure to replace YOUR_SHABBAT_TIMES with the actual name of your shabbat_times sensor:

```yaml
sensor:
- platform: template
sensors:
shabbat_mode:
friendly_name: Shabbat Mode
value_template: >-
{%- set now_time = as_timestamp(strptime(states.sensor.date__time.state, "%Y-%m-%d, %H:%M")) %}
{%- if not is_state("sensor.YOUR_SHABBAT_TIMES", "Updated") -%}
unknown
{%- elif now_time >= as_timestamp(state_attr("sensor.YOUR_SHABBAT_TIMES", "shabbat_start"))
and now_time < as_timestamp(state_attr("sensor.YOUR_SHABBAT_TIMES", "shabbat_end")) -%}
on
{%- else -%}
off
{%- endif -%}
```

Defining the template sensor this way has the benefit that if your HomeAssistant server reboots on Shabbat, the Shabbat Mode sensor will be up-to-date.

One additional requirement for this template sensor is that you define the date__time sensor. That is because now() does not update properly in template sensors; for details see this [forum post](https://community.home-assistant.io/t/how-to-replace-entity-id-in-template-sensors/40540/2?u=kallb123). Here's an example on how to configure that:

```yaml
sensor:
- platform: time_date
display_options:
- 'time'
- 'date'
- 'date_time'
```

## Automations

If you've set up a "Shabbat Mode" sensor as described above, it's quite easy to use it to build an automation. Here's an example:

```yaml
automation:
- alias: Shabbat Mode On
trigger:
entity_id: sensor.shabbat_mode
from: 'off'
platform: state
to: 'on'
action:
- service: notify.notify
data:
message: 'Shabbat mode is on'
- service: tts.google_say
entity_id: media_player.kitchen_speaker
data:
message: "It's candle lighting time. Shabbat Shalom!"
- service: switch.turn_off
data:
entity_id: switch.garage_floodlight
- service: scene.turn_on
data:
entity_id: scene.dining
- service: light.turn_off
data:
entity_id: light.master_bedroom_main_lights
```

TODO(arigilder): Describe more advanced hysteresis via input_booleans.

## Special Notes
- The sensors will always show the date and time for the next Shabbat, unless the Shabbat is now, and therefore the sensors will show the current Shabbat date and time.
155 changes: 94 additions & 61 deletions shabbat_times/custom_components/sensor/shabbat_times.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,45 @@
"""////////////////////////////////////////////////////////////////////////////////////////////////
Home Assistant Custom Component for following the jewish shabbat times as a Sensor platform.
Build by TomerFi
Please visit https://github.com/TomerFi/home-assistant-custom-components for more custom components

installation notes:
place this file in the following folder and restart home assistant:
/config/custom_components/sensor

yaml configuration example:
sensor:
- platform: shabbat_times
geonames: "IL-Haifa,IL-Rishon LeZion"

////////////////////////////////////////////////////////////////////////////////////////////////"""
import asyncio
import datetime
import logging
import json
import requests
import voluptuous as vol
from collections import namedtuple
from custom_components.sensor import shabbat_times_util as shabbat
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import CONF_SCAN_INTERVAL
from homeassistant.const import CONF_SCAN_INTERVAL, CONF_LONGITUDE, CONF_LATITUDE, CONF_NAME, CONF_TIME_ZONE
from homeassistant.util import Throttle
from homeassistant.helpers.entity import Entity
#from homeassistant.helpers.restore_state import async_get_last_state
from homeassistant.helpers.restore_state import RestoreEntity
import homeassistant.helpers.config_validation as cv

_LOGGER = logging.getLogger(__name__)

GEONAMES = 'geonames'
DEFAULT_NAME = "Shabbat Time"
HAVDALAH_MINUTES = 'havdalah_minutes_after_sundown'
CANDLE_LIGHT_MINUTES = 'candle_lighting_minutes_before_sunset'

HAVDALAH_DEFAULT = 42
CANDLE_LIGHT_DEFAULT = 30
SCAN_INTERVAL = datetime.timedelta(seconds=60)
SCAN_INTERVAL = datetime.timedelta(minutes=60)

SHABBAT_START = 'shabbat_start'
SHABBAT_END = 'shabbat_end'
LAST_UPDATE = 'last_update'
TITLE = 'title'
HEBREW_TITLE = 'hebrew_title'

SENSOR_ATTRIBUTES = [HAVDALAH_MINUTES, CANDLE_LIGHT_MINUTES,
SHABBAT_START, SHABBAT_END, LAST_UPDATE, TITLE, HEBREW_TITLE]

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(GEONAMES): cv.string,
vol.Inclusive(CONF_LATITUDE, 'coordinates',
'Latitude and longitude must exist together'): cv.latitude,
vol.Inclusive(CONF_LONGITUDE, 'coordinates',
'Latitude and longitude must exist together'): cv.longitude,
vol.Optional(CONF_TIME_ZONE): cv.time_zone,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(HAVDALAH_MINUTES, default=HAVDALAH_DEFAULT): int,
vol.Optional(CANDLE_LIGHT_MINUTES, default=CANDLE_LIGHT_DEFAULT): int,
vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL): cv.time_period
Expand All @@ -47,25 +48,62 @@
def setup_platform(hass, config, add_devices, discovery_info=None):
havdalah = config.get(HAVDALAH_MINUTES)
candle_light = config.get(CANDLE_LIGHT_MINUTES)
cities = config.get(GEONAMES)
cities_list = cities.split(",")
name = config.get(CONF_NAME)
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
timezone = config.get(CONF_TIME_ZONE, hass.config.time_zone)

for city in cities_list:
add_devices([ShabbatTimes(hass, city, 'Shabbat Times ' + city.replace('-', '_'), havdalah, candle_light)])
add_devices([ShabbatTimes(hass, latitude, longitude, timezone, name, havdalah, candle_light)])


class ShabbatTimes(Entity):
class ShabbatTimes(RestoreEntity, Entity):

def __init__(self, hass, city, name, havdalah, candle_light):
def __init__(self, hass, latitude, longitude, timezone, name, havdalah, candle_light):
self._hass = hass
self._city = city
self._name = name
self._latitude = latitude
self._longitude = longitude
self._timezone = timezone
self._name = "Shabbat Times " + name
self._havdalah = havdalah
self._candle_light = candle_light
self._state = 'Awaiting Update'
self._shabbat_start = None
self._shabbat_end = None

self._last_update = None
self._title = None
self._hebrew_title = None


@asyncio.coroutine
def async_added_to_hass(self):
""" Restore original state."""
old_state = yield from self.async_get_last_state()
_LOGGER.info('Old state: ' + str(old_state))
if (not old_state or
old_state.attributes[LAST_UPDATE] is None or
old_state.attributes[SHABBAT_START] is None or
old_state.attributes[SHABBAT_END] is None):
self.update()
return

if shabbat.parse_time(old_state.attributes[SHABBAT_END], False) < datetime.datetime.now():
_LOGGER.error("Current time is newer than shabbat end time. Updating.")
self.update()
return

params = {key: old_state.attributes[key] for key in SENSOR_ATTRIBUTES
if key in old_state.attributes}
_LOGGER.debug('params: ' + str(params))
self._state = old_state.state
self._havdalah = params[HAVDALAH_MINUTES]
self._candle_light = params[CANDLE_LIGHT_MINUTES]
self._shabbat_start = params[SHABBAT_START]
self._shabbat_end = params[SHABBAT_END]
self._last_update = params[LAST_UPDATE]
self._title = params[TITLE]
self._hebrew_title = params[HEBREW_TITLE]
_LOGGER.info('New state: ' + str(self))

@property
def name(self):
return self._name
Expand All @@ -80,42 +118,37 @@ def device_state_attributes(self):
SHABBAT_START: self._shabbat_start,
SHABBAT_END: self._shabbat_end,
HAVDALAH_MINUTES: self._havdalah,
CANDLE_LIGHT_MINUTES: self._candle_light
CANDLE_LIGHT_MINUTES: self._candle_light,
LAST_UPDATE: self._last_update,
TITLE: self._title,
HEBREW_TITLE: self._hebrew_title,
}

@Throttle(SCAN_INTERVAL)
def update(self):
self._state = 'Working'
self.shabbat_start = None
self._shabbat_start = None
self._shabbat_end = None
today = datetime.date.today()
if (today.weekday() == 5):
friday = today + datetime.timedelta(-1)
else:
friday = today + datetime.timedelta((4-today.weekday()) % 7)

saturday = friday + datetime.timedelta(+1)

year = str(friday.year)
month = ("0" + str(friday.month))[-2:]

hebcal_url = "http://www.hebcal.com/hebcal/?v=1&cfg=json&maj=off&min=off&mod=off&nx=off&year=" + year + "&month=" + month + "&ss=off&mf=off&c=on&geo=city&city=" + self._city + "&m=" + str(self._havdalah) + "&s=off&i=off&b=" + str(self._candle_light)
hebcal_response = requests.get(hebcal_url)
hebcal_json_input = hebcal_response.text
hebcal_decoded = json.loads(hebcal_json_input)

if 'error' in hebcal_decoded:
self._state = hebcal_decoded['error']
_LOGGER.error(hebcal_decoded['error'])
self._title = None

fetcher = shabbat.ShabbatTimesFetcher(
self._latitude, self._longitude, self._timezone, self._havdalah,
self._candle_light)
parser = shabbat.ShabbatTimesParser(fetcher)
now = datetime.datetime.now()
current_interval = parser.update(now)
if current_interval is None:
_LOGGER.error('Could not parse Shabbat Times!')
if parser.error:
self._state = parser.error
else:
self._state = 'Error'
else:
for item in hebcal_decoded['items']:
if (item['category'] == 'candles'):
ret_date = datetime.datetime.strptime(item['date'][0:-6].replace('T',' '), '%Y-%m-%d %H:%M:%S')
if (ret_date.date() == friday):
self._shabbat_start = ret_date
elif (item['category'] == 'havdalah'):
ret_date = datetime.datetime.strptime(item['date'][0:-6].replace('T',' '), '%Y-%m-%d %H:%M:%S')
if (ret_date.date() == saturday):
self._shabbat_end = ret_date

self._state = 'Updated'
# Valid interval.
self._shabbat_start = current_interval.start_time
self._shabbat_end = current_interval.end_time
self._title = current_interval.title
self._hebrew_title = current_interval.hebrew_title
_LOGGER.info('Setting Shabbat times to ' + str(current_interval))
self._state = 'Updated'
self._last_update = now
Loading