mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[pwm] Initial Contribution (#10205)
Signed-off-by: Fabian Wolter <github@fabian-wolter.de>
This commit is contained in:
parent
cf34ba23a0
commit
9d903c240e
@ -9,6 +9,7 @@
|
||||
/bundles/org.openhab.automation.jsscripting/ @jpg0
|
||||
/bundles/org.openhab.automation.jythonscripting/ @openhab/add-ons-maintainers
|
||||
/bundles/org.openhab.automation.pidcontroller/ @fwolter
|
||||
/bundles/org.openhab.automation.pwm/ @fwolter
|
||||
/bundles/org.openhab.binding.adorne/ @theiding
|
||||
/bundles/org.openhab.binding.ahawastecollection/ @soenkekueper
|
||||
/bundles/org.openhab.binding.airq/ @aurelio1
|
||||
|
@ -36,6 +36,11 @@
|
||||
<artifactId>org.openhab.automation.pidcontroller</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.automation.pwm</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.adorne</artifactId>
|
||||
|
13
bundles/org.openhab.automation.pwm/NOTICE
Normal file
13
bundles/org.openhab.automation.pwm/NOTICE
Normal file
@ -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
|
62
bundles/org.openhab.automation.pwm/README.md
Normal file
62
bundles/org.openhab.automation.pwm/README.md
Normal file
@ -0,0 +1,62 @@
|
||||
# Pulse Width Modulation (PWM) Automation
|
||||
|
||||
This automation module implements [Pulse Width Modulation (PWM)](https://en.wikipedia.org/wiki/Pulse-width_modulation).
|
||||
|
||||
PWM can be used to control actuators continuously from 0 to 100% that only support ON/OFF commands.
|
||||
E.g. valves or heating burners.
|
||||
It accomplishes that by switching the actuator on and off with a fixed interval.
|
||||
The higher the control percentage (duty cycle), the longer the ON phase.
|
||||
|
||||
Example: If you have an interval of 10 sec and the duty cycle is 30%, the output is ON for 3 sec and OFF for 7 sec.
|
||||
|
||||
This module is **unsuitable** for controlling LED lights as the high PWM frequency can't be met.
|
||||
|
||||
> Note: The module starts to work only if the duty cycle has been updated at least once.
|
||||
|
||||
## Modules
|
||||
|
||||
The PWM module can be used in openHAB's [rule engine](https://www.openhab.org/docs/configuration/rules-dsl.html).
|
||||
|
||||
This automation provides a trigger module ("PWM triggers") with one input Item: `dutycycleItem` (0-100%).
|
||||
The module calculates the ON/OFF state and returns it.
|
||||
The return value is used to feed the Action module "Item Action" aka "send a command", which controls the actuator.
|
||||
|
||||
To configure a rule, you need to add a Trigger ("PWM triggers") and an Action ("Item Action").
|
||||
Select the Item you like to control in the "Item Action" and leave the command empty.
|
||||
|
||||
### Trigger
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|-----------------|---------|----------------------------------------------------------------------------------------------|----------|
|
||||
| `dutycycleItem` | Item | The Item (PercentType) to read the duty cycle from | Yes |
|
||||
| `interval` | Decimal | The constant interval in which the output is switch ON and OFF again in sec. | Yes |
|
||||
| `minDutyCycle` | Decimal | Any duty cycle below this value will be increased to this value | No |
|
||||
| `maxDutycycle` | Decimal | Any duty cycle above this value will be decreased to this value | No |
|
||||
| `deadManSwitch` | Decimal | The output will be switched off, when the duty cycle is not updated within this time (in ms) | No |
|
||||
|
||||
The duty cycle can be limited via the parameters `minDutycycle` and `maxDutyCycle`.
|
||||
This is helpful if you need to maintain a minimum time between the switching of the output.
|
||||
This is necessary for example for heating burners, which may not be switched on for very short times.
|
||||
The on time is than increased to `minDutycycle`.
|
||||
In this case one should also set a max duty cycle to prevent short off times.
|
||||
It makes sense to apply these symmetrically e.g. 10%/90% or 20%/80%.
|
||||
|
||||
If the duty cycle is 0% or 100%, the min/max parameters are ignored and the output is switched ON or OFF continuously.
|
||||
|
||||
If the duty cycle Item is not updated within the dead-man switch timeout, the output is switched off, regardless of the current duty cycle.
|
||||
The function can be used to save energy if the source of the duty cycle died for whatever reason and doesn't update the value anymore.
|
||||
When the duty cycle is updated again, the module returns to normal operation.
|
||||
|
||||
> Note: The min/max ON/OFF times set via `minDutycycle` and `maxDutycycle` are not met if the dead-man switch triggers and recovers fast.
|
||||
|
||||
## Control Algorithm
|
||||
|
||||
This module is designed to respond fast to duty cycle changes, but at the same time maintain a constant interval and also the min/max ON/OFF parameters.
|
||||
For that reason, the module might seem to act peculiarly in some cases:
|
||||
|
||||
- When the output is ON and the duty cycle is decreased, the output might switch off immediately, if applicable.
|
||||
Example: The interval is 10 sec and the current duty cycle is 80%.
|
||||
When the duty cycle is decreased to 20%, the output would switch off immediately, if it has been already ON for more than 2 sec.
|
||||
- When the duty cycle is 0% for a short interval and then increased again, the output will only switch on when the new interval starts.
|
||||
- When the duty cycle is 0% or 100% for more than a whole interval, a new interval will start as soon as the duty cycle is updated to a value other than 0%, respective 100%.
|
||||
- The module starts to work only if the duty cycle Item has been updated at least once.
|
BIN
bundles/org.openhab.automation.pwm/doc/statemachine.odg
Normal file
BIN
bundles/org.openhab.automation.pwm/doc/statemachine.odg
Normal file
Binary file not shown.
BIN
bundles/org.openhab.automation.pwm/doc/statemachine.png
Normal file
BIN
bundles/org.openhab.automation.pwm/doc/statemachine.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 74 KiB |
17
bundles/org.openhab.automation.pwm/pom.xml
Normal file
17
bundles/org.openhab.automation.pwm/pom.xml
Normal file
@ -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 http://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>3.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.automation.pwm</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Automation :: PWM</name>
|
||||
|
||||
</project>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.automation.pwm-${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-automation-pwm" description="PWM Automation" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.automation.pwm/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Constants for the PWM automation module.
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PWMConstants {
|
||||
public static final String AUTOMATION_NAME = "pwm";
|
||||
|
||||
public static final String CONFIG_DUTY_CYCLE_ITEM = "dutycycleItem";
|
||||
public static final String CONFIG_PERIOD = "interval";
|
||||
public static final String CONFIG_MIN_DUTYCYCLE = "minDutycycle";
|
||||
public static final String CONFIG_MAX_DUTYCYCLE = "maxDutycycle";
|
||||
public static final String CONFIG_COMMAND_ITEM = "command";
|
||||
public static final String CONFIG_DEAD_MAN_SWITCH = "deadManSwitch";
|
||||
public static final String CONFIG_OUTPUT_ITEM = "outputItem";
|
||||
public static final String INPUT = "input";
|
||||
public static final String OUTPUT = "command";
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Common exception for the PWM automation module.
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PWMException extends Exception {
|
||||
private static final long serialVersionUID = -3029834022610530982L;
|
||||
|
||||
public PWMException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal.factory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.automation.pwm.internal.handler.PWMTriggerHandler;
|
||||
import org.openhab.core.automation.Module;
|
||||
import org.openhab.core.automation.Trigger;
|
||||
import org.openhab.core.automation.handler.BaseModuleHandlerFactory;
|
||||
import org.openhab.core.automation.handler.ModuleHandler;
|
||||
import org.openhab.core.automation.handler.ModuleHandlerFactory;
|
||||
import org.openhab.core.items.ItemRegistry;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* Factory for the PWM automation module.
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ModuleHandlerFactory.class, configurationPid = "automation.pwm")
|
||||
public class PWMModuleHandlerFactory extends BaseModuleHandlerFactory {
|
||||
private static final Collection<String> TYPES = Set.of(PWMTriggerHandler.MODULE_TYPE_ID);
|
||||
private ItemRegistry itemRegistry;
|
||||
private BundleContext bundleContext;
|
||||
|
||||
@Activate
|
||||
public PWMModuleHandlerFactory(@Reference ItemRegistry itemRegistry, BundleContext bundleContext) {
|
||||
this.itemRegistry = itemRegistry;
|
||||
this.bundleContext = bundleContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getTypes() {
|
||||
return TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ModuleHandler internalCreate(Module module, String ruleUID) {
|
||||
switch (module.getTypeUID()) {
|
||||
case PWMTriggerHandler.MODULE_TYPE_ID:
|
||||
return new PWMTriggerHandler((Trigger) module, itemRegistry, bundleContext);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,240 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal.handler;
|
||||
|
||||
import static org.openhab.automation.pwm.internal.PWMConstants.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
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.automation.pwm.internal.PWMException;
|
||||
import org.openhab.automation.pwm.internal.handler.state.StateMachine;
|
||||
import org.openhab.core.automation.ModuleHandlerCallback;
|
||||
import org.openhab.core.automation.Trigger;
|
||||
import org.openhab.core.automation.handler.BaseTriggerModuleHandler;
|
||||
import org.openhab.core.automation.handler.TriggerHandlerCallback;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.events.Event;
|
||||
import org.openhab.core.events.EventFilter;
|
||||
import org.openhab.core.events.EventSubscriber;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.ItemNotFoundException;
|
||||
import org.openhab.core.items.ItemRegistry;
|
||||
import org.openhab.core.items.events.ItemStateEvent;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Represents a Trigger module in the rules engine.
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PWMTriggerHandler extends BaseTriggerModuleHandler implements EventSubscriber {
|
||||
public static final String MODULE_TYPE_ID = AUTOMATION_NAME + ".trigger";
|
||||
private static final Set<String> SUBSCRIBED_EVENT_TYPES = Set.of(ItemStateEvent.TYPE);
|
||||
private final Logger logger = LoggerFactory.getLogger(PWMTriggerHandler.class);
|
||||
private final BundleContext bundleContext;
|
||||
private final EventFilter eventFilter;
|
||||
private final Optional<Double> minDutyCycle;
|
||||
private final Optional<Double> maxDutyCycle;
|
||||
private final Optional<Double> deadManSwitchTimeoutMs;
|
||||
private final Item dutyCycleItem;
|
||||
private @Nullable ServiceRegistration<?> eventSubscriberRegistration;
|
||||
private @Nullable ScheduledFuture<?> deadMeanSwitchTimer;
|
||||
private @Nullable StateMachine stateMachine;
|
||||
|
||||
public PWMTriggerHandler(Trigger module, ItemRegistry itemRegistry, BundleContext bundleContext) {
|
||||
super(module);
|
||||
this.bundleContext = bundleContext;
|
||||
|
||||
Configuration config = module.getConfiguration();
|
||||
|
||||
String dutycycleItemName = (String) Objects.requireNonNull(config.get(CONFIG_DUTY_CYCLE_ITEM),
|
||||
"DutyCycle item is not set");
|
||||
|
||||
minDutyCycle = getOptionalDoubleFromConfig(config, CONFIG_MIN_DUTYCYCLE);
|
||||
maxDutyCycle = getOptionalDoubleFromConfig(config, CONFIG_MAX_DUTYCYCLE);
|
||||
deadManSwitchTimeoutMs = getOptionalDoubleFromConfig(config, CONFIG_DEAD_MAN_SWITCH);
|
||||
|
||||
try {
|
||||
dutyCycleItem = itemRegistry.getItem(dutycycleItemName);
|
||||
} catch (ItemNotFoundException e) {
|
||||
throw new IllegalArgumentException("Dutycycle item not found: " + dutycycleItemName, e);
|
||||
}
|
||||
|
||||
eventFilter = event -> event.getTopic().equals("openhab/items/" + dutycycleItemName + "/state");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCallback(ModuleHandlerCallback callback) {
|
||||
super.setCallback(callback);
|
||||
|
||||
double periodSec = getDoubleFromConfig(module.getConfiguration(), CONFIG_PERIOD);
|
||||
stateMachine = new StateMachine(getCallback().getScheduler(), this::setOutput, (long) (periodSec * 1000));
|
||||
|
||||
eventSubscriberRegistration = bundleContext.registerService(EventSubscriber.class.getName(), this, null);
|
||||
}
|
||||
|
||||
private double getDoubleFromConfig(Configuration config, String key) {
|
||||
return ((BigDecimal) Objects.requireNonNull(config.get(key), key + " is not set")).doubleValue();
|
||||
}
|
||||
|
||||
private Optional<Double> getOptionalDoubleFromConfig(Configuration config, String key) {
|
||||
Object o = config.get(key);
|
||||
|
||||
if (o instanceof BigDecimal) {
|
||||
return Optional.of(((BigDecimal) o).doubleValue());
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(Event event) {
|
||||
if (!(event instanceof ItemStateEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStateEvent changedEvent = (ItemStateEvent) event;
|
||||
synchronized (this) {
|
||||
try {
|
||||
double newDutycycle = getDutyCycleValueInPercent(changedEvent.getItemState());
|
||||
double newDutycycleBeforeLimit = newDutycycle;
|
||||
|
||||
restartDeadManSwitchTimer();
|
||||
|
||||
// set duty cycle to min duty cycle if it is smaller than min duty cycle
|
||||
// set duty cycle to 0% if it is 0%, regardless of the min duty cycle
|
||||
final double newDutyCycleFinal1 = newDutycycle;
|
||||
newDutycycle = minDutyCycle.map(minDutycycle -> {
|
||||
if (Math.round(newDutyCycleFinal1) <= 0) {
|
||||
return 0d;
|
||||
} else {
|
||||
return Math.max(minDutycycle, newDutyCycleFinal1);
|
||||
}
|
||||
}).orElse(newDutycycle);
|
||||
|
||||
// set duty cycle to 100% if the current duty cycle is larger than the max duty cycle
|
||||
final double newDutyCycleFinal2 = newDutycycle;
|
||||
newDutycycle = maxDutyCycle.map(maxDutycycle -> {
|
||||
if (Math.round(newDutyCycleFinal2) >= maxDutycycle) {
|
||||
return 100d;
|
||||
} else {
|
||||
return newDutyCycleFinal2;
|
||||
}
|
||||
}).orElse(newDutycycle);
|
||||
|
||||
logger.debug("Received new duty cycle: {} {}", newDutycycleBeforeLimit,
|
||||
newDutycycle != newDutycycleBeforeLimit ? "Limited to: " + newDutycycle : "");
|
||||
|
||||
StateMachine localStateMachine = stateMachine;
|
||||
if (localStateMachine != null) {
|
||||
localStateMachine.setDutycycle(newDutycycle);
|
||||
} else {
|
||||
logger.debug("Initialization not finished");
|
||||
}
|
||||
} catch (PWMException e) {
|
||||
logger.warn("{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void restartDeadManSwitchTimer() {
|
||||
ScheduledFuture<?> timer = deadMeanSwitchTimer;
|
||||
if (timer != null) {
|
||||
timer.cancel(true);
|
||||
}
|
||||
|
||||
deadManSwitchTimeoutMs.ifPresent(timeout -> {
|
||||
deadMeanSwitchTimer = getCallback().getScheduler().schedule(this::activateDeadManSwitch,
|
||||
timeout.longValue(), TimeUnit.MILLISECONDS);
|
||||
});
|
||||
}
|
||||
|
||||
private void activateDeadManSwitch() {
|
||||
logger.warn("Dead-man switch activated. Disabling output");
|
||||
|
||||
StateMachine localStateMachine = stateMachine;
|
||||
if (localStateMachine != null) {
|
||||
localStateMachine.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void setOutput(boolean enable) {
|
||||
getCallback().triggered(module, Collections.singletonMap(OUTPUT, OnOffType.from(enable)));
|
||||
}
|
||||
|
||||
private TriggerHandlerCallback getCallback() {
|
||||
ModuleHandlerCallback localCallback = callback;
|
||||
if (localCallback != null && localCallback instanceof TriggerHandlerCallback) {
|
||||
return (TriggerHandlerCallback) localCallback;
|
||||
}
|
||||
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
private double getDutyCycleValueInPercent(State state) throws PWMException {
|
||||
if (state instanceof DecimalType) {
|
||||
return ((DecimalType) state).doubleValue();
|
||||
} else if (state instanceof StringType) {
|
||||
try {
|
||||
return Integer.parseInt(state.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
// nothing
|
||||
}
|
||||
} else if (state instanceof UnDefType) {
|
||||
throw new PWMException("Duty cycle item '" + dutyCycleItem.getName() + "' has no valid value");
|
||||
}
|
||||
throw new PWMException("Duty cycle item not of type DecimalType: " + state.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSubscribedEventTypes() {
|
||||
return SUBSCRIBED_EVENT_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable EventFilter getEventFilter() {
|
||||
return eventFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
ServiceRegistration<?> localEventSubscriberRegistration = eventSubscriberRegistration;
|
||||
if (localEventSubscriberRegistration != null) {
|
||||
localEventSubscriberRegistration.unregister();
|
||||
}
|
||||
|
||||
StateMachine localStateMachine = stateMachine;
|
||||
if (localStateMachine != null) {
|
||||
localStateMachine.stop();
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal.handler.state;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Active when, the duty cycle is 0% for at least a whole period.
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AlwaysOffState extends State {
|
||||
public AlwaysOffState(StateMachine context) {
|
||||
super(context);
|
||||
|
||||
controlOutput(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dutyCycleChanged() {
|
||||
if (Math.round(context.getDutycycle()) >= 100) {
|
||||
nextState(DutycycleHundredState::new);
|
||||
} else {
|
||||
nextState(OnState::new);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dutyCycleUpdated() {
|
||||
// in case we came here by the dead-man switch
|
||||
if (Math.round(context.getDutycycle()) > 0) {
|
||||
nextState(OnState::new);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
// nothing
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal.handler.state;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Active when, the duty cycle is 100% for at least a whole period.
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AlwaysOnState extends State {
|
||||
public AlwaysOnState(StateMachine context) {
|
||||
super(context);
|
||||
|
||||
controlOutput(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dutyCycleChanged() {
|
||||
nextState(OffState::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dutyCycleUpdated() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
// nothing
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal.handler.state;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Active when, the PWM period ended with a duty cycle set to 100%.
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DutycycleHundredState extends State {
|
||||
private ScheduledFuture<?> periodTimer;
|
||||
private @Nullable ScheduledFuture<?> offTimer;
|
||||
private Instant enabledAt = Instant.now();
|
||||
private boolean dutyCycleChanged;
|
||||
|
||||
public DutycycleHundredState(StateMachine context) {
|
||||
super(context);
|
||||
|
||||
controlOutput(true);
|
||||
|
||||
periodTimer = scheduler.schedule(this::periodEnded, context.getPeriodMs(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void periodEnded() {
|
||||
long dutycycleRounded = Math.round(context.getDutycycle());
|
||||
|
||||
if (!dutyCycleChanged && dutycycleRounded <= 0) {
|
||||
nextState(AlwaysOffState::new);
|
||||
} else if (!dutyCycleChanged && dutycycleRounded >= 100) {
|
||||
nextState(AlwaysOnState::new);
|
||||
} else {
|
||||
nextState(OnState::new);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dutyCycleChanged() {
|
||||
dutyCycleChanged = true;
|
||||
|
||||
long newOnTimeMs = calculateOnTimeMs(context.getDutycycle());
|
||||
long elapsedMs = enabledAt.until(Instant.now(), ChronoUnit.MILLIS);
|
||||
|
||||
if (elapsedMs - newOnTimeMs > 0) {
|
||||
controlOutput(false);
|
||||
} else {
|
||||
ScheduledFuture<?> timer = offTimer;
|
||||
if (timer != null) {
|
||||
timer.cancel(false);
|
||||
}
|
||||
offTimer = scheduler.schedule(() -> controlOutput(false), newOnTimeMs - elapsedMs, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dutyCycleUpdated() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
periodTimer.cancel(false);
|
||||
|
||||
ScheduledFuture<?> timer = offTimer;
|
||||
if (timer != null) {
|
||||
timer.cancel(false);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal.handler.state;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Active when, the PWM period ended with a duty cycle set to 0%.
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DutycycleZeroState extends State {
|
||||
private ScheduledFuture<?> periodTimer;
|
||||
|
||||
public DutycycleZeroState(StateMachine context) {
|
||||
super(context);
|
||||
|
||||
controlOutput(false);
|
||||
|
||||
periodTimer = scheduler.schedule(this::periodEnded, context.getPeriodMs(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void periodEnded() {
|
||||
long dutycycleRounded = Math.round(context.getDutycycle());
|
||||
|
||||
if (dutycycleRounded <= 0) {
|
||||
nextState(AlwaysOffState::new);
|
||||
} else if (dutycycleRounded >= 100) {
|
||||
nextState(DutycycleHundredState::new);
|
||||
} else {
|
||||
nextState(OnState::new);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dutyCycleChanged() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dutyCycleUpdated() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
periodTimer.cancel(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal.handler.state;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Active when, the output is currently OFF and the duty cycle is between 0% and 100% (exclusively).
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OffState extends State {
|
||||
ScheduledFuture<?> offTimer;
|
||||
|
||||
public OffState(StateMachine context) {
|
||||
super(context);
|
||||
|
||||
controlOutput(false);
|
||||
|
||||
long offTimeMs = context.getPeriodMs() - calculateOnTimeMs(context.getDutycycle());
|
||||
offTimer = scheduler.schedule(this::periodEnded, offTimeMs, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void periodEnded() {
|
||||
long dutycycleRounded = Math.round(context.getDutycycle());
|
||||
|
||||
if (dutycycleRounded <= 0) {
|
||||
nextState(DutycycleZeroState::new);
|
||||
} else if (dutycycleRounded >= 100) {
|
||||
nextState(DutycycleHundredState::new);
|
||||
} else {
|
||||
nextState(OnState::new);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dutyCycleChanged() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dutyCycleUpdated() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
offTimer.cancel(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal.handler.state;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Active when, the output is currently ON and the duty cycle is between 0% and 100% (exclusively).
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OnState extends State {
|
||||
private @NonNullByDefault({}) ScheduledFuture<?> offTimer;
|
||||
private Instant enabledAt = Instant.now();
|
||||
|
||||
public OnState(StateMachine context) {
|
||||
super(context);
|
||||
|
||||
context.controlOutput(true);
|
||||
|
||||
startOnTimer(calculateOnTimeMs(context.getDutycycle()));
|
||||
}
|
||||
|
||||
private void startOnTimer(long timeMs) {
|
||||
offTimer = scheduler.schedule(() -> {
|
||||
if (Math.round(context.getDutycycle()) >= 100) {
|
||||
nextState(DutycycleHundredState::new);
|
||||
} else {
|
||||
nextState(OffState::new);
|
||||
}
|
||||
}, timeMs, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dutyCycleChanged() {
|
||||
// end current ON phase prematurely or extend it if the new duty cycle demands it
|
||||
offTimer.cancel(false);
|
||||
|
||||
long newOnTimeMs = calculateOnTimeMs(context.getDutycycle());
|
||||
long elapsedMs = enabledAt.until(Instant.now(), ChronoUnit.MILLIS);
|
||||
|
||||
if (elapsedMs - newOnTimeMs > 0) {
|
||||
nextState(OffState::new);
|
||||
} else {
|
||||
startOnTimer(newOnTimeMs - elapsedMs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dutyCycleUpdated() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
offTimer.cancel(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal.handler.state;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The base class of all states.
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class State {
|
||||
private final Logger logger = LoggerFactory.getLogger(State.class);
|
||||
protected StateMachine context;
|
||||
protected ScheduledExecutorService scheduler;
|
||||
|
||||
public State(StateMachine context) {
|
||||
this.context = context;
|
||||
this.scheduler = context.getScheduler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the duty cycle updated and changed.
|
||||
*/
|
||||
public abstract void dutyCycleChanged();
|
||||
|
||||
/**
|
||||
* Invoked when the duty cycle updated.
|
||||
*/
|
||||
protected abstract void dutyCycleUpdated();
|
||||
|
||||
public abstract void dispose();
|
||||
|
||||
/**
|
||||
* Sets a new state in the state machine.
|
||||
*/
|
||||
public synchronized void nextState(Function<StateMachine, ? extends State> nextState) {
|
||||
if (context.getState() != this) { // compare identity
|
||||
return;
|
||||
}
|
||||
|
||||
context.getState().dispose();
|
||||
State newState = nextState.apply(context);
|
||||
|
||||
logger.trace("{} -> {}", context.getState().getClass().getSimpleName(), newState.getClass().getSimpleName());
|
||||
|
||||
context.setState(newState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the ON duration by the duty cycle.
|
||||
*
|
||||
* @param dutyCycleInPercent the duty cycle in percent
|
||||
* @return the ON duration in ms
|
||||
*/
|
||||
protected long calculateOnTimeMs(double dutyCycleInPercent) {
|
||||
return (long) (context.getPeriodMs() / 100 * dutyCycleInPercent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the output on or off.
|
||||
*
|
||||
* @param on true, if the output shall be switched on.
|
||||
*/
|
||||
protected void controlOutput(boolean on) {
|
||||
context.controlOutput(on);
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal.handler.state;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The context of all states.
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class StateMachine {
|
||||
private ScheduledExecutorService scheduler;
|
||||
private Consumer<Boolean> controlOutput;
|
||||
private State state;
|
||||
private long periodMs;
|
||||
private double dutycycle;
|
||||
|
||||
public StateMachine(ScheduledExecutorService scheduler, Consumer<Boolean> controlOutput, long periodMs) {
|
||||
this.scheduler = scheduler;
|
||||
this.controlOutput = controlOutput;
|
||||
this.periodMs = periodMs;
|
||||
this.state = new AlwaysOffState(this);
|
||||
}
|
||||
|
||||
public ScheduledExecutorService getScheduler() {
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
public void setDutycycle(double newDutycycle) {
|
||||
if (dutycycle != newDutycycle) {
|
||||
this.dutycycle = newDutycycle;
|
||||
state.dutyCycleChanged();
|
||||
}
|
||||
|
||||
state.dutyCycleUpdated();
|
||||
}
|
||||
|
||||
public double getDutycycle() {
|
||||
return dutycycle;
|
||||
}
|
||||
|
||||
public long getPeriodMs() {
|
||||
return periodMs;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(State current) {
|
||||
this.state = current;
|
||||
}
|
||||
|
||||
public void controlOutput(boolean on) {
|
||||
controlOutput.accept(on);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
state.nextState(OnState::new);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
state.nextState(AlwaysOffState::new);
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal.template;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.automation.pwm.internal.PWMConstants;
|
||||
import org.openhab.automation.pwm.internal.type.PWMTriggerType;
|
||||
import org.openhab.core.automation.Action;
|
||||
import org.openhab.core.automation.Condition;
|
||||
import org.openhab.core.automation.Trigger;
|
||||
import org.openhab.core.automation.Visibility;
|
||||
import org.openhab.core.automation.template.RuleTemplate;
|
||||
import org.openhab.core.automation.util.ModuleBuilder;
|
||||
import org.openhab.core.config.core.ConfigDescriptionParameter;
|
||||
|
||||
/**
|
||||
* Rule template for the PWM automation module.
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PWMRuleTemplate extends RuleTemplate {
|
||||
public static final String UID = "PWMRuleTemplate";
|
||||
|
||||
public static PWMRuleTemplate initialize() {
|
||||
final String triggerId = UUID.randomUUID().toString();
|
||||
|
||||
final List<Trigger> triggers = Collections.singletonList(ModuleBuilder.createTrigger().withId(triggerId)
|
||||
.withTypeUID(PWMTriggerType.UID).withLabel("PWM Trigger").build());
|
||||
|
||||
final Map<String, String> actionInputs = new HashMap<String, String>();
|
||||
actionInputs.put(PWMConstants.INPUT, triggerId + "." + PWMConstants.OUTPUT);
|
||||
|
||||
Set<String> tags = new HashSet<String>();
|
||||
tags.add("PWM");
|
||||
|
||||
return new PWMRuleTemplate(tags, triggers, Collections.emptyList(), Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
}
|
||||
|
||||
public PWMRuleTemplate(Set<String> tags, List<Trigger> triggers, List<Condition> conditions, List<Action> actions,
|
||||
List<ConfigDescriptionParameter> configDescriptions) {
|
||||
super(UID, "PWM", "Template for a PWM rule", tags, triggers, conditions, actions, configDescriptions,
|
||||
Visibility.VISIBLE);
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal.template;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.automation.template.RuleTemplate;
|
||||
import org.openhab.core.automation.template.RuleTemplateProvider;
|
||||
import org.openhab.core.common.registry.ProviderChangeListener;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* Rule template provider for the PWM automation module.
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@Component
|
||||
@NonNullByDefault
|
||||
public class PWMTemplateProvider implements RuleTemplateProvider {
|
||||
private final Map<String, RuleTemplate> providedRuleTemplates = new HashMap<String, RuleTemplate>();
|
||||
|
||||
public PWMTemplateProvider() {
|
||||
providedRuleTemplates.put(PWMRuleTemplate.UID, PWMRuleTemplate.initialize());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public RuleTemplate getTemplate(String UID, @Nullable Locale locale) {
|
||||
return providedRuleTemplates.get(UID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RuleTemplate> getTemplates(@Nullable Locale locale) {
|
||||
return providedRuleTemplates.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addProviderChangeListener(ProviderChangeListener<RuleTemplate> listener) {
|
||||
// does nothing because this provider does not change
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RuleTemplate> getAll() {
|
||||
return Collections.unmodifiableCollection(providedRuleTemplates.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeProviderChangeListener(ProviderChangeListener<RuleTemplate> listener) {
|
||||
// does nothing because this provider does not change
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal.type;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.automation.pwm.internal.handler.PWMTriggerHandler;
|
||||
import org.openhab.core.automation.type.ModuleType;
|
||||
import org.openhab.core.automation.type.ModuleTypeProvider;
|
||||
import org.openhab.core.common.registry.ProviderChangeListener;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* Provides the module types for the rules engine.
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@Component
|
||||
@NonNullByDefault
|
||||
public class PWMModuleTypeProvider implements ModuleTypeProvider {
|
||||
private static final Map<String, ModuleType> PROVIDED_MODULE_TYPES = Map.of(PWMTriggerHandler.MODULE_TYPE_ID,
|
||||
PWMTriggerType.initialize());
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends ModuleType> T getModuleType(@Nullable String UID, @Nullable Locale locale) {
|
||||
return (T) PROVIDED_MODULE_TYPES.get(UID);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends ModuleType> Collection<T> getModuleTypes(@Nullable Locale locale) {
|
||||
return (Collection<T>) PROVIDED_MODULE_TYPES.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addProviderChangeListener(ProviderChangeListener<ModuleType> listener) {
|
||||
// does nothing because this provider does not change
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ModuleType> getAll() {
|
||||
return Collections.unmodifiableCollection(PROVIDED_MODULE_TYPES.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeProviderChangeListener(ProviderChangeListener<ModuleType> listener) {
|
||||
// does nothing because this provider does not change
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.automation.pwm.internal.type;
|
||||
|
||||
import static org.openhab.automation.pwm.internal.PWMConstants.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.automation.pwm.internal.handler.PWMTriggerHandler;
|
||||
import org.openhab.core.automation.Visibility;
|
||||
import org.openhab.core.automation.type.Output;
|
||||
import org.openhab.core.automation.type.TriggerType;
|
||||
import org.openhab.core.config.core.ConfigDescriptionParameter;
|
||||
import org.openhab.core.config.core.ConfigDescriptionParameter.Type;
|
||||
import org.openhab.core.config.core.ConfigDescriptionParameterBuilder;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
|
||||
/**
|
||||
* Creates the configuration for the Trigger module in the rules engine.
|
||||
*
|
||||
* @author Fabian Wolter - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PWMTriggerType extends TriggerType {
|
||||
public static final String UID = PWMTriggerHandler.MODULE_TYPE_ID;
|
||||
|
||||
public static PWMTriggerType initialize() {
|
||||
List<ConfigDescriptionParameter> configDescriptions = new ArrayList<>();
|
||||
configDescriptions.add(ConfigDescriptionParameterBuilder.create(CONFIG_DUTY_CYCLE_ITEM, Type.TEXT) //
|
||||
.withRequired(true) //
|
||||
.withMultiple(false) //
|
||||
.withContext("item") //
|
||||
.withLabel("Dutycycle Item").withDescription("Item to read the current dutycycle from (PercentType)")
|
||||
.build());
|
||||
configDescriptions.add(ConfigDescriptionParameterBuilder.create(CONFIG_PERIOD, Type.DECIMAL) //
|
||||
.withRequired(true) //
|
||||
.withMultiple(false) //
|
||||
.withDefault("600") //
|
||||
.withLabel("PWM Interval") //
|
||||
.withUnit("s") //
|
||||
.withDescription("Duration of the PWM interval in sec.").build());
|
||||
configDescriptions.add(ConfigDescriptionParameterBuilder.create(CONFIG_MIN_DUTYCYCLE, Type.DECIMAL) //
|
||||
.withRequired(false) //
|
||||
.withMultiple(false) //
|
||||
.withMinimum(BigDecimal.ZERO) //
|
||||
.withMaximum(BigDecimal.valueOf(100)) //
|
||||
.withDefault("0") //
|
||||
.withLabel("Min Dutycycle") //
|
||||
.withUnit("%") //
|
||||
.withDescription("The dutycycle will be min this value").build());
|
||||
configDescriptions.add(ConfigDescriptionParameterBuilder.create(CONFIG_MAX_DUTYCYCLE, Type.DECIMAL) //
|
||||
.withRequired(false) //
|
||||
.withMultiple(false) //
|
||||
.withMinimum(BigDecimal.ZERO) //
|
||||
.withMaximum(BigDecimal.valueOf(100)) //
|
||||
.withDefault("100") //
|
||||
.withUnit("%") //
|
||||
.withLabel("Max Dutycycle") //
|
||||
.withDescription("The dutycycle will be max this value").build());
|
||||
configDescriptions.add(ConfigDescriptionParameterBuilder.create(CONFIG_DEAD_MAN_SWITCH, Type.DECIMAL) //
|
||||
.withRequired(false) //
|
||||
.withMultiple(false) //
|
||||
.withMinimum(BigDecimal.ZERO) //
|
||||
.withDefault("") //
|
||||
.withLabel("Dead Man Switch") //
|
||||
.withUnit("ms") //
|
||||
.withDescription(
|
||||
"If the duty cycle Item is not updated within this time (in ms), the output is switched off")
|
||||
.build());
|
||||
|
||||
List<Output> outputs = Collections.singletonList(new Output(OUTPUT, OnOffType.class.getName(), "Output",
|
||||
"Output value of the PWM module", Set.of("command"), null, null));
|
||||
|
||||
return new PWMTriggerType(configDescriptions, outputs);
|
||||
}
|
||||
|
||||
public PWMTriggerType(List<ConfigDescriptionParameter> configDescriptions, List<Output> outputs) {
|
||||
super(UID, configDescriptions, "PWM triggers", null, null, Visibility.VISIBLE, outputs);
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
<module>org.openhab.automation.jsscripting</module>
|
||||
<module>org.openhab.automation.jythonscripting</module>
|
||||
<module>org.openhab.automation.pidcontroller</module>
|
||||
<module>org.openhab.automation.pwm</module>
|
||||
<!-- io -->
|
||||
<module>org.openhab.io.homekit</module>
|
||||
<module>org.openhab.io.hueemulation</module>
|
||||
|
Loading…
Reference in New Issue
Block a user