Skip to content

Commit

Permalink
[astro] Introducing AstroActions service. (openhab#8021)
Browse files Browse the repository at this point in the history
* Introducing AstroActions service.

Signed-off-by: Gaël L'hopital <gael@lhopital.org>
  • Loading branch information
clinique authored and andrewfg committed Aug 31, 2020
1 parent 05bea92 commit 0f8053d
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 21 deletions.
60 changes: 60 additions & 0 deletions bundles/org.openhab.binding.astro/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,66 @@ then
end
```

## Rule Actions

Multiple actions are supported by this binding. In classic rules these are accessible as shown in the example below:

Getting sunActions variable in scripts

```
val sunActions = getActions("astro","astro:sun:local")
if(null === sunActions) {
logInfo("actions", "sunActions not found, check thing ID")
return
} else {
// do something with sunActions
}
```

### getEventTime(sunPhaseName, moment, date)

Retrieves date and time (ZonedDateTime) of the requested phase name.
Thing method only applies to Sun thing type.

* `sunPhaseName` (String), values: `SUN_RISE, ASTRO_DAWN, NAUTIC_DAWN, CIVIL_DAWN, CIVIL_DUSK, NAUTIC_DUSK, ASTRO_DUSK, SUN_SET, DAYLIGHT, NIGHT`. Mandatory.

* `date` (ZonedDateTime), only the date part of this parameter will be considered - defaulted to now() if null.

* `moment` (String), values: `START, END` - defaulted to `START` if null.

Example :

```
val sunEvent = "SUN_SET"
val today = ZonedDateTime.now;
val sunEventTime = sunActions.getEventTime(sunEvent,today,"START")
logInfo("AstroActions","{} will happen at : {}", sunEvent, sunEventTime.toString)
```

### getElevation(timeStamp)

Retrieves the elevation (QuantityType<Angle>) of the sun at the requested instant.
Thing method applies to Sun and Moon.

* `timeStamp` (ZonedDateTime) - defaulted to now() if null.


### getAzimuth(timeStamp)

Retrieves the azimuth (QuantityType<Angle>) of the sun at the requested instant.
Thing method applies to Sun and Moon.

* `timeStamp` (ZonedDateTime) - defaulted to now() if null.

Example :

```
val azimuth = sunActions.getAzimuth(sunEventTime)
val elevation = sunActions.getElevation(sunEventTime)
logInfo("AstroActions", "{} will be positioned at elevation {} - azimuth {}",sunEvent, elevation.toString,azimuth.toString)
```


## Tips

Do not worry if for example the "astro dawn" is undefined at your location.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/**
* Copyright (c) 2010-2020 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.astro.internal.action;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.time.ZonedDateTime;

import javax.measure.quantity.Angle;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.library.types.QuantityType;
import org.eclipse.smarthome.core.thing.binding.ThingActions;
import org.eclipse.smarthome.core.thing.binding.ThingActionsScope;
import org.eclipse.smarthome.core.thing.binding.ThingHandler;
import org.openhab.binding.astro.internal.AstroBindingConstants;
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
import org.openhab.binding.astro.internal.handler.SunHandler;
import org.openhab.binding.astro.internal.model.SunPhaseName;
import org.openhab.core.automation.annotation.ActionInput;
import org.openhab.core.automation.annotation.ActionOutput;
import org.openhab.core.automation.annotation.RuleAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The {AstroActions } defines rule actions for the Astro binding.
* <p>
* <b>Note:</b>The static method <b>invokeMethodOf</b> handles the case where
* the test <i>actions instanceof AstroActions</i> fails. This test can fail
* due to an issue in openHAB core v2.5.0 where the {@link AstroActions} class
* can be loaded by a different classloader than the <i>actions</i> instance.
*
* @author Gaël L'hopital - Initial contribution
*/
@ThingActionsScope(name = "astro")
@NonNullByDefault
public class AstroActions implements ThingActions, IAstroActions {

private final Logger logger = LoggerFactory.getLogger(AstroActions.class);
protected @Nullable AstroThingHandler handler;

public AstroActions() {
logger.debug("Astro actions service instanciated");
}

@Override
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof AstroThingHandler) {
this.handler = (AstroThingHandler) handler;
}
}

@Override
public @Nullable ThingHandler getThingHandler() {
return this.handler;
}

@Override
@RuleAction(label = "Astro : Get Azimuth", description = "Get the azimuth of the sun for a given time")
public @Nullable @ActionOutput(name = "getAzimuth", label = "Azimuth", type = "org.eclipse.smarthome.core.library.types.QuantityType<javax.measure.quantity.Angle>") QuantityType<Angle> getAzimuth(
@ActionInput(name = "date", label = "Date", required = false, description = "Considered date") @Nullable ZonedDateTime date) {
logger.debug("Astro action 'getAzimuth' called");
AstroThingHandler theHandler = this.handler;
if (theHandler != null) {
return theHandler.getAzimuth(date != null ? date : ZonedDateTime.now());
} else {
logger.info("Astro Action service ThingHandler is null!");
}
return null;
}

@Override
@RuleAction(label = "Astro : Get Elevation", description = "Get the Elevation of the sun for a given time")
public @Nullable @ActionOutput(name = "getElevation", label = "Elevation", type = "org.eclipse.smarthome.core.library.types.QuantityType<javax.measure.quantity.Angle>") QuantityType<Angle> getElevation(
@ActionInput(name = "date", label = "Date", required = false, description = "Considered date") @Nullable ZonedDateTime date) {
logger.debug("Astro action 'getElevation' called");
AstroThingHandler theHandler = this.handler;
if (theHandler != null) {
return theHandler.getElevation(date != null ? date : ZonedDateTime.now());
} else {
logger.info("Astro Action service ThingHandler is null!");
}
return null;
}

@Override
@RuleAction(label = "Sun : Get Event Time", description = "Get the date time of a given planet event")
public @Nullable @ActionOutput(name = "getEventTime", type = "java.time.ZonedDateTime") ZonedDateTime getEventTime(
@ActionInput(name = "phaseName", label = "Phase", required = true, description = "Requested phase") String phaseName,
@ActionInput(name = "date", label = "Date", required = false, description = "Considered date") @Nullable ZonedDateTime date,
@ActionInput(name = "moment", label = "Moment", required = false, defaultValue = "START", description = "Either START or END") @Nullable String moment) {
logger.debug("Sun action 'getEventTime' called");
try {
if (handler instanceof SunHandler) {
SunHandler handler = (SunHandler) this.handler;
SunPhaseName phase = SunPhaseName.valueOf(phaseName.toUpperCase());
return handler.getEventTime(phase, date != null ? date : ZonedDateTime.now(),
moment == null || AstroBindingConstants.EVENT_START.equalsIgnoreCase(moment));
} else {
logger.info("Astro Action service ThingHandler is not a SunHandler!");
}
} catch (IllegalArgumentException e) {
logger.info("Parameter {} is not a valid phase name", phaseName);
}
return null;
}

public static @Nullable QuantityType<Angle> getElevation(@Nullable ThingActions actions,
@Nullable ZonedDateTime date) {
return invokeMethodOf(actions).getElevation(date);
}

public static @Nullable QuantityType<Angle> getAzimuth(@Nullable ThingActions actions,
@Nullable ZonedDateTime date) {
return invokeMethodOf(actions).getAzimuth(date);
}

public static @Nullable ZonedDateTime getEventTime(@Nullable ThingActions actions, @Nullable String phaseName,
@Nullable ZonedDateTime date, @Nullable String moment) {
if (phaseName != null) {
return invokeMethodOf(actions).getEventTime(phaseName, date, moment);
} else {
throw new IllegalArgumentException("phaseName can not be null");
}
}

private static IAstroActions invokeMethodOf(@Nullable ThingActions actions) {
if (actions == null) {
throw new IllegalArgumentException("actions cannot be null");
}
if (actions.getClass().getName().equals(AstroActions.class.getName())) {
if (actions instanceof IAstroActions) {
return (IAstroActions) actions;
} else {
return (IAstroActions) Proxy.newProxyInstance(IAstroActions.class.getClassLoader(),
new Class[] { IAstroActions.class }, (Object proxy, Method method, Object[] args) -> {
Method m = actions.getClass().getDeclaredMethod(method.getName(),
method.getParameterTypes());
return m.invoke(actions, args);
});
}
}
throw new IllegalArgumentException("Actions is not an instance of AstroActions");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright (c) 2010-2020 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.astro.internal.action;

import java.time.ZonedDateTime;

import javax.measure.quantity.Angle;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.library.types.QuantityType;

/**
* The {@link IAstroActions} defines the interface for all thing actions supported by the binding.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public interface IAstroActions {
public @Nullable ZonedDateTime getEventTime(String phaseName, @Nullable ZonedDateTime date,
@Nullable String moment);

public @Nullable QuantityType<Angle> getAzimuth(@Nullable ZonedDateTime date);

public @Nullable QuantityType<Angle> getElevation(@Nullable ZonedDateTime date);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
import static org.eclipse.smarthome.core.types.RefreshType.REFRESH;

import java.lang.invoke.MethodHandles;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
Expand All @@ -27,25 +30,31 @@
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.measure.quantity.Angle;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.i18n.TimeZoneProvider;
import org.eclipse.smarthome.core.library.types.QuantityType;
import org.eclipse.smarthome.core.scheduler.CronScheduler;
import org.eclipse.smarthome.core.scheduler.ScheduledCompletableFuture;
import org.eclipse.smarthome.core.thing.Channel;
import org.eclipse.smarthome.core.thing.ChannelUID;
import org.eclipse.smarthome.core.thing.Thing;
import org.eclipse.smarthome.core.thing.ThingStatusDetail;
import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
import org.eclipse.smarthome.core.thing.binding.ThingHandlerService;
import org.eclipse.smarthome.core.types.Command;
import org.openhab.binding.astro.internal.action.AstroActions;
import org.openhab.binding.astro.internal.config.AstroChannelConfig;
import org.openhab.binding.astro.internal.config.AstroThingConfig;
import org.openhab.binding.astro.internal.job.Job;
import org.openhab.binding.astro.internal.job.PositionalJob;
import org.openhab.binding.astro.internal.model.Planet;
import org.openhab.binding.astro.internal.model.Position;
import org.openhab.binding.astro.internal.util.PropertyUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -318,7 +327,9 @@ private void tidyScheduledFutures() {
/**
* Calculates and publishes the daily Astro data.
*/
public abstract void publishDailyInfo();
public void publishDailyInfo() {
publishPositionalInfo();
}

/**
* Calculates and publishes the interval Astro data.
Expand All @@ -339,4 +350,22 @@ private void tidyScheduledFutures() {
* Returns the daily calculation {@link Job} (cannot be {@code null})
*/
protected abstract Job getDailyJob();

protected abstract @Nullable Position getPositionAt(ZonedDateTime date);

public @Nullable QuantityType<Angle> getAzimuth(ZonedDateTime date) {
Position position = getPositionAt(date);
return position != null ? position.getAzimuth() : null;
}

public @Nullable QuantityType<Angle> getElevation(ZonedDateTime date) {
Position position = getPositionAt(date);
return position != null ? position.getElevation() : null;
}

@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Collections.singletonList(AstroActions.class);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@

import static org.openhab.binding.astro.internal.AstroBindingConstants.THING_TYPE_MOON;

import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Set;

Expand All @@ -30,6 +32,7 @@
import org.openhab.binding.astro.internal.job.Job;
import org.openhab.binding.astro.internal.model.Moon;
import org.openhab.binding.astro.internal.model.Planet;
import org.openhab.binding.astro.internal.model.Position;

/**
* The MoonHandler is responsible for updating calculated moon data.
Expand All @@ -54,15 +57,9 @@ public MoonHandler(Thing thing, final CronScheduler scheduler, final TimeZonePro
super(thing, scheduler, timeZoneProvider);
}

@Override
public void publishDailyInfo() {
initializeMoon();
publishPositionalInfo();
}

@Override
public void publishPositionalInfo() {
initializeMoon();
moon = getMoonAt(ZonedDateTime.now());
Double latitude = thingConfig.latitude;
Double longitude = thingConfig.longitude;
moonCalc.setPositionalInfo(Calendar.getInstance(), latitude != null ? latitude : 0,
Expand Down Expand Up @@ -91,10 +88,21 @@ protected Job getDailyJob() {
return new DailyJobMoon(thing.getUID().getAsString(), this);
}

private void initializeMoon() {
private Moon getMoonAt(ZonedDateTime date) {
Double latitude = thingConfig.latitude;
Double longitude = thingConfig.longitude;
moon = moonCalc.getMoonInfo(Calendar.getInstance(), latitude != null ? latitude : 0,
return moonCalc.getMoonInfo(GregorianCalendar.from(date), latitude != null ? latitude : 0,
longitude != null ? longitude : 0);
}

@Override
protected @Nullable Position getPositionAt(ZonedDateTime date) {
Moon localMoon = getMoonAt(date);
Double latitude = thingConfig.latitude;
Double longitude = thingConfig.longitude;
moonCalc.setPositionalInfo(GregorianCalendar.from(date), latitude != null ? latitude : 0,
longitude != null ? longitude : 0, localMoon);
return localMoon.getPosition();
}

}
Loading

0 comments on commit 0f8053d

Please sign in to comment.