]> git.basschouten.com Git - openhab-addons.git/commitdiff
[Ephemeris] Binding to make the bridge with core Ephemeris functions (#16628)
authorGaël L'hopital <gael@lhopital.org>
Sun, 26 May 2024 16:58:48 +0000 (18:58 +0200)
committerGitHub <noreply@github.com>
Sun, 26 May 2024 16:58:48 +0000 (18:58 +0200)
* [ephemeris] Initial commit of the ephemeris binding

Signed-off-by: gael@lhopital.org <gael@lhopital.org>
23 files changed:
CODEOWNERS
bom/openhab-addons/pom.xml
bundles/org.openhab.binding.ephemeris/NOTICE [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/README.md [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/pom.xml [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/feature/feature.xml [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/EphemerisBindingConstants.java [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/EphemerisException.java [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/EphemerisHandlerFactory.java [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/configuration/DaysetConfiguration.java [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/configuration/FileConfiguration.java [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/discovery/EphemerisDiscoveryService.java [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/BaseEphemerisHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/CustomHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/DaysetHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/HolidayHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/JollydayHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/WeekendHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/providers/EphemerisDescriptionProvider.java [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/resources/OH-INF/addon/addon.xml [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/resources/OH-INF/i18n/ephemeris.properties [new file with mode: 0644]
bundles/org.openhab.binding.ephemeris/src/main/resources/OH-INF/thing/thing-types.xml [new file with mode: 0644]
bundles/pom.xml

index a82897a8b9633bb4e4796e22a4193c918e29ff77..7c798cf0bf19454236ad6d44a233b92ebacdec60 100644 (file)
 /bundles/org.openhab.binding.enocean/ @fruggy83
 /bundles/org.openhab.binding.enphase/ @Hilbrand
 /bundles/org.openhab.binding.enturno/ @klocsson
+/bundles/org.openhab.binding.ephemeris/ @clinique
 /bundles/org.openhab.binding.epsonprojector/ @mlobstein
 /bundles/org.openhab.binding.etherrain/ @dfad1469
 /bundles/org.openhab.binding.evcc/ @florian-h05
index f0b12695efedaa55d9a070bebe9215d09457bb49..6a669f40976949dc53307c32788c904a3ed9836c 100644 (file)
       <artifactId>org.openhab.binding.enturno</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.openhab.addons.bundles</groupId>
+      <artifactId>org.openhab.binding.ephemeris</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.openhab.addons.bundles</groupId>
       <artifactId>org.openhab.binding.epsonprojector</artifactId>
diff --git a/bundles/org.openhab.binding.ephemeris/NOTICE b/bundles/org.openhab.binding.ephemeris/NOTICE
new file mode 100644 (file)
index 0000000..38d625e
--- /dev/null
@@ -0,0 +1,13 @@
+This content is produced and maintained by the openHAB project.
+
+* Project home: https://www.openhab.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/openhab/openhab-addons
diff --git a/bundles/org.openhab.binding.ephemeris/README.md b/bundles/org.openhab.binding.ephemeris/README.md
new file mode 100644 (file)
index 0000000..238e905
--- /dev/null
@@ -0,0 +1,105 @@
+# Ephemeris Binding
+
+The Ephemeris Binding makes the bridge with Ephemeris core actions.
+It provides access to Ephemeris data via Items without requiring usage of a scripting language.
+
+The binding will search your Jollyday event definition files in the sub folder `/misc/ephemeris` located in the configuration folder of openHAB (e.g. for a linux system : /etc/openhab/misc/ephemeris/)
+
+## Supported Things
+
+The binding handles the following Things:
+
+* default holiday data (`holiday`)
+* custom holiday file (`custom`)
+* daysets (`dayset`)
+* weekend (`weekend`)
+
+## Discovery
+
+The binding discovers `weekend` and `holiday` things.
+
+## Binding Configuration
+
+There is no configuration at binding level.
+
+## Thing Configuration
+
+
+### `custom` Thing Configuration
+
+| Name            | Type    | Description                                       | Default | Required | Advanced |
+|-----------------|---------|---------------------------------------------------|---------|----------|----------|
+| fileName        | text    | Name of the XML file in the configuration folder  | N/A     | yes      | no       |
+
+The file has to use the syntax described here : https://www.openhab.org/docs/configuration/actions.html#custom-bank-holidays
+
+### `dayset` Thing Configuration
+
+| Name            | Type    | Description               | Default | Required | Advanced |
+|-----------------|---------|---------------------------|---------|----------|----------|
+| name            | text    | Name of the dayset used   | N/A     | yes      | no       |
+
+
+## Channels
+
+### `weekend` Channels
+
+| Name     | Type   | Description                                                   |
+|----------|--------|---------------------------------------------------------------|
+| today    | Switch | Set to ON if today is a weekend day, OFF in the other case    |
+| tomorrow | Switch | Set to ON if tomorrow is a weekend day, OFF in the other case |
+
+### `dayset` Channels
+
+| Name     | Type   | Description                                                         |
+|----------|--------|---------------------------------------------------------------------|
+| today    | Switch | Set to ON if today is in the given dayset, OFF in the other case    |
+| tomorrow | Switch | Set to ON if tomorrow is in the given dayset, OFF in the other case |
+
+### `holiday` Channels
+
+| Name             | Type        | Description                                    |
+|------------------|-------------|------------------------------------------------|
+| title-today      | String      | Name of today's holiday if any, NULL otherwise |
+| holiday-today    | Switch      | Set to ON if today is a holiday                |
+| holiday-tomorrow | Switch      | Set to ON if tomorrow is a holiday             |
+| next-title       | String      | Name of the next coming holiday                |
+| next-start       | DateTime    | Start date of the next coming holiday          |
+| days-remaining   | Number:Time | Remaining days until next holiday              |
+
+### `custom` Channels
+
+| Name           | Type        | Description                            |
+|----------------|-------------|----------------------------------------|
+| title-today    | String      | Title of the currently present event   |
+| event-today    | Switch      | Set to ON if an event exists today     |
+| event-tomorrow | Switch      | Set to ON if an event exists tomorrow  |
+| next-title     | String      | Title of the next starting event       |
+| next-start     | DateTime    | Start date of the next coming event    |
+| days-remaining | Number:Time | Remaining days until next event        |
+
+## Full Example
+
+### Thing Configuration
+
+```java
+Thing ephemeris:holiday:local "Holidays"
+Thing ephemeris:weekend:local "Week-end"
+Thing ephemeris:custom:events "Event" [fileName="events.xml"]
+```
+
+### Item Configuration
+
+```java
+String         ToD_Event_Current          "Event Today"       <calendar>    (gEvents)                           {channel="ephemeris:custom:events:title-today"}
+String         ToD_Event_Next          "Event Next"       <calendar>    (gEvents)                           {channel="ephemeris:custom:events:next-title"}
+Number:Time    ToD_Event_Next_Left       "Event In"          <calendar>    (gEvents)       ["Measurement","Duration"]    {channel="ephemeris:custom:events:days-remaining", unit="day"}
+
+Switch         ToD_Week_End_Current           "Week-End"                <calendar>    (gWeekEnd)                               {channel="ephemeris:weekend:local:today"}
+Switch         ToD_Week_End_Tomorrow           "Week-End Tomorrow"         <calendar>    (gWeekEnd)                               {channel="ephemeris:weekend:local:tomorrow"}
+
+String         ToD_Holiday_Current              "Holiday Today"       <calendar>    (gHoliday)                                 {channel="ephemeris:holiday:local:title-today"}
+String         ToD_Holiday_Next              "Holiday Next"           <calendar>    (gHoliday)                                 {channel="ephemeris:holiday:local:next-title"}
+Number:Time    ToD_Holiday_Next_Left           "Holiday In"              <calendar>    (gHoliday)            ["Measurement","Duration"]    {channel="ephemeris:holiday:local:days-remaining", unit="day"}
+
+```
diff --git a/bundles/org.openhab.binding.ephemeris/pom.xml b/bundles/org.openhab.binding.ephemeris/pom.xml
new file mode 100644 (file)
index 0000000..f8c582d
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.openhab.addons.bundles</groupId>
+    <artifactId>org.openhab.addons.reactor.bundles</artifactId>
+    <version>4.2.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>org.openhab.binding.ephemeris</artifactId>
+
+  <name>openHAB Add-ons :: Bundles :: Ephemeris Binding</name>
+
+</project>
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/feature/feature.xml b/bundles/org.openhab.binding.ephemeris/src/main/feature/feature.xml
new file mode 100644 (file)
index 0000000..0d647a0
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<features name="org.openhab.binding.ephemeris-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
+       <repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
+
+       <feature name="openhab-binding-ephemeris" description="Ephemeris Binding" version="${project.version}">
+               <feature>openhab-runtime-base</feature>
+               <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.ephemeris/${project.version}</bundle>
+       </feature>
+</features>
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/EphemerisBindingConstants.java b/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/EphemerisBindingConstants.java
new file mode 100644 (file)
index 0000000..4e2c7af
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ephemeris.internal;
+
+import java.io.File;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.OpenHAB;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link EphemerisBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class EphemerisBindingConstants {
+
+    public static final String BINDING_ID = "ephemeris";
+
+    // List of all Thing Type UIDs
+    public static final ThingTypeUID THING_TYPE_CUSTOM = new ThingTypeUID(BINDING_ID, "custom");
+    public static final ThingTypeUID THING_TYPE_HOLIDAY = new ThingTypeUID(BINDING_ID, "holiday");
+    public static final ThingTypeUID THING_TYPE_DAYSET = new ThingTypeUID(BINDING_ID, "dayset");
+    public static final ThingTypeUID THING_TYPE_WEEKEND = new ThingTypeUID(BINDING_ID, "weekend");
+
+    // List of all Channel ids
+    public static final String CHANNEL_CURRENT_EVENT = "title-today";
+    public static final String CHANNEL_NEXT_EVENT = "next-title";
+    public static final String CHANNEL_NEXT_START = "next-start";
+    public static final String CHANNEL_NEXT_REMAINING = "days-remaining";
+    public static final String CHANNEL_TODAY = "today";
+    public static final String CHANNEL_TOMORROW = "tomorrow";
+    public static final String CHANNEL_HOLIDAY_TODAY = "holiday-today";
+    public static final String CHANNEL_HOLIDAY_TOMORROW = "holiday-tomorrow";
+    public static final String CHANNEL_EVENT_TODAY = "event-today";
+    public static final String CHANNEL_EVENT_TOMORROW = "event-tomorrow";
+
+    // Folder for xml storage eg: /etc/openhab/misc/ephemeris
+    public static final String BINDING_DATA_PATH = "%s%smisc%s%s".formatted(OpenHAB.getConfigFolder(), File.separator,
+            File.separator, BINDING_ID);
+}
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/EphemerisException.java b/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/EphemerisException.java
new file mode 100644 (file)
index 0000000..67cab6f
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ephemeris.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingStatusDetail;
+
+/**
+ * Exception raised by Ephemeris Handlers
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class EphemerisException extends Exception {
+    private static final long serialVersionUID = -8813754360966576513L;
+    private final ThingStatusDetail statusDetail;
+
+    public EphemerisException(String message, ThingStatusDetail statusDetail) {
+        super(message);
+        this.statusDetail = statusDetail;
+    }
+
+    public ThingStatusDetail getStatusDetail() {
+        return statusDetail;
+    }
+}
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/EphemerisHandlerFactory.java b/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/EphemerisHandlerFactory.java
new file mode 100644 (file)
index 0000000..695c677
--- /dev/null
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ephemeris.internal;
+
+import static org.openhab.binding.ephemeris.internal.EphemerisBindingConstants.*;
+
+import java.io.File;
+import java.time.ZoneId;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.ephemeris.internal.handler.CustomHandler;
+import org.openhab.binding.ephemeris.internal.handler.DaysetHandler;
+import org.openhab.binding.ephemeris.internal.handler.HolidayHandler;
+import org.openhab.binding.ephemeris.internal.handler.WeekendHandler;
+import org.openhab.binding.ephemeris.internal.providers.EphemerisDescriptionProvider;
+import org.openhab.core.ephemeris.EphemerisManager;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.binding.BaseThingHandlerFactory;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerFactory;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link EphemerisHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.ephemeris", service = ThingHandlerFactory.class)
+public class EphemerisHandlerFactory extends BaseThingHandlerFactory {
+    private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_CUSTOM, THING_TYPE_HOLIDAY,
+            THING_TYPE_DAYSET, THING_TYPE_WEEKEND);
+
+    private final Logger logger = LoggerFactory.getLogger(EphemerisHandlerFactory.class);
+    private final EphemerisManager ephemerisManager;
+    private final ZoneId zoneId;
+    private final EphemerisDescriptionProvider descriptionProvider;
+
+    @Activate
+    public EphemerisHandlerFactory(final @Reference EphemerisManager ephemerisManager,
+            final @Reference TimeZoneProvider timeZoneProvider,
+            final @Reference EphemerisDescriptionProvider descriptionProvider) {
+        this.ephemerisManager = ephemerisManager;
+        this.zoneId = timeZoneProvider.getTimeZone();
+        this.descriptionProvider = descriptionProvider;
+        File folder = new File(BINDING_DATA_PATH);
+        if (!folder.exists()) {
+            logger.info("Please, create the folder '{}' to store your custom Jollyday definition files.",
+                    BINDING_DATA_PATH);
+        }
+    }
+
+    @Override
+    public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+        return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+    }
+
+    @Override
+    protected @Nullable ThingHandler createHandler(Thing thing) {
+        ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+
+        if (THING_TYPE_CUSTOM.equals(thingTypeUID)) {
+            return new CustomHandler(thing, ephemerisManager, zoneId);
+        } else if (THING_TYPE_HOLIDAY.equals(thingTypeUID)) {
+            return new HolidayHandler(thing, ephemerisManager, zoneId, descriptionProvider);
+        } else if (THING_TYPE_DAYSET.equals(thingTypeUID)) {
+            return new DaysetHandler(thing, ephemerisManager, zoneId);
+        } else if (THING_TYPE_WEEKEND.equals(thingTypeUID)) {
+            return new WeekendHandler(thing, ephemerisManager, zoneId);
+        }
+
+        return null;
+    }
+}
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/configuration/DaysetConfiguration.java b/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/configuration/DaysetConfiguration.java
new file mode 100644 (file)
index 0000000..71d5538
--- /dev/null
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ephemeris.internal.configuration;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link DaysetConfiguration} class contains fields mapping Dayset Thing configuration parameters.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class DaysetConfiguration {
+    public String name = "";
+}
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/configuration/FileConfiguration.java b/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/configuration/FileConfiguration.java
new file mode 100644 (file)
index 0000000..7773f3f
--- /dev/null
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ephemeris.internal.configuration;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link FileConfiguration} class contains fields mapping File Thing configuration parameters.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class FileConfiguration {
+    public String fileName = "";
+}
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/discovery/EphemerisDiscoveryService.java b/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/discovery/EphemerisDiscoveryService.java
new file mode 100644 (file)
index 0000000..152a802
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ephemeris.internal.discovery;
+
+import static org.openhab.binding.ephemeris.internal.EphemerisBindingConstants.*;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.config.discovery.AbstractDiscoveryService;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.config.discovery.DiscoveryService;
+import org.openhab.core.i18n.LocaleProvider;
+import org.openhab.core.i18n.TranslationProvider;
+import org.openhab.core.thing.ThingUID;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link EphemerisDiscoveryService} creates default available Ephemeris Things.
+ *
+ * @author Gaël L'hopital - Initial Contribution
+ */
+@NonNullByDefault
+@Component(service = DiscoveryService.class, configurationPid = "discovery.ephemeris")
+public class EphemerisDiscoveryService extends AbstractDiscoveryService {
+    private static final int DISCOVER_TIMEOUT_SECONDS = 2;
+    private static final String LOCAL = "local";
+    private static final ThingUID HOLIDAY_THING = new ThingUID(THING_TYPE_HOLIDAY, LOCAL);
+    private static final ThingUID WEEKEND_THING = new ThingUID(THING_TYPE_WEEKEND, LOCAL);
+
+    private final Logger logger = LoggerFactory.getLogger(EphemerisDiscoveryService.class);
+
+    private Optional<ScheduledFuture<?>> discoveryJob = Optional.empty();
+
+    @Activate
+    public EphemerisDiscoveryService(final @Reference LocaleProvider localeProvider,
+            final @Reference TranslationProvider i18nProvider, @Nullable Map<String, Object> configProperties) {
+        super(Set.of(THING_TYPE_HOLIDAY, THING_TYPE_WEEKEND), DISCOVER_TIMEOUT_SECONDS, true);
+        this.localeProvider = localeProvider;
+        this.i18nProvider = i18nProvider;
+        activate(configProperties);
+    }
+
+    @Override
+    protected void startScan() {
+        logger.debug("Starting Ephemeris discovery scan");
+        createResults();
+    }
+
+    @Override
+    protected void startBackgroundDiscovery() {
+        discoveryJob = Optional.of(scheduler.schedule(this::createResults, 2, TimeUnit.SECONDS));
+    }
+
+    @Override
+    protected void stopBackgroundDiscovery() {
+        logger.debug("Stopping Ephemeris device background discovery");
+        discoveryJob.ifPresent(job -> job.cancel(true));
+        discoveryJob = Optional.empty();
+    }
+
+    public void createResults() {
+        thingDiscovered(DiscoveryResultBuilder.create(HOLIDAY_THING).build());
+        thingDiscovered(DiscoveryResultBuilder.create(WEEKEND_THING).build());
+    }
+}
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/BaseEphemerisHandler.java b/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/BaseEphemerisHandler.java
new file mode 100644 (file)
index 0000000..d4019ac
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ephemeris.internal.handler;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Optional;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.ephemeris.internal.EphemerisException;
+import org.openhab.core.ephemeris.EphemerisManager;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link BaseEphemerisHandler} is the base class for Ephemeris Things. It takes care of
+ * update logic and update scheduling once a day.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public abstract class BaseEphemerisHandler extends BaseThingHandler {
+    private static final int REFRESH_FIRST_HOUR_OF_DAY = 0;
+    private static final int REFRESH_FIRST_MINUTE_OF_DAY = 1;
+
+    private final Logger logger = LoggerFactory.getLogger(BaseEphemerisHandler.class);
+    private final ZoneId zoneId;
+    private Optional<ScheduledFuture<?>> refreshJob = Optional.empty();
+
+    protected final EphemerisManager ephemeris;
+
+    public BaseEphemerisHandler(Thing thing, EphemerisManager ephemerisManager, ZoneId zoneId) {
+        super(thing);
+        this.zoneId = zoneId;
+        this.ephemeris = ephemerisManager;
+    }
+
+    @Override
+    public void initialize() {
+        updateStatus(ThingStatus.UNKNOWN);
+        refreshJob = Optional.of(scheduler.schedule(this::updateData, 1, TimeUnit.SECONDS));
+    }
+
+    @Override
+    public void dispose() {
+        refreshJob.ifPresent(job -> job.cancel(true));
+        refreshJob = Optional.empty();
+        super.dispose();
+    }
+
+    private void updateData() {
+        ZonedDateTime now = ZonedDateTime.now().withZoneSameLocal(zoneId);
+
+        logger.debug("Updating {} channels", getThing().getUID());
+        try {
+            internalUpdate(now.truncatedTo(ChronoUnit.DAYS));
+
+            updateStatus(ThingStatus.ONLINE);
+            ZonedDateTime nextUpdate = now.plusDays(1).withHour(REFRESH_FIRST_HOUR_OF_DAY)
+                    .withMinute(REFRESH_FIRST_MINUTE_OF_DAY).truncatedTo(ChronoUnit.MINUTES);
+            long delay = ChronoUnit.MINUTES.between(now, nextUpdate);
+            logger.debug("Scheduling next {} update in {} minutes", getThing().getUID(), delay);
+            refreshJob = Optional.of(scheduler.schedule(this::updateData, delay, TimeUnit.MINUTES));
+        } catch (EphemerisException e) {
+            updateStatus(ThingStatus.OFFLINE, e.getStatusDetail(), e.getMessage());
+        }
+    }
+
+    protected abstract void internalUpdate(ZonedDateTime today) throws EphemerisException;
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+        if (RefreshType.REFRESH.equals(command)) {
+            updateData();
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/CustomHandler.java b/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/CustomHandler.java
new file mode 100644 (file)
index 0000000..b204f96
--- /dev/null
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ephemeris.internal.handler;
+
+import static org.openhab.binding.ephemeris.internal.EphemerisBindingConstants.*;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.ephemeris.internal.EphemerisException;
+import org.openhab.binding.ephemeris.internal.configuration.FileConfiguration;
+import org.openhab.core.ephemeris.EphemerisManager;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+
+/**
+ * The {@link CustomHandler} delivers user defined Jollyday definition events.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class CustomHandler extends JollydayHandler {
+    private Optional<File> definitionFile = Optional.empty();
+
+    public CustomHandler(Thing thing, EphemerisManager ephemerisManager, ZoneId zoneId) {
+        super(thing, ephemerisManager, zoneId);
+    }
+
+    @Override
+    public void initialize() {
+        String fileName = getConfigAs(FileConfiguration.class).fileName;
+
+        if (fileName.isBlank()) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+                    "'fileName' can not be blank or empty");
+            return;
+        }
+
+        File file = new File(BINDING_DATA_PATH, fileName);
+        if (!file.exists()) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+                    "Missing file: %s".formatted(file.getAbsolutePath()));
+            return;
+        }
+
+        definitionFile = Optional.of(file);
+        super.initialize();
+    }
+
+    @Override
+    protected void internalUpdate(ZonedDateTime today) throws EphemerisException {
+        String event = getEvent(today);
+        updateState(CHANNEL_EVENT_TODAY, OnOffType.from(event != null));
+
+        event = getEvent(today.plusDays(1));
+        updateState(CHANNEL_EVENT_TOMORROW, OnOffType.from(event != null));
+
+        super.internalUpdate(today);
+    }
+
+    @Override
+    protected @Nullable String getEvent(ZonedDateTime day) throws EphemerisException {
+        String path = definitionFile.get().getAbsolutePath();
+        try {
+            return ephemeris.getBankHolidayName(day, path);
+        } catch (IllegalStateException e) {
+            throw new EphemerisException("Incorrect syntax", ThingStatusDetail.NONE);
+        } catch (FileNotFoundException e) {
+            throw new EphemerisException("File is absent: " + path, ThingStatusDetail.CONFIGURATION_ERROR);
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/DaysetHandler.java b/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/DaysetHandler.java
new file mode 100644 (file)
index 0000000..343a31a
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ephemeris.internal.handler;
+
+import static org.openhab.binding.ephemeris.internal.EphemerisBindingConstants.*;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.ephemeris.internal.EphemerisException;
+import org.openhab.binding.ephemeris.internal.configuration.DaysetConfiguration;
+import org.openhab.core.ephemeris.EphemerisManager;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.Thing;
+
+/**
+ * The {@link DaysetHandler} delivers system default dayset data.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class DaysetHandler extends BaseEphemerisHandler {
+    private String dayset = "";
+
+    public DaysetHandler(Thing thing, EphemerisManager ephemerisManager, ZoneId zoneId) {
+        super(thing, ephemerisManager, zoneId);
+    }
+
+    @Override
+    public void initialize() {
+        DaysetConfiguration config = getConfigAs(DaysetConfiguration.class);
+        dayset = config.name;
+        super.initialize();
+    }
+
+    @Override
+    protected void internalUpdate(ZonedDateTime today) throws EphemerisException {
+        updateState(CHANNEL_TODAY, OnOffType.from(ephemeris.isInDayset(dayset, today)));
+        updateState(CHANNEL_TOMORROW, OnOffType.from(ephemeris.isInDayset(dayset, today.plusDays(1))));
+    }
+}
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/HolidayHandler.java b/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/HolidayHandler.java
new file mode 100644 (file)
index 0000000..83e546e
--- /dev/null
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ephemeris.internal.handler;
+
+import static org.openhab.binding.ephemeris.internal.EphemerisBindingConstants.*;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.ephemeris.internal.EphemerisException;
+import org.openhab.binding.ephemeris.internal.providers.EphemerisDescriptionProvider;
+import org.openhab.core.ephemeris.EphemerisManager;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.StateOption;
+
+/**
+ * The {@link HolidayHandler} delivers system default Holidays data.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class HolidayHandler extends JollydayHandler {
+
+    public HolidayHandler(Thing thing, EphemerisManager ephemerisManager, ZoneId zoneId,
+            EphemerisDescriptionProvider descriptionProvider) {
+        super(thing, ephemerisManager, zoneId);
+
+        // Search all holidays in the coming year, using a map to avoid duplicates
+        Map<String, StateOption> events = new HashMap<>();
+        ZonedDateTime now = ZonedDateTime.now();
+        // Scans 13 monthes to be sure to catch mobile holidays
+        for (int offset = 0; offset < 398; offset++) {
+            String event = getEvent(now.plusDays(offset));
+            if (event != null) {
+                String description = ephemeris.getHolidayDescription(event);
+                events.put(event, new StateOption(event, description == null ? event : description));
+            }
+        }
+
+        // Set descriptions for these events
+        List<StateOption> stateOptions = events.values().stream().toList();
+        descriptionProvider.setStateOptions(new ChannelUID(thing.getUID(), CHANNEL_CURRENT_EVENT), stateOptions);
+        descriptionProvider.setStateOptions(new ChannelUID(thing.getUID(), CHANNEL_NEXT_EVENT), stateOptions);
+    }
+
+    @Override
+    protected void internalUpdate(ZonedDateTime today) throws EphemerisException {
+        updateState(CHANNEL_HOLIDAY_TODAY, OnOffType.from(getEvent(today) != null));
+        updateState(CHANNEL_HOLIDAY_TOMORROW, OnOffType.from(getEvent(today.plusDays(1)) != null));
+        super.internalUpdate(today);
+    }
+
+    @Override
+    protected @Nullable String getEvent(ZonedDateTime day) {
+        return ephemeris.getBankHolidayName(day);
+    }
+}
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/JollydayHandler.java b/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/JollydayHandler.java
new file mode 100644 (file)
index 0000000..8f5cee5
--- /dev/null
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ephemeris.internal.handler;
+
+import static org.openhab.binding.ephemeris.internal.EphemerisBindingConstants.*;
+
+import java.time.Duration;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.ephemeris.internal.EphemerisException;
+import org.openhab.core.ephemeris.EphemerisManager;
+import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
+
+/**
+ * The {@link JollydayHandler} handles common parts for Jollyday file based events
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public abstract class JollydayHandler extends BaseEphemerisHandler {
+
+    public JollydayHandler(Thing thing, EphemerisManager ephemerisManager, ZoneId zoneId) {
+        super(thing, ephemerisManager, zoneId);
+    }
+
+    @Override
+    protected void internalUpdate(ZonedDateTime today) throws EphemerisException {
+        String todayEvent = getEvent(today);
+        updateState(CHANNEL_CURRENT_EVENT, toStringType(todayEvent));
+
+        String nextEvent = null;
+        ZonedDateTime nextDay = today;
+
+        for (int offset = 1; offset < 366 && (nextEvent == null || nextEvent.isEmpty()); offset++) {
+            nextDay = today.plusDays(offset);
+            nextEvent = getEvent(nextDay);
+        }
+
+        updateState(CHANNEL_NEXT_EVENT, toStringType(nextEvent));
+        updateState(CHANNEL_NEXT_REMAINING,
+                nextEvent != null ? new QuantityType<>(Duration.between(today, nextDay).toDays(), Units.DAY)
+                        : UnDefType.UNDEF);
+        updateState(CHANNEL_NEXT_START, nextEvent != null ? new DateTimeType(nextDay) : UnDefType.UNDEF);
+    }
+
+    protected abstract @Nullable String getEvent(ZonedDateTime day) throws EphemerisException;
+
+    protected State toStringType(@Nullable String event) {
+        return event == null || event.isEmpty() ? UnDefType.NULL : new StringType(event);
+    }
+}
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/WeekendHandler.java b/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/handler/WeekendHandler.java
new file mode 100644 (file)
index 0000000..a2a5aea
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ephemeris.internal.handler;
+
+import static org.openhab.binding.ephemeris.internal.EphemerisBindingConstants.*;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.ephemeris.internal.EphemerisException;
+import org.openhab.core.ephemeris.EphemerisManager;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.Thing;
+
+/**
+ * The {@link WeekendHandler} delivers system default Weekend data.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class WeekendHandler extends BaseEphemerisHandler {
+
+    public WeekendHandler(Thing thing, EphemerisManager ephemerisManager, ZoneId zoneId) {
+        super(thing, ephemerisManager, zoneId);
+    }
+
+    @Override
+    protected void internalUpdate(ZonedDateTime today) throws EphemerisException {
+        updateState(CHANNEL_TODAY, OnOffType.from(ephemeris.isWeekend(today)));
+        updateState(CHANNEL_TOMORROW, OnOffType.from(ephemeris.isWeekend(today.plusDays(1))));
+    }
+}
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/providers/EphemerisDescriptionProvider.java b/bundles/org.openhab.binding.ephemeris/src/main/java/org/openhab/binding/ephemeris/internal/providers/EphemerisDescriptionProvider.java
new file mode 100644 (file)
index 0000000..f249faa
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ephemeris.internal.providers;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.events.EventPublisher;
+import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
+import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
+import org.openhab.core.thing.link.ItemChannelLinkRegistry;
+import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * Dynamic provider of state options while leaving other state description fields as original.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@Component(service = { DynamicStateDescriptionProvider.class, EphemerisDescriptionProvider.class })
+@NonNullByDefault
+public class EphemerisDescriptionProvider extends BaseDynamicStateDescriptionProvider {
+
+    @Activate
+    public EphemerisDescriptionProvider(final @Reference EventPublisher eventPublisher, //
+            final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, //
+            final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
+        this.eventPublisher = eventPublisher;
+        this.itemChannelLinkRegistry = itemChannelLinkRegistry;
+        this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
+    }
+}
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.ephemeris/src/main/resources/OH-INF/addon/addon.xml
new file mode 100644 (file)
index 0000000..a4173aa
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<addon:addon id="ephemeris" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
+
+       <type>binding</type>
+       <name>Ephemeris Binding</name>
+       <description>The Ephemeris Binding makes Ephemeris core actions accessible to Items</description>
+       <connection>none</connection>
+
+</addon:addon>
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/resources/OH-INF/i18n/ephemeris.properties b/bundles/org.openhab.binding.ephemeris/src/main/resources/OH-INF/i18n/ephemeris.properties
new file mode 100644 (file)
index 0000000..d64ac72
--- /dev/null
@@ -0,0 +1,86 @@
+# add-on
+
+addon.ephemeris.name = Ephemeris Binding
+addon.ephemeris.description = The Ephemeris Binding makes Ephemeris core actions accessible to Items
+
+# thing types
+
+thing-type.ephemeris.custom.label = Custom Jollyday File
+thing-type.ephemeris.custom.description = Events defined in a Jollyday file
+thing-type.ephemeris.custom.channel.event-today.label = Event Today
+thing-type.ephemeris.custom.channel.event-today.description = Set to ON if an event exists today
+thing-type.ephemeris.custom.channel.event-tomorrow.label = Event Tomorrow
+thing-type.ephemeris.custom.channel.event-tomorrow.description = Set to ON if an event exists tomorrow
+thing-type.ephemeris.dayset.label = Dayset
+thing-type.ephemeris.dayset.description = Events based on a given dayset
+thing-type.ephemeris.dayset.channel.today.label = Today in Dayset
+thing-type.ephemeris.dayset.channel.today.description = Set to ON if today is in the given dayset, OFF in the other case
+thing-type.ephemeris.dayset.channel.tomorrow.label = Tomorrow in Dayset
+thing-type.ephemeris.dayset.channel.tomorrow.description = Set to ON if tomorrow is in the given dayset, OFF in the other case
+thing-type.ephemeris.holiday.label = Ephemeris Holidays
+thing-type.ephemeris.holiday.description = Holidays based on system default Ephemeris configuration
+thing-type.ephemeris.holiday.channel.holiday-today.label = Holiday Today
+thing-type.ephemeris.holiday.channel.holiday-today.description = Set to ON if today is a holiday
+thing-type.ephemeris.holiday.channel.holiday-tomorrow.label = Holiday Tomorrow
+thing-type.ephemeris.holiday.channel.holiday-tomorrow.description = Set to ON if tomorrow is a holiday
+thing-type.ephemeris.holiday.channel.next-title.description = Name of the next coming holiday
+thing-type.ephemeris.holiday.channel.title-today.description = Name of today's holiday if any, NULL otherwise
+thing-type.ephemeris.weekend.label = Weekend
+thing-type.ephemeris.weekend.description = Events based on the system default week-end dayset
+thing-type.ephemeris.weekend.channel.today.label = Weekend Today
+thing-type.ephemeris.weekend.channel.today.description = Set to ON if today is a weekend day, OFF in the other case
+thing-type.ephemeris.weekend.channel.tomorrow.label = Weekend Tomorrow
+thing-type.ephemeris.weekend.channel.tomorrow.description = Set to ON if tomorrow is a weekend day, OFF in the other case
+
+# thing types config
+
+thing-type.config.ephemeris.custom.fileName.label = File Name
+thing-type.config.ephemeris.custom.fileName.description = Name of a Jollyday XML file in the configuration folder.
+thing-type.config.ephemeris.dayset.name.label = Name
+thing-type.config.ephemeris.dayset.name.description = Name of the dayset.
+
+# channel types
+
+channel-type.ephemeris.days-remaining.label = Remaining Days
+channel-type.ephemeris.days-remaining.description = Remaining days until next event
+channel-type.ephemeris.event-current-title.label = Current Event Title
+channel-type.ephemeris.event-current-title.description = Title of the currently present event
+channel-type.ephemeris.event-next-start.label = Next Event Start
+channel-type.ephemeris.event-next-start.description = Start date of the next coming event
+channel-type.ephemeris.event-next-start.state.pattern = %1$tY-%1$tm-%1$td
+channel-type.ephemeris.event-next-title.label = Next Event Title
+channel-type.ephemeris.event-next-title.description = Title of the next starting event
+channel-type.ephemeris.in-dayset.label = In Dayset
+
+# channel types
+
+channel-type.ephemeris.in-dayset.description = Set to ON if the day is in the dayset, OFF in the other case
+
+# thing types
+
+thing-type.ephemeris.file.label = Ephemeris File
+thing-type.ephemeris.file.description = Events defined in a Jollyday file
+thing-type.ephemeris.file.channel.event-today.label = Event Today
+thing-type.ephemeris.file.channel.event-today.description = Set to ON if an event exists today
+thing-type.ephemeris.file.channel.event-tomorrow.label = Event Tomorrow
+thing-type.ephemeris.file.channel.event-tomorrow.description = Set to ON if an event exists tomorrow
+
+# thing types config
+
+thing-type.config.ephemeris.file.fileName.label = File Name
+thing-type.config.ephemeris.file.fileName.description = Name of a Jollyday XML file in the binding folder.
+
+# channel types
+
+channel-type.ephemeris.remaining-days.label = Remaining Days
+channel-type.ephemeris.remaining-days.description = Days until next event
+
+# thing types
+
+thing-type.ephemeris.default.label = Ephemeris
+thing-type.ephemeris.default.description = Events based on system default Ephemeris configuration
+
+# discovery result
+
+discovery.ephemeris.holiday.local.label = Local Holiday
+discovery.ephemeris.weekend.local.label = Local Weekend
diff --git a/bundles/org.openhab.binding.ephemeris/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.ephemeris/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644 (file)
index 0000000..b0bb17f
--- /dev/null
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="ephemeris"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+       <thing-type id="custom">
+               <label>Custom Jollyday File</label>
+               <description>Events defined in a Jollyday file</description>
+
+               <channels>
+                       <channel id="title-today" typeId="event-current-title"/>
+                       <channel id="event-today" typeId="in-dayset">
+                               <label>Event Today</label>
+                               <description>Set to ON if an event exists today</description>
+                       </channel>
+                       <channel id="event-tomorrow" typeId="in-dayset">
+                               <label>Event Tomorrow</label>
+                               <description>Set to ON if an event exists tomorrow</description>
+                       </channel>
+                       <channel id="next-title" typeId="event-next-title"/>
+                       <channel id="next-start" typeId="event-next-start"/>
+                       <channel id="days-remaining" typeId="days-remaining"/>
+               </channels>
+
+               <config-description>
+                       <parameter name="fileName" type="text" required="true">
+                               <label>File Name</label>
+                               <description>Name of a Jollyday XML file in the configuration folder.</description>
+                       </parameter>
+               </config-description>
+
+       </thing-type>
+
+       <thing-type id="holiday">
+               <label>Ephemeris Holidays</label>
+               <description>Holidays based on system default Ephemeris configuration</description>
+
+               <channels>
+                       <channel id="title-today" typeId="event-current-title">
+                               <description>Name of today's holiday if any, NULL otherwise </description>
+                       </channel>
+                       <channel id="holiday-today" typeId="in-dayset">
+                               <label>Holiday Today</label>
+                               <description>Set to ON if today is a holiday</description>
+                       </channel>
+                       <channel id="holiday-tomorrow" typeId="in-dayset">
+                               <label>Holiday Tomorrow</label>
+                               <description>Set to ON if tomorrow is a holiday</description>
+                       </channel>
+                       <channel id="next-title" typeId="event-next-title">
+                               <description>Name of the next coming holiday</description>
+                       </channel>
+                       <channel id="next-start" typeId="event-next-start"/>
+                       <channel id="days-remaining" typeId="days-remaining"/>
+               </channels>
+       </thing-type>
+
+       <thing-type id="dayset">
+               <label>Dayset</label>
+               <description>Events based on a given dayset</description>
+
+               <channels>
+                       <channel id="today" typeId="in-dayset">
+                               <label>Today In Dayset</label>
+                               <description>Set to ON if today is in the given dayset, OFF in the other case</description>
+                       </channel>
+                       <channel id="tomorrow" typeId="in-dayset">
+                               <label>Tomorrow In Dayset</label>
+                               <description>Set to ON if tomorrow is in the given dayset, OFF in the other case</description>
+                       </channel>
+               </channels>
+
+               <config-description>
+                       <parameter name="name" type="text" required="true">
+                               <label>Name</label>
+                               <description>Name of the dayset.</description>
+                       </parameter>
+               </config-description>
+
+       </thing-type>
+
+       <thing-type id="weekend">
+               <label>Weekend</label>
+               <description>Events based on the system default weekend dayset</description>
+
+               <channels>
+                       <channel id="today" typeId="in-dayset">
+                               <label>Weekend Today</label>
+                               <description>Set to ON if today is a weekend day, OFF in the other case</description>
+                       </channel>
+                       <channel id="tomorrow" typeId="in-dayset">
+                               <label>Weekend Tomorrow</label>
+                               <description>Set to ON if tomorrow is a weekend day, OFF in the other case</description>
+                       </channel>
+               </channels>
+       </thing-type>
+
+       <channel-type id="in-dayset">
+               <item-type>Switch</item-type>
+               <label>In Dayset</label>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="event-current-title">
+               <item-type>String</item-type>
+               <label>Current Event Title</label>
+               <description>Title of the currently present event</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="event-next-title">
+               <item-type>String</item-type>
+               <label>Next Event Title</label>
+               <description>Title of the next starting event</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="event-next-start">
+               <item-type>DateTime</item-type>
+               <label>Next Event Start</label>
+               <description>Start date of the next coming event</description>
+               <state readOnly="true" pattern="%1$tY-%1$tm-%1$td"/>
+       </channel-type>
+
+       <channel-type id="days-remaining">
+               <item-type>Number:Time</item-type>
+               <label>Remaining Days</label>
+               <description>Remaining days until next event</description>
+               <state pattern="%d %unit%" readOnly="true"/>
+       </channel-type>
+
+</thing:thing-descriptions>
index 456712dea36270d6e7371480c9349c9d6351e0e6..4d133db1b15b9cd55cd38cf620f3713a7a50d9e4 100644 (file)
     <module>org.openhab.binding.enocean</module>
     <module>org.openhab.binding.enphase</module>
     <module>org.openhab.binding.enturno</module>
+    <module>org.openhab.binding.ephemeris</module>
     <module>org.openhab.binding.epsonprojector</module>
     <module>org.openhab.binding.etherrain</module>
     <module>org.openhab.binding.evcc</module>