diff --git a/addons/io/org.openhab.io.imperihome/ESH-INF/config/config.xml b/addons/io/org.openhab.io.imperihome/ESH-INF/config/config.xml new file mode 100644 index 0000000000000..d5325a805ac00 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/ESH-INF/config/config.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/addons/io/org.openhab.io.imperihome/META-INF/MANIFEST.MF b/addons/io/org.openhab.io.imperihome/META-INF/MANIFEST.MF new file mode 100644 index 0000000000000..d61ae320be6e1 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/META-INF/MANIFEST.MF @@ -0,0 +1,34 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: ImperiHome Integration Service +Bundle-SymbolicName: org.openhab.io.imperihome;singleton:=true +Bundle-Vendor: openHAB +Bundle-Version: 2.0.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Bundle-ClassPath: . +Import-Package: com.google.common.base, + com.google.gson, + javax.servlet, + javax.servlet.http, + org.apache.commons.io, + org.apache.commons.lang, + org.eclipse.smarthome.config.core, + org.eclipse.smarthome.core.common.registry, + org.eclipse.smarthome.core.events, + org.eclipse.smarthome.core.id, + org.eclipse.smarthome.core.items, + org.eclipse.smarthome.core.items.events, + org.eclipse.smarthome.core.library.types, + org.eclipse.smarthome.core.persistence, + org.eclipse.smarthome.core.thing, + org.eclipse.smarthome.core.thing.binding, + org.eclipse.smarthome.core.thing.binding.builder, + org.eclipse.smarthome.core.thing.type, + org.eclipse.smarthome.core.types, + org.eclipse.smarthome.io.rest, + org.osgi.framework, + org.osgi.service.component, + org.osgi.service.event, + org.osgi.service.http, + org.slf4j +Service-Component: OSGI-INF/*.xml diff --git a/addons/io/org.openhab.io.imperihome/OSGI-INF/imperihome.xml b/addons/io/org.openhab.io.imperihome/OSGI-INF/imperihome.xml new file mode 100644 index 0000000000000..0f6ad26265e1b --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/OSGI-INF/imperihome.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + diff --git a/addons/io/org.openhab.io.imperihome/README.md b/addons/io/org.openhab.io.imperihome/README.md new file mode 100644 index 0000000000000..04e71c3df5e46 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/README.md @@ -0,0 +1,428 @@ +# ImperiHome integration service + +This IO service exposes openHAB Items to the Evertygo [ImperiHome](http://www.evertygo.com/imperihome) dashboard app for Android and iOS. +It creates a REST service at _/imperihome/iss_ that implements the [ImperiHome Standard System API](http://dev.evertygo.com/api/iss) (ISS). + +## Installation + +The ImperiHome integration service can be installed through the Paper UI. Navigate to Extensions > Misc and click Install. + +## Configuration + +The service itself has no configuration. ImperiHome on the other hand must be configured to connect to your openHAB instance. + +Start ImperiHome, open the menu and go to My Systems. Add a new system (+) and choose 'ImperiHome Standard System' as the object type. Now enter the URL to your openHAB instance + as Local URL, followed by _/imperihome/iss_. For example, if your OH instance is running at _http://192.168.1.10:8080/_, the Local URL would be _http://192.168.1.10:8080/imperihome/iss_. + +If you have port forwarding or similar set up to access your OH form the internet, you can also fill the Remote URL in the same way. For example: +_http://my-openhab-url.dyndns.org:8080/imperihome/iss_. Please be aware that this service provides no authentication mechanism, so anyone could use the API to control your +system when accessible from the internet. + +Click Next to let ImperiHome validate the URL. After validation succeeded the system is added and you can continue to configure your Items for use in ImperiHome. + +## Device Tagging + +This service uses Item tags to determine how to expose your Items to ImperiHome. All tags are formatted like this: + +``` +iss:: +``` + +For example: + +``` +iss:room:Kitchen +``` + +If you've defined your Items in _.items_ files, tags can be added using the + +``` +[ "mytag" ] +``` + +syntax (after the _(Groups)_ and before the _{channel}_). +If you created your items another way, e.g. using the Paper UI, [HABmin](https://github.com/openhab/org.openhab.ui.habmin) allows you to modify the tags. + +### Tag: _type_ + +Specifies the device type to expose to ImperiHome. Take a look at [Device types](#deviceTypes) below for the supported device types and how to configure them. +If no type is specified, this service will try to auto-detect the type from the Item, based on supported value types (OnOff for a switch, HSB for color light) and Item name. + +_Required_: no
+_Default_: auto-detect
+Example: + +``` +iss:type:DevSwitch +``` + +### Tag: _room_ + +Specifies the room the device will show up in in ImperiHome. + +_Required_: no
+_Default_: 'No Room'
+_Example_: + +``` +iss:room:Kitchen +``` + +### Tag: _label_ + +Sets the device label in ImperiHome. If no label is specified, the Item label is used if available. Otherwise the Item name will be used. + +_Required_: no
+_Default_: Item label or name
+_Example_: + +``` +iss:label:Kitchen light +``` + +### Tag: _mapping_ + +Sets the mapping for a ImperiHome MultiSwitch device, just like an openHAB sitemap mapping does. +In the example below, 'All off', 'Relax' and 'Reading' will be visible in ImperiHome. Clicking one of the options will send +a 0, 1 or 2 value command to the openHAB item. + +_Required_: only for MultiSwitch device
+_Default_: none
+_Example_: + +``` +iss:mapping:0=All off,1=Relax,2=Reading +``` + +### Tag: _link_ + +Links two devices together, using the value from the linked device as an additional value in the device containing the link tag. +See [Device links](#deviceLinks) for details. + +_Required_: no
+_Default_: none
+_Example_: + +``` +iss:link:energy:Kitchen_Current_Consumption +``` + +### Tag: _unit_ + +Sets the unit for devices with a numeric value, such as _DevTemperature_ and _DevGenericSensor_. +The unit is only used to tell ImperiHome what to display; no conversion is performed. + +_Required_: no
+_Default_: none
+_Example_: + +``` +iss:unit:°C +``` + +### Tag: _invert_ + +Inverts the state of on/off devices such as switches and dimmers. + +_Required_: no
+_Default_: false
+_Example_: + +``` +iss:invert:true +``` + + + +## Device types + +The following table lists the ImperiHome API device types that you can use in a _iss:type_ tag. Not all device types are currently supported. For those that are supported, +the Item types you can use them on are listed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DeviceDescriptionSupportedItem typesLink types
DevCameraMJPEG IP CameraNo-
DevCO2CO2 sensorYesNumber-
DevCO2AlertCO2 Alert sensorYesContact, Number, String(1), Switch-
DevDimmerDimmable lightYesDimmerenergy
DevDoorDoor / window security sensorYesContact, Number, String(1), Switch-
DevElectricityElectricity consumption sensorYesNumberkwh, watt
DevFloodFlood security sensorYesContact, Number, String(1), Switch-
DevGenericSensorGeneric sensor (any value)YesNumber, String-
DevHygrometryHygro sensorYesNumber-
DevLockDoor lockYesContact, Switch-
DevLuminosityLuminance sensorYesNumber-
DevMotionMotion security sensorYesContact, Number, String(1), Switch-
DevMultiSwitchMultiple choice actuatorYesNumber-
DevNoiseNoise sensorYesNumber-
DevPlayerAudio/Video playerNo-
DevPlaylistAudio/Video playlistNo-
DevPressurePressure sensorYesNumber-
DevRainRain sensorYesNumberaccum
DevRGBLightRGB(W) Light (dimmable)YesColorenergy
DevSceneScene (launchable)YesSwitch, Number-
DevShutterShutter actuatorNo-
DevSmokeSmoke security sensorYesContact, Number, String(1), Switch-
DevSwitchStandard on/off switchYesSwitchenergy
DevTemperatureTemperature sensorYesNumber-
DevTempHygroTemperature and Hygrometry combined sensorYesNumberhygro, temp
DevThermostatThermostatNo-
DevUVUV sensorYesNumber-
DevWindWind sensorYesNumberdirection
+ +(1) When using a String Item for trippable devices, any non-empty value other than 'ok' will set the device to tripped. This makes it compatible with the Nest Protect binding. + + + +## Device links + +Some devices can be linked to another device. This allows you to create combined devices reporting multiple values, or reporting the energy consumption with a switch device. + +The _link_ tag refers to the name of the Item it should link to. The item must be an ImperiHome device itself, so it must have at least one _iss_ tag. + +### Switch energy consumption + +ImperiHome allows you to show the current energy consumption for a _DevDimmer_, _DevRGBLight_ and _DevSwitch_. +This example links the _MyLightEnergy_ Number Item to the _MyLight_ Switch Item, +so the _DevSwitch_ device will also report the energy consumption value to ImperiHome: + +``` +Switch MyLight "My Light" ["iss:type:DevSwitch", "iss:link:energy:MyLight_Energy"] { channel="zwave:device:1:node14:switch_binary1" } +Number MyLightEnergy "My Light Usage [%.1f W]" ["iss:type:DevElectricity"] { channel="zwave:device:1:node14:meter_watts1" } +``` + +### Total energy consumption + +The _DevElectricity_ devices main value is the current consumption in Watts. To add the total consumption in KWh, link your electricity device to +a generic sensor device containing the total energy consumption value: + +``` +Number MyLight_Energy "My Light Usage [%.1f W]" ["iss:type:DevElectricity", "iss:link:kwh:MyLight_Total_Energy"] { channel="zwave:device:1:node14:meter_watts1" } +Number MyLight_Total_Energy "My Light Total usage [%.1f KWh]" ["iss:type:DevGenericSensor", "iss:unit:KWh"] { channel="zwave:device:1:node14:sensor_power1" } +``` + +### TempHygro + +ImperiHome recognizes the special _DevTempHygro_ device, combining a temperature and hydrometry sensor. You can create such a device by linking either from a temperature Item to a hygro Item: + +``` +Number MyTemp "Temperature [%.1f °C]" ["iss:type:DevTempHygro", "iss:link:hygro:MyHum"] { channel="zwave:device:1:node8:sensor_temperature" } +Number MyHum "Humidity [%d%%]" ["iss:type:DevHygrometry"] { channel="zwave:device:1:node8:sensor_relhumidity" } +``` + +or vise versa: + +``` +Number MyTemp "Temperature [%.1f °C]" ["iss:type:DevTemperature"] { channel="zwave:device:1:node8:sensor_temperature" } +Number MyHum "Humidity [%d%%]" ["iss:type:DevTempHygro", "iss:link:temp:MyTemp"] { channel="zwave:device:1:node8:sensor_relhumidity" } +``` + +### Rain accumulation + +The _DevRain_ devices main value is the current instant rain value (default in mm per hour). To add the total rain accumulation value, link your rain device to +a generic sensor device: + +``` +Number RainCurrent "Rain current [%.1f mm/h]" ["iss:type:DevRain", "iss:link:accum:RainAccumulation"] { channel="..." } +Number RainAccumulation "Rain accumulation [%.1f mm]" ["iss:type:DevGenericSensor", "iss:unit:mm"] { channel="..." } +``` + +### Wind direction + +The _DevWind_ devices main value is the current wind speed (default in km per hour). To add the wind direction value (default in degrees), link your wind device to +a generic sensor device: + +``` +Number WindSpeed "Wind speed [%.1f km/h]" ["iss:type:DevWind", "iss:link:direction:WindDirection"] { channel="..." } +Number WindDirection "Wind direction [%d deg]" ["iss:type:DevGenericSensor", "iss:unit:deg"] { channel="..." } +``` + +## Items example + +``` +Color LVR_Billy "Billy" (Lights) ["iss:room:Living room", "iss:type:DevRGBLight"] { channel="hue:0210:001122334455:bulb1:color" } +Switch LVR_TallLamp "Tall lamp" (Lights) ["iss:room:Living room", "iss:type:DevSwitch", "iss:invert:true"] { channel="zwave:device:1:node3:switch_binary" } +Dimmer LVR_DinnerTable "Dinner table" (Lights) ["iss:room:Living room", "iss:type:DevDimmer"] { channel="zwave:device:1:node13:switch_dimmer" } + +Number ENT_Entrance_Current "Entrance usage [%.1f W]" (Wattage) ["iss:room:Entrance", "iss:type:DevElectricity", "iss:unit:Watt"] { channel="zwave:device:1:node14:meter_watts1" } + +Number ENT_Temperature "Entrance temperature [%.1f °C]" (Temperature) ["iss:room:Entrance", "iss:type:DevTempHygro", "iss:link:hygro:ENT_Humidity"] { channel="zwave:device:1:node8:sensor_temperature" } +Number ENT_Luminance "Entrance light [%d lm]" (Luminance) ["iss:room:Entrance", "iss:type:DevLuminosity", "iss:unit:lux"] { channel="zwave:device:1:node8:sensor_luminance" } +Number ENT_Humidity "Entrance humidity [%d%%]" (Humidity) ["iss:room:Entrance", "iss:type:DevHygrometry"] { channel="zwave:device:1:node8:sensor_relhumidity" } +``` + diff --git a/addons/io/org.openhab.io.imperihome/build.properties b/addons/io/org.openhab.io.imperihome/build.properties new file mode 100644 index 0000000000000..baf60bca786d7 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/build.properties @@ -0,0 +1,7 @@ +source.. = src/main/java/,\ + src/main/resources/ +output.. = target/classes +bin.includes = META-INF/,\ + .,\ + OSGI-INF/,\ + ESH-INF/ \ No newline at end of file diff --git a/addons/io/org.openhab.io.imperihome/pom.xml b/addons/io/org.openhab.io.imperihome/pom.xml new file mode 100644 index 0000000000000..f1a0c78a2e087 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/pom.xml @@ -0,0 +1,20 @@ + + + + + org.openhab.addons.io + pom + 2.0.0-SNAPSHOT + + + 4.0.0 + org.openhab.io + org.openhab.io.imperihome + + ImperiHome Integration Service + + eclipse-plugin + + diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/ImperiHomeApiServlet.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/ImperiHomeApiServlet.java new file mode 100644 index 0000000000000..22b02607c01ed --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/ImperiHomeApiServlet.java @@ -0,0 +1,209 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal; + +import java.io.IOException; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.items.ItemRegistry; +import org.eclipse.smarthome.core.persistence.PersistenceServiceRegistry; +import org.openhab.io.imperihome.internal.action.ActionRegistry; +import org.openhab.io.imperihome.internal.handler.DeviceActionHandler; +import org.openhab.io.imperihome.internal.handler.DeviceHistoryHandler; +import org.openhab.io.imperihome.internal.handler.DevicesListHandler; +import org.openhab.io.imperihome.internal.handler.RoomListHandler; +import org.openhab.io.imperihome.internal.handler.SystemHandler; +import org.openhab.io.imperihome.internal.io.DeviceParametersSerializer; +import org.openhab.io.imperihome.internal.io.DeviceTypeSerializer; +import org.openhab.io.imperihome.internal.io.ParamTypeSerializer; +import org.openhab.io.imperihome.internal.model.device.DeviceType; +import org.openhab.io.imperihome.internal.model.param.DeviceParameters; +import org.openhab.io.imperihome.internal.model.param.ParamType; +import org.openhab.io.imperihome.internal.processor.DeviceRegistry; +import org.openhab.io.imperihome.internal.processor.ItemProcessor; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.http.HttpService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * Main OSGi service and HTTP servlet for ImperiHome integration. + * @author Pepijn de Geus - Initial contribution + */ +public class ImperiHomeApiServlet extends HttpServlet { + + private static final Logger LOGGER = LoggerFactory.getLogger(ImperiHomeApiServlet.class); + + private static final String PATH = "/imperihome/iss"; + + private static final String APPLICATION_JSON = "application/json"; + private static final String CHARSET = "utf-8"; + + private static final Pattern URL_PATTERN_SYSTEM = Pattern.compile(PATH + "/system$", Pattern.CASE_INSENSITIVE); + private static final Pattern URL_PATTERN_ROOMS = Pattern.compile(PATH + "/rooms$", Pattern.CASE_INSENSITIVE); + private static final Pattern URL_PATTERN_DEVICES = Pattern.compile(PATH + "/devices$", Pattern.CASE_INSENSITIVE); + private static final Pattern URL_PATTERN_DEVICE_ACTION = Pattern.compile(PATH + "/devices/(.+?)/action/(.+?)/(.*?)$", Pattern.CASE_INSENSITIVE); + private static final Pattern URL_PATTERN_DEVICE_HISTORY = Pattern.compile(PATH + "/devices/(.+?)/(.+?)/histo/(.+?)/(.+?)$", Pattern.CASE_INSENSITIVE); + + private final Gson gson; + + private HttpService httpService; + private ItemRegistry itemRegistry; + private PersistenceServiceRegistry persistenceServiceRegistry; + private EventPublisher eventPublisher; + + private ItemProcessor itemProcessor; + private DevicesListHandler devicesListHandler; + private RoomListHandler roomListHandler; + private SystemHandler systemHandler; + private DeviceRegistry deviceRegistry; + private DeviceActionHandler deviceActionHandler; + private DeviceHistoryHandler deviceHistoryHandler; + private ActionRegistry actionRegistry; + + /** + * Default constructor. + */ + public ImperiHomeApiServlet() { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.registerTypeAdapter(DeviceType.class, new DeviceTypeSerializer()); + gsonBuilder.registerTypeAdapter(ParamType.class, new ParamTypeSerializer()); + gsonBuilder.registerTypeAdapter(DeviceParameters.class, new DeviceParametersSerializer()); + gson = gsonBuilder.create(); + } + + /** + * OSGi activation callback. + * @param config Service config. + */ + protected void activate(Map config) { + systemHandler = new SystemHandler(); + deviceRegistry = new DeviceRegistry(); + actionRegistry = new ActionRegistry(eventPublisher); + itemProcessor = new ItemProcessor(itemRegistry, deviceRegistry, actionRegistry); + roomListHandler = new RoomListHandler(deviceRegistry); + devicesListHandler = new DevicesListHandler(deviceRegistry); + deviceActionHandler = new DeviceActionHandler(deviceRegistry); + deviceHistoryHandler = new DeviceHistoryHandler(deviceRegistry, persistenceServiceRegistry); + + try { + Dictionary servletParams = new Hashtable(); + httpService.registerServlet(PATH, this, servletParams, httpService.createDefaultHttpContext()); + LOGGER.info("Started ImperiHome integration service at " + PATH); + } catch (Exception e) { + LOGGER.error("Could not start ImperiHome integration service: {}", e.getMessage(), e); + } + } + + /** + * OSGi deactivation callback. + * @param componentContext Context. + */ + protected void deactivate(ComponentContext componentContext) { + try { + httpService.unregister(PATH); + } catch (IllegalArgumentException ignored) { + } + + itemProcessor.destroy(); + + systemHandler = null; + deviceRegistry = null; + itemProcessor = null; + roomListHandler = null; + devicesListHandler = null; + deviceActionHandler = null; + deviceHistoryHandler = null; + + LOGGER.info("ImperiHome integration service stopped"); + } + + protected void setItemRegistry(ItemRegistry itemRegistry) { + this.itemRegistry = itemRegistry; + } + + protected void unsetItemRegistry(ItemRegistry itemRegistry) { + this.itemRegistry = null; + } + + protected void setEventPublisher(EventPublisher eventPublisher) { + this.eventPublisher = eventPublisher; + } + + protected void unsetEventPublisher(EventPublisher eventPublisher) { + this.eventPublisher = null; + } + + protected void setHttpService(HttpService httpService) { + this.httpService = httpService; + } + + protected void unsetHttpService(HttpService httpService) { + this.httpService = null; + } + + protected void setPersistenceServiceRegistry(PersistenceServiceRegistry persistenceServiceRegistry) { + this.persistenceServiceRegistry = persistenceServiceRegistry; + } + + protected void unsetPersistenceServiceRegistry(PersistenceServiceRegistry persistenceServiceRegistry) { + this.persistenceServiceRegistry = null; + } + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String path = req.getRequestURI(); + LOGGER.debug("{}: {} {}", req.getRemoteAddr(), req.getMethod(), path); + setHeaders(resp); + + Object response = null; + + Matcher actionMatcher = URL_PATTERN_DEVICE_ACTION.matcher(path); + Matcher historyMatcher = URL_PATTERN_DEVICE_HISTORY.matcher(path); + + if (URL_PATTERN_ROOMS.matcher(path).matches()) { + response = roomListHandler.handle(req); + } else if (URL_PATTERN_DEVICES.matcher(path).matches()) { + response = devicesListHandler.handle(req); + } else if (actionMatcher.matches()) { + deviceActionHandler.handle(req, actionMatcher); + } else if (historyMatcher.matches()) { + response = deviceHistoryHandler.handle(req, historyMatcher); + } else if (URL_PATTERN_SYSTEM.matcher(path).matches()) { + response = systemHandler.handle(req); + } else { + LOGGER.warn("Unrecognized request: {}", path); + } + + resp.getWriter().write(gson.toJson(response)); + } + + private void setHeaders(HttpServletResponse response) { + response.setCharacterEncoding(CHARSET); + response.setContentType(APPLICATION_JSON); + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT"); + response.setHeader("Access-Control-Max-Age", "3600"); + response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/Action.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/Action.java new file mode 100644 index 0000000000000..75ebaa525b9ef --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/Action.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.action; + +import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.items.Item; +import org.openhab.io.imperihome.internal.model.device.AbstractDevice; + +/** + * Abstract action, called through the API by ImperiHome clients. + * @author Pepijn de Geus - Initial contribution + */ +public abstract class Action { + + protected static final String COMMAND_SOURCE = "imperihome"; + + protected final EventPublisher eventPublisher; + + protected Action(EventPublisher eventPublisher) { + this.eventPublisher = eventPublisher; + } + + /** + * Indicates if this action can be performed on the given Item. + * + * @param device + * @param item Item to check compatibility with. + * @return True if the Item is supported. + */ + public abstract boolean supports(AbstractDevice device, Item item); + + /** + * Perform this action on the given Item. + * @param device + * @param item Item to perform action on. + * @param value Action parameter value. + */ + public abstract void perform(AbstractDevice device, Item item, String value); + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/ActionRegistry.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/ActionRegistry.java new file mode 100644 index 0000000000000..ea87ce51155e8 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/ActionRegistry.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.action; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.smarthome.core.events.EventPublisher; + +/** + * Action registry. Maps ImperiHome API action name to {@link Action} implementation. + * @author Pepijn de Geus - Initial contribution + */ +public class ActionRegistry { + + private final Map actions = new HashMap<>(); + + public ActionRegistry(EventPublisher eventPublisher) { + actions.put("setStatus", new SetStatusAction(eventPublisher)); + actions.put("setColor", new SetColorAction(eventPublisher)); + actions.put("setLevel", new SetLevelAction(eventPublisher)); + actions.put("setChoice", new SetChoiceAction(eventPublisher)); + actions.put("launchScene", new LaunchSceneAction(eventPublisher)); + } + + public Action get(String action) { + return actions.get(action); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/LaunchSceneAction.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/LaunchSceneAction.java new file mode 100644 index 0000000000000..9246a6d54e30a --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/LaunchSceneAction.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.action; + +import java.util.List; + +import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.events.ItemCommandEvent; +import org.eclipse.smarthome.core.items.events.ItemEventFactory; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.io.imperihome.internal.model.device.AbstractDevice; + +/** + * Action performed on a DevScene. Sends an {@link OnOffType#ON} or {@link DecimalType} 1 to the Item. + * @author Pepijn de Geus - Initial contribution + */ +public class LaunchSceneAction extends Action { + + public LaunchSceneAction(EventPublisher eventPublisher) { + super(eventPublisher); + } + + @Override + public boolean supports(AbstractDevice device, Item item) { + List> acceptedCommandTypes = item.getAcceptedCommandTypes(); + return acceptedCommandTypes.contains(OnOffType.class) || + acceptedCommandTypes.contains(DecimalType.class); + } + + @Override + public void perform(AbstractDevice device, Item item, String value) { + ItemCommandEvent event = null; + + List> acceptedCommandTypes = item.getAcceptedCommandTypes(); + if (acceptedCommandTypes.contains(OnOffType.class)) { + event = ItemEventFactory.createCommandEvent(item.getName(), OnOffType.ON, COMMAND_SOURCE); + } else if (acceptedCommandTypes.contains(DecimalType.class)) { + event = ItemEventFactory.createCommandEvent(item.getName(), new DecimalType(1), COMMAND_SOURCE); + } + + if (event != null) { + eventPublisher.post(event); + } + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/SetChoiceAction.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/SetChoiceAction.java new file mode 100644 index 0000000000000..e2bdc7b3d2841 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/SetChoiceAction.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.action; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; + +import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.events.ItemCommandEvent; +import org.eclipse.smarthome.core.items.events.ItemEventFactory; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.openhab.io.imperihome.internal.model.device.AbstractDevice; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Action setting a choice from a selection list, e.g. MultiSwitch. + * @author Pepijn de Geus - Initial contribution + */ +public class SetChoiceAction extends Action { + + private static final Logger LOGGER = LoggerFactory.getLogger(SetChoiceAction.class); + + public SetChoiceAction(EventPublisher eventPublisher) { + super(eventPublisher); + } + + @Override + public boolean supports(AbstractDevice device, Item item) { + Map mapping = device.getMapping(); + return mapping != null && !mapping.isEmpty() && item.getAcceptedCommandTypes().contains(DecimalType.class); + } + + @Override + public void perform(AbstractDevice device, Item item, String value) { + String newValue = null; + + for (Entry entry : device.getMapping().entrySet()) { + if (Objects.equals(entry.getValue(), value)) { + newValue = entry.getKey(); + break; + } + } + + if (newValue == null) { + LOGGER.warn("Could not find selection '{}' in mapping {} of device {}", value, device.getMapping(), device); + return; + } + + ItemCommandEvent event = ItemEventFactory.createCommandEvent(item.getName(), new DecimalType(newValue), COMMAND_SOURCE); + eventPublisher.post(event); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/SetColorAction.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/SetColorAction.java new file mode 100644 index 0000000000000..d96c4b79bda63 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/SetColorAction.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.action; + +import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.events.ItemCommandEvent; +import org.eclipse.smarthome.core.items.events.ItemEventFactory; +import org.eclipse.smarthome.core.library.types.HSBType; +import org.openhab.io.imperihome.internal.model.device.AbstractDevice; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Items setting RGB color value. + * @author Pepijn de Geus - Initial contribution + */ +public class SetColorAction extends Action { + + private static final Logger LOGGER = LoggerFactory.getLogger(SetColorAction.class); + + public SetColorAction(EventPublisher eventPublisher) { + super(eventPublisher); + } + + @Override + public boolean supports(AbstractDevice device, Item item) { + return item.getAcceptedCommandTypes().contains(HSBType.class); + } + + @Override + public void perform(AbstractDevice device, Item item, String value) { + if (value == null || value.length() != 8) { + LOGGER.error("Invalid parameter: '{}'. Format must be 'aarrggbb'.", value); + return; + } + + int r = Integer.parseInt(value.substring(2, 4), 16); + int g = Integer.parseInt(value.substring(4, 6), 16); + int b = Integer.parseInt(value.substring(6, 8), 16); + + HSBType hsbValue = HSBType.fromRGB(r, g, b); + + ItemCommandEvent event = ItemEventFactory.createCommandEvent(item.getName(), hsbValue, COMMAND_SOURCE); + eventPublisher.post(event); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/SetLevelAction.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/SetLevelAction.java new file mode 100644 index 0000000000000..e2617a809f0d0 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/SetLevelAction.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.action; + +import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.events.ItemCommandEvent; +import org.eclipse.smarthome.core.items.events.ItemEventFactory; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.openhab.io.imperihome.internal.model.device.AbstractDevice; + +/** + * Action setting percentage level, e.g. dimmer. + * @author Pepijn de Geus - Initial contribution + */ +public class SetLevelAction extends Action { + + public SetLevelAction(EventPublisher eventPublisher) { + super(eventPublisher); + } + + @Override + public boolean supports(AbstractDevice device, Item item) { + return item.getAcceptedCommandTypes().contains(PercentType.class); + } + + @Override + public void perform(AbstractDevice device, Item item, String value) { + ItemCommandEvent event = ItemEventFactory.createCommandEvent(item.getName(), new PercentType(value), COMMAND_SOURCE); + eventPublisher.post(event); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/SetStatusAction.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/SetStatusAction.java new file mode 100644 index 0000000000000..cc8d04f5a7edc --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/action/SetStatusAction.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.action; + +import java.util.Objects; + +import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.events.ItemCommandEvent; +import org.eclipse.smarthome.core.items.events.ItemEventFactory; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.openhab.io.imperihome.internal.model.device.AbstractDevice; + +/** + * Action setting device status to 1 or 0. + * @author Pepijn de Geus - Initial contribution + */ +public class SetStatusAction extends Action { + + public SetStatusAction(EventPublisher eventPublisher) { + super(eventPublisher); + } + + @Override + public boolean supports(AbstractDevice device, Item item) { + return item.getAcceptedCommandTypes().contains(OnOffType.class); + } + + @Override + public void perform(AbstractDevice device, Item item, String value) { + OnOffType cmdValue = OnOffType.OFF; + if (Objects.equals("1", value)) { + cmdValue = OnOffType.ON; + } + + ItemCommandEvent event = ItemEventFactory.createCommandEvent(item.getName(), cmdValue, COMMAND_SOURCE); + eventPublisher.post(event); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/DeviceActionHandler.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/DeviceActionHandler.java new file mode 100644 index 0000000000000..24fb27f450987 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/DeviceActionHandler.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.handler; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.regex.Matcher; + +import javax.servlet.http.HttpServletRequest; + +import org.openhab.io.imperihome.internal.model.device.AbstractDevice; +import org.openhab.io.imperihome.internal.processor.DeviceRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Device action request handler. + * @author Pepijn de Geus - Initial contribution + */ +public class DeviceActionHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(DeviceActionHandler.class); + + private static final String CHARSET = "UTF-8"; + + private final DeviceRegistry deviceRegistry; + + public DeviceActionHandler(DeviceRegistry deviceRegistry) { + this.deviceRegistry = deviceRegistry; + } + + public void handle(HttpServletRequest req, Matcher urlMatcher) { + String deviceId, action, value; + try { + deviceId = URLDecoder.decode(urlMatcher.group(1), CHARSET); + action = URLDecoder.decode(urlMatcher.group(2), CHARSET); + value = URLDecoder.decode(urlMatcher.group(3), CHARSET); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Could not decode request params", e); + } + + LOGGER.debug("Action request for device {}: [{}] [{}]", deviceId, action, value); + + AbstractDevice device = deviceRegistry.getDevice(deviceId); + if (device == null) { + LOGGER.warn("Received action request for unknown device: {}", urlMatcher.group(0)); + } else { + device.performAction(action, value); + } + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/DeviceHistoryHandler.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/DeviceHistoryHandler.java new file mode 100644 index 0000000000000..d24fe7cdb752b --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/DeviceHistoryHandler.java @@ -0,0 +1,118 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.handler; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.persistence.FilterCriteria; +import org.eclipse.smarthome.core.persistence.HistoricItem; +import org.eclipse.smarthome.core.persistence.PersistenceService; +import org.eclipse.smarthome.core.persistence.PersistenceServiceRegistry; +import org.eclipse.smarthome.core.persistence.QueryablePersistenceService; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.HistoryItem; +import org.openhab.io.imperihome.internal.model.HistoryList; +import org.openhab.io.imperihome.internal.model.device.AbstractDevice; +import org.openhab.io.imperihome.internal.processor.DeviceRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Device history request handler. + * @author Pepijn de Geus - Initial contribution + */ +public class DeviceHistoryHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(DeviceHistoryHandler.class); + + private static final String CHARSET = "UTF-8"; + + private final DeviceRegistry deviceRegistry; + private final PersistenceServiceRegistry persistenceServiceRegistry; + + public DeviceHistoryHandler(DeviceRegistry deviceRegistry, PersistenceServiceRegistry persistenceServiceRegistry) { + this.deviceRegistry = deviceRegistry; + this.persistenceServiceRegistry = persistenceServiceRegistry; + } + + public HistoryList handle(HttpServletRequest req, Matcher urlMatcher) { + String deviceId, field; + long start, end; + try { + deviceId = URLDecoder.decode(urlMatcher.group(1), CHARSET); + field = URLDecoder.decode(urlMatcher.group(2), CHARSET); + start = Long.parseLong(urlMatcher.group(3)); + end = Long.parseLong(urlMatcher.group(4)); + } catch (UnsupportedEncodingException | NumberFormatException e) { + throw new RuntimeException("Could not decode request params", e); + } + + LOGGER.debug("History request for device {}, field {}: {}-{}", deviceId, field, start, end); + + AbstractDevice device = deviceRegistry.getDevice(deviceId); + if (device == null) { + LOGGER.warn("Received history request for unknown device: {}", urlMatcher.group(0)); + return null; + } + + PersistenceService persistence = persistenceServiceRegistry.getDefault(); + if (persistence == null) { + LOGGER.warn("Could not retrieve default persistence service; can't serve history request"); + return null; + } + if (!(persistence instanceof QueryablePersistenceService)) { + LOGGER.warn("Default persistence service is not queryable; can't serve history request"); + return null; + } + + return serveHistory(device, (QueryablePersistenceService) persistence, start, end); + } + + private HistoryList serveHistory(AbstractDevice device, QueryablePersistenceService persistence, long start, long end) { + LOGGER.info("Querying persistence for history of Item {}, from {} to {}", device.getItemName(), start, end); + + FilterCriteria criteria = new FilterCriteria() + .setItemName(device.getItemName()) + .setBeginDate(new Date(start)) + .setEndDate(new Date(end)); + + List resultItems = new LinkedList<>(); + Iterable historicItems = persistence.query(criteria); + + Iterator iterator = historicItems.iterator(); + if (!iterator.hasNext()) { + LOGGER.info("Persistence returned no results for history query"); + } else { + while (iterator.hasNext()) { + HistoricItem historicItem = iterator.next(); + State state = historicItem.getState(); + if (state instanceof DecimalType) { + Number value = ((DecimalType) state).toBigDecimal(); + resultItems.add(new HistoryItem(historicItem.getTimestamp(), value)); + } + } + + if (resultItems.isEmpty()) { + LOGGER.warn("Persistence returned results for history query, but could not be interpreted as DecimalTypes"); + } + } + + return new HistoryList(resultItems); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/DevicesListHandler.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/DevicesListHandler.java new file mode 100644 index 0000000000000..13de0c0dd0ad6 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/DevicesListHandler.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.handler; + +import java.util.Collection; + +import javax.servlet.http.HttpServletRequest; + +import org.openhab.io.imperihome.internal.model.device.AbstractDevice; +import org.openhab.io.imperihome.internal.model.device.DeviceList; +import org.openhab.io.imperihome.internal.processor.DeviceRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Device list request handler. + * @author Pepijn de Geus - Initial contribution + */ +public class DevicesListHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(DevicesListHandler.class); + + private final DeviceRegistry deviceRegistry; + + public DevicesListHandler(DeviceRegistry deviceRegistry) { + this.deviceRegistry = deviceRegistry; + } + + public DeviceList handle(HttpServletRequest req) { + DeviceList response = new DeviceList(); + + Collection devices = deviceRegistry.getDevices().values(); + for (AbstractDevice device : devices) { + device.updateParams(); + } + + response.setDevices(devices); + + LOGGER.debug("Device list response: {}", response); + return response; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/RoomListHandler.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/RoomListHandler.java new file mode 100644 index 0000000000000..0e18af9970aa1 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/RoomListHandler.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.handler; + +import javax.servlet.http.HttpServletRequest; + +import org.openhab.io.imperihome.internal.model.RoomList; +import org.openhab.io.imperihome.internal.processor.DeviceRegistry; + +/** + * Rooms list request handler. + * @author Pepijn de Geus - Initial contribution + */ +public class RoomListHandler { + + private final DeviceRegistry deviceRegistry; + + public RoomListHandler(DeviceRegistry deviceRegistry) { + this.deviceRegistry = deviceRegistry; + } + + public RoomList handle(HttpServletRequest req) { + RoomList response = new RoomList(); + response.setRooms(deviceRegistry.getRooms()); + return response; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/SystemHandler.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/SystemHandler.java new file mode 100644 index 0000000000000..c9ca28b5bae71 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/handler/SystemHandler.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.handler; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.smarthome.core.id.InstanceUUID; +import org.openhab.io.imperihome.internal.model.System; + +/** + * System data request handler. + * @author Pepijn de Geus - Initial contribution + */ +public class SystemHandler { + + public System handle(HttpServletRequest req) { + System system = new System(); + system.setId(InstanceUUID.get()); + system.setApiVersion(1); + return system; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/io/DeviceParametersSerializer.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/io/DeviceParametersSerializer.java new file mode 100644 index 0000000000000..5602d6f55ab03 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/io/DeviceParametersSerializer.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.io; + +import java.lang.reflect.Type; + +import org.openhab.io.imperihome.internal.model.param.DeviceParam; +import org.openhab.io.imperihome.internal.model.param.DeviceParameters; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * Serializer for {@link DeviceParameters}. + * @author Pepijn de Geus - Initial contribution + */ +public class DeviceParametersSerializer implements JsonSerializer { + + public JsonElement serialize(DeviceParameters params, Type type, JsonSerializationContext jsonSerializationContext) { + JsonArray result = new JsonArray(); + for (DeviceParam param : params.values()) { + result.add(jsonSerializationContext.serialize(param)); + } + return result; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/io/DeviceTypeSerializer.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/io/DeviceTypeSerializer.java new file mode 100644 index 0000000000000..7cc30ed99092f --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/io/DeviceTypeSerializer.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.io; + +import java.lang.reflect.Type; + +import org.openhab.io.imperihome.internal.model.device.DeviceType; + +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * Serializer for {@link DeviceType}. + * @author Pepijn de Geus - Initial contribution + */ +public class DeviceTypeSerializer implements JsonSerializer { + + public JsonElement serialize(DeviceType deviceType, Type type, JsonSerializationContext jsonSerializationContext) { + return new JsonPrimitive(deviceType.getApiString()); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/io/ParamTypeSerializer.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/io/ParamTypeSerializer.java new file mode 100644 index 0000000000000..638005fe985c0 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/io/ParamTypeSerializer.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.io; + +import java.lang.reflect.Type; + +import org.openhab.io.imperihome.internal.model.param.ParamType; + +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * Serializer for {@link ParamType}. + * @author Pepijn de Geus - Initial contribution + */ +public class ParamTypeSerializer implements JsonSerializer { + + public JsonElement serialize(ParamType paramType, Type type, JsonSerializationContext jsonSerializationContext) { + return new JsonPrimitive(paramType.getApiString()); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/HistoryItem.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/HistoryItem.java new file mode 100644 index 0000000000000..5b4c789f600d1 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/HistoryItem.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model; + +import java.util.Date; + +/** + * History item data object. + * @author Pepijn de Geus - Initial contribution + */ +public class HistoryItem { + + private long date; + private Number value; + + public HistoryItem(Date date, Number value) { + this(date.getTime(), value); + } + + public HistoryItem(long date, Number value) { + this.date = date; + this.value = value; + } + + public long getDate() { + return date; + } + + public void setDate(long date) { + this.date = date; + } + + public Number getValue() { + return value; + } + + public void setValue(Number value) { + this.value = value; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/HistoryList.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/HistoryList.java new file mode 100644 index 0000000000000..f71d4af46d260 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/HistoryList.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model; + +import java.util.LinkedList; +import java.util.List; + +/** + * History list data object. + * @author Pepijn de Geus - Initial contribution + */ +public class HistoryList { + + private List values; + + public HistoryList() { + this(new LinkedList()); + } + + public HistoryList(List resultItems) { + this.values = resultItems; + } + + public List getValues() { + return values; + } + + public void setValues(List values) { + this.values = values; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/Room.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/Room.java new file mode 100644 index 0000000000000..b979a42a27345 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/Room.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model; + +/** + * Room data object. + * @author Pepijn de Geus - Initial contribution + */ +public class Room { + + private String id; + private String name; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Room)) return false; + + Room room = (Room) o; + + return id != null ? id.equals(room.id) : room.id == null; + + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } + + @Override + public String toString() { + return "Room{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + '}'; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/RoomList.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/RoomList.java new file mode 100644 index 0000000000000..1532833337110 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/RoomList.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model; + +import java.util.Collection; + +/** + * Room list data object. + * @author Pepijn de Geus - Initial contribution + */ +public class RoomList { + + private Collection rooms; + + public Collection getRooms() { + return rooms; + } + + public void setRooms(Collection rooms) { + this.rooms = rooms; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/System.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/System.java new file mode 100644 index 0000000000000..d5e1b6aad4ac6 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/System.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model; + +/** + * System data object. + * @author Pepijn de Geus - Initial contribution + */ +public class System { + + private String id; + private int apiVersion; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public int getApiVersion() { + return apiVersion; + } + + public void setApiVersion(int apiVersion) { + this.apiVersion = apiVersion; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/AbstractDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/AbstractDevice.java new file mode 100644 index 0000000000000..e964b460ce13b --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/AbstractDevice.java @@ -0,0 +1,227 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.smarthome.core.items.GenericItem; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.StateChangeListener; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.action.Action; +import org.openhab.io.imperihome.internal.action.ActionRegistry; +import org.openhab.io.imperihome.internal.model.param.DeviceParam; +import org.openhab.io.imperihome.internal.model.param.DeviceParameters; +import org.openhab.io.imperihome.internal.model.param.ParamType; +import org.openhab.io.imperihome.internal.processor.DeviceRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Abstract parent of all devices. Sets up and tears down state listeners and contains parameter and link data. + * @author Pepijn de Geus - Initial contribution + */ +public abstract class AbstractDevice implements StateChangeListener { + + protected transient final Logger LOGGER = LoggerFactory.getLogger(getClass()); + + private String id; + private String name; + private String room; + private DeviceType type; + private boolean inverted; + private final DeviceParameters params; + + private transient String roomName; + private transient Item item; + + private final transient Map links; + private transient Map mapping; + + private transient DeviceRegistry deviceRegistry; + private transient ActionRegistry actionRegistry; + + public AbstractDevice(DeviceType type, Item item) { + this.type = type; + this.item = item; + params = new DeviceParameters(); + links = new HashMap<>(); + + if (item instanceof GenericItem) { + ((GenericItem) item).addStateChangeListener(this); + } + } + + public void destroy() { + if (item instanceof GenericItem) { + ((GenericItem) item).removeStateChangeListener(this); + } + + deviceRegistry = null; + actionRegistry = null; + item = null; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getRoom() { + return room; + } + + public void setRoom(String room) { + this.room = room; + } + + public String getRoomName() { + return roomName; + } + + public void setRoomName(String roomName) { + this.roomName = roomName; + } + + public DeviceType getType() { + return type; + } + + public void setType(DeviceType type) { + this.type = type; + } + + public boolean isInverted() { + return inverted; + } + + public void setInverted(boolean inverted) { + this.inverted = inverted; + } + + public DeviceParameters getParams() { + return params; + } + + public void addParam(DeviceParam param) { + LOGGER.trace("Setting param for device {}: {}", this, param); + params.set(param); + } + + public Map getLinks() { + return links; + } + + public void addLink(String linkType, String deviceId) { + links.put(linkType, deviceId); + } + + public void setMapping(Map mapping) { + this.mapping = mapping; + } + + public Map getMapping() { + return mapping; + } + + public void setDeviceRegistry(DeviceRegistry deviceRegistry) { + this.deviceRegistry = deviceRegistry; + } + + protected DeviceRegistry getDeviceRegistry() { + return deviceRegistry; + } + + public void setActionRegistry(ActionRegistry actionRegistry) { + this.actionRegistry = actionRegistry; + } + + protected ActionRegistry getActionRegistry() { + return actionRegistry; + } + + protected Item getItem() { + return item; + } + + public String getItemName() { + return item.getName(); + } + + /** + * Can be implemented by Devices that require their state to be updated manually, instead of relying (only) on Item state change events. + * This method is called just before serializing the device to JSON. + */ + public void updateParams() { + LOGGER.trace("updateParams on {}", this); + } + + /** + * Performs an action on this device. + * @param action Action name. + * @param value Action value. + */ + public void performAction(String action, String value) { + Action actionInst = actionRegistry.get(action); + if (actionInst == null) { + LOGGER.warn("Unknown action: {}", action); + return; + } + + Item item = getItem(); + if (!actionInst.supports(this, item)) { + LOGGER.warn("Action '{}' not supported on this device ({})", action, this); + return; + } + + actionInst.perform(this, item, value); + } + + @Override + public void stateChanged(Item item, State oldState, State newState) { + } + + @Override + public void stateUpdated(Item item, State newState) { + LOGGER.debug("Device item {} state changed to {}", item, newState); + + OnOffType onOffState = (OnOffType) item.getStateAs(OnOffType.class); + if (onOffState != null) { + boolean isOn = onOffState == OnOffType.ON; + DeviceParam param = new DeviceParam(ParamType.STATUS, isOn ^ isInverted() ? "1" : "0"); + addParam(param); + } + } + + @Override + public String toString() { + return "AbstractDevice{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", room='" + room + '\'' + + ", type=" + type + + ", invert=" + inverted + + ", links=" + links + + '}'; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/AbstractEnergyLinkDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/AbstractEnergyLinkDevice.java new file mode 100644 index 0000000000000..4f407a01c574b --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/AbstractEnergyLinkDevice.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; +import org.openhab.io.imperihome.internal.model.param.NumericValueParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; +import org.openhab.io.imperihome.internal.processor.ItemProcessor; + +/** + * Abstraction of devices that allow a link to a current energy consumption item. + * @author Pepijn de Geus - Initial contribution + */ +public abstract class AbstractEnergyLinkDevice extends AbstractDevice { + + public AbstractEnergyLinkDevice(DeviceType type, Item item) { + super(type, item); + } + + @Override + public void updateParams() { + super.updateParams(); + + if (getLinks().containsKey("energy")) { + String deviceName = getLinks().get("energy"); + String deviceId = ItemProcessor.getDeviceId(deviceName); + AbstractDevice energyDevice = getDeviceRegistry().getDevice(deviceId); + if (energyDevice == null) { + LOGGER.error("Couldn't resolve linked energy device '{}', make sure the Item has iss tags", deviceName); + } else { + NumericValueParam valueParam = (NumericValueParam) energyDevice.getParams().get(ParamType.WATTS); + if (valueParam == null) { + LOGGER.warn("Linked energy device has no Watts value parameter: {}", energyDevice); + } else { + NumericValueParam energyParam = new NumericValueParam(ParamType.ENERGY, valueParam.getUnit(), null); + energyParam.setValue(valueParam.getValue()); + addParam(energyParam); + } + } + } + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/AbstractNumericValueDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/AbstractNumericValueDevice.java new file mode 100644 index 0000000000000..0ba6722cb5047 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/AbstractNumericValueDevice.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; +import org.openhab.io.imperihome.internal.model.param.NumericValueParam; + +/** + * Parent of devices with a {@link NumericValueParam}. Contains the value unit. + * @author Pepijn de Geus - Initial contribution + */ +public abstract class AbstractNumericValueDevice extends AbstractDevice { + + private transient String unit; + + public AbstractNumericValueDevice(DeviceType type, Item item, String defaultUnit) { + super(type, item); + this.unit = defaultUnit; + } + + public String getUnit() { + return unit; + } + + public void setUnit(String unit) { + this.unit = unit; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "super=" + super.toString() + + ", unit='" + unit + '\'' + + '}'; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/Co2SensorDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/Co2SensorDevice.java new file mode 100644 index 0000000000000..b37a09f3e5c34 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/Co2SensorDevice.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.NumericValueParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; + +/** + * CO2 sensor device. + * @author Pepijn de Geus - Initial contribution + */ +public class Co2SensorDevice extends AbstractNumericValueDevice { + + public Co2SensorDevice(Item item) { + super(DeviceType.CO2, item, "ppm"); + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + DecimalType value = (DecimalType) item.getStateAs(DecimalType.class); + addParam(new NumericValueParam(ParamType.CO2_VALUE, getUnit(), value)); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/DeviceList.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/DeviceList.java new file mode 100644 index 0000000000000..52ab49e24f03b --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/DeviceList.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import java.util.Collection; + +/** + * Device list holder. + * @author Pepijn de Geus - Initial contribution + */ +public class DeviceList { + + private Collection devices; + + public Collection getDevices() { + return devices; + } + + public void setDevices(Collection devices) { + this.devices = devices; + } + + @Override + public String toString() { + return "DeviceList{" + + "devices=" + devices + + '}'; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/DeviceType.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/DeviceType.java new file mode 100644 index 0000000000000..59118a82f19d9 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/DeviceType.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +/** + * Device type enumeration. Contains ImperiHome API type strings. + * @author Pepijn de Geus - Initial contribution + */ +public enum DeviceType { + + SWITCH ("DevSwitch"), + DIMMER ("DevDimmer"), + CAMERA ("DevCamera"), + CO2 ("DevCO2"), + CO2_ALERT ("DevCO2Alert"), + DOOR ("DevDoor"), + ELECTRICITY ("DevElectricity"), + FLOOD ("DevFlood"), + GENERIC_SENSOR ("DevGenericSensor"), + HYGROMETRY ("DevHygrometry"), + LOCK ("DevLock"), + LUMINOSITY ("DevLuminosity"), + MOTION ("DevMotion"), + MULTI_SWITCH ("DevMultiSwitch"), + NOISE ("DevNoise"), + PLAYER ("DevPlayer"), + PLAYLIST ("DevPlaylist"), + PRESSURE ("DevPressure"), + RAIN ("DevRain"), + RGB_LIGHT ("DevRGBLight"), + SCENE ("DevScene"), + SHUTTER ("DevShutter"), + SMOKE ("DevSmoke"), + TEMPERATURE ("DevTemperature"), + TEMP_HYGRO ("DevTempHygro"), + THERMOSTAT ("DevThermostat"), + UV ("DevUV"), + WIND ("DevWind"); + + private final String apiString; + + DeviceType(String apiString) { + this.apiString = apiString; + } + + public String getApiString() { + return apiString; + } + + public static DeviceType forApiString(String apiString) { + for (DeviceType deviceType : values()) { + if (deviceType.getApiString().equalsIgnoreCase(apiString.trim())) { + return deviceType; + } + } + + return null; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/DimmerDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/DimmerDevice.java new file mode 100644 index 0000000000000..ca297b0f025c5 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/DimmerDevice.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.DeviceParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; + +/** + * Dimmer device, containing on/off status and dim level. + * @author Pepijn de Geus - Initial contribution + */ +public class DimmerDevice extends AbstractEnergyLinkDevice { + + public DimmerDevice(Item item) { + super(DeviceType.DIMMER, item); + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + int level = 0; + + PercentType percentState = (PercentType) item.getStateAs(PercentType.class); + if (percentState != null) { + level = percentState.intValue(); + } + + addParam(new DeviceParam(ParamType.LEVEL, String.valueOf(level))); + addParam(new DeviceParam(ParamType.STATUS, (level > 0) ^ isInverted() ? "1" : "0")); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/ElectricityDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/ElectricityDevice.java new file mode 100644 index 0000000000000..acdfc361fd73b --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/ElectricityDevice.java @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.apache.commons.lang.StringUtils; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.NumericValueParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; +import org.openhab.io.imperihome.internal.processor.ItemProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Electricity device, containing current (Watt) and total (KWh) consumption. + * @author Pepijn de Geus - Initial contribution + */ +public class ElectricityDevice extends AbstractNumericValueDevice { + + private static final Logger LOGGER = LoggerFactory.getLogger(ElectricityDevice.class); + + private static final String LINK_WATTS = "watt"; + private static final String LINK_KWH = "kwh"; + + public ElectricityDevice(Item item) { + super(DeviceType.ELECTRICITY, item, null); + } + + @Override + public void addLink(String linkType, String deviceId) { + super.addLink(linkType, deviceId); + + if (getLinks().containsKey(LINK_WATTS)) { + setUnit("KWh"); + } else if (getLinks().containsKey(LINK_KWH)) { + setUnit("W"); + } + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + DecimalType value = (DecimalType) item.getStateAs(DecimalType.class); + + if (getLinks().containsKey(LINK_WATTS)) { + addParam(new NumericValueParam(ParamType.KWH, getUnit(), value)); + } else if (getLinks().isEmpty() || getLinks().containsKey(LINK_KWH)) { + addParam(new NumericValueParam(ParamType.WATTS, getUnit(), value)); + } + } + + @Override + public void updateParams() { + super.updateParams(); + + if (getLinks().containsKey(LINK_WATTS)) { + String deviceName = getLinks().get(LINK_WATTS); + String deviceId = ItemProcessor.getDeviceId(deviceName); + AbstractDevice wattsDevice = getDeviceRegistry().getDevice(deviceId); + if (wattsDevice == null) { + LOGGER.error("Couldn't resolve linked watts device '{}', make sure the Item has iss tags", deviceName); + } else { + setWattsParam(wattsDevice); + } + } + + if (getLinks().containsKey(LINK_KWH)) { + String deviceName = getLinks().get(LINK_KWH); + String deviceId = ItemProcessor.getDeviceId(deviceName); + AbstractDevice kwhDevice = getDeviceRegistry().getDevice(deviceId); + if (kwhDevice == null) { + LOGGER.error("Couldn't resolve linked KWh device '{}', make sure the Item has iss tags", deviceName); + } else { + setKwhParam(kwhDevice); + } + } + } + + private void setWattsParam(AbstractDevice device) { + NumericValueParam valueParam = (NumericValueParam) device.getParams().get(ParamType.WATTS); + if (valueParam == null) { + LOGGER.warn("Linked Watts device has no Watt value parameter: {}", device); + return; + } + + NumericValueParam wattsParam = new NumericValueParam(ParamType.WATTS, valueParam.getUnit(), null); + if (StringUtils.isEmpty(wattsParam.getUnit())) { + wattsParam.setUnit("W"); + } + wattsParam.setValue(valueParam.getValue()); + addParam(wattsParam); + } + + private void setKwhParam(AbstractDevice device) { + NumericValueParam valueParam = (NumericValueParam) device.getParams().get(ParamType.KWH); + if (valueParam == null) { + LOGGER.warn("Linked KWh device has no KWh value parameter: {}", device); + return; + } + + NumericValueParam kwhParam = new NumericValueParam(ParamType.KWH, valueParam.getUnit(), null); + if (StringUtils.isEmpty(kwhParam.getUnit())) { + kwhParam.setUnit("KWh"); + } + kwhParam.setValue(valueParam.getValue()); + addParam(kwhParam); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/GenericSensorDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/GenericSensorDevice.java new file mode 100644 index 0000000000000..c7f885289561b --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/GenericSensorDevice.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.DeviceParam; +import org.openhab.io.imperihome.internal.model.param.NumericValueParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; + +/** + * Generic sensor device. + * @author Pepijn de Geus - Initial contribution + */ +public class GenericSensorDevice extends AbstractNumericValueDevice { + + public GenericSensorDevice(Item item) { + super(DeviceType.GENERIC_SENSOR, item, null); + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + DecimalType value = (DecimalType) item.getStateAs(DecimalType.class); + if (value != null) { + addParam(new NumericValueParam(ParamType.GENERIC_VALUE, getUnit(), value)); + } else { + State state = item.getState(); + String strVal = (state == null) ? null : state.toFullString(); + addParam(new DeviceParam(ParamType.GENERIC_VALUE, strVal)); + } + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/HygrometryDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/HygrometryDevice.java new file mode 100644 index 0000000000000..494d85dfaae4d --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/HygrometryDevice.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.NumericValueParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; + +/** + * Hygrometry sensor device. + * @author Pepijn de Geus - Initial contribution + */ +public class HygrometryDevice extends AbstractNumericValueDevice { + + public HygrometryDevice(Item item) { + super(DeviceType.HYGROMETRY, item, "%"); + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + DecimalType value = (DecimalType) item.getStateAs(DecimalType.class); + addParam(new NumericValueParam(ParamType.HYGROMETRY_VALUE, getUnit(), value)); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/LockDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/LockDevice.java new file mode 100644 index 0000000000000..f41d1305b2d09 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/LockDevice.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.OpenClosedType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.DeviceParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; + +/** + * Lock device. + * @author Pepijn de Geus - Initial contribution + */ +public class LockDevice extends AbstractDevice { + + public LockDevice(Item item) { + super(DeviceType.LOCK, item); + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + if (getParams().get(ParamType.STATUS) == null) { + OpenClosedType openClosedState = (OpenClosedType) item.getStateAs(OpenClosedType.class); + if (openClosedState != null) { + boolean value = openClosedState == OpenClosedType.OPEN; + DeviceParam param = new DeviceParam(ParamType.STATUS, value ^ isInverted() ? "1" : "0"); + addParam(param); + } + } + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/LuminosityDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/LuminosityDevice.java new file mode 100644 index 0000000000000..85fc599128d4f --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/LuminosityDevice.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.NumericValueParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; + +/** + * Luminosity sensor device. + * @author Pepijn de Geus - Initial contribution + */ +public class LuminosityDevice extends AbstractNumericValueDevice { + + public LuminosityDevice(Item item) { + super(DeviceType.LUMINOSITY, item, "lux"); + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + DecimalType value = (DecimalType) item.getStateAs(DecimalType.class); + addParam(new NumericValueParam(ParamType.LUMINOSITY_VALUE, getUnit(), value)); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/MultiSwitchDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/MultiSwitchDevice.java new file mode 100644 index 0000000000000..bec949792a544 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/MultiSwitchDevice.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import java.util.Map; + +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.DeviceParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; + +import com.google.common.base.Joiner; + +/** + * MultiSwitch device, mimics behavior of a OH Switch with a mapping. + * @author Pepijn de Geus - Initial contribution + */ +public class MultiSwitchDevice extends AbstractDevice { + + private String itemValue = ""; + + public MultiSwitchDevice(Item item) { + super(DeviceType.MULTI_SWITCH, item); + } + + @Override + public void updateParams() { + super.updateParams(); + + Map mapping = getMapping(); + if (mapping == null || mapping.isEmpty()) { + LOGGER.error("MultiSwitch device {} contains no mapping", this); + return; + } + + DeviceParam choicesParam = new DeviceParam(ParamType.CHOICES, Joiner.on(',').join(mapping.values())); + addParam(choicesParam); + + //Find current value text + String currentValue = ""; + if (mapping.containsKey(itemValue)) { + currentValue = mapping.get(itemValue); + } + + DeviceParam valueParam = new DeviceParam(ParamType.MULTISWITCH_VALUE, currentValue); + addParam(valueParam); + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + State state = item.getStateAs(DecimalType.class); + if (state instanceof DecimalType) { + itemValue = String.valueOf(((DecimalType) state).intValue()); + } + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/NoiseDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/NoiseDevice.java new file mode 100644 index 0000000000000..3b3f5cbd89dfa --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/NoiseDevice.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.NumericValueParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; + +/** + * Noise sensor device. + * @author Pepijn de Geus - Initial contribution + */ +public class NoiseDevice extends AbstractNumericValueDevice { + + public NoiseDevice(Item item) { + super(DeviceType.NOISE, item, "dB"); + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + DecimalType value = (DecimalType) item.getStateAs(DecimalType.class); + addParam(new NumericValueParam(ParamType.NOISE_VALUE, getUnit(), value)); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/PressureDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/PressureDevice.java new file mode 100644 index 0000000000000..c2871dfea6851 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/PressureDevice.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.NumericValueParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; + +/** + * Pressure sensor device. + * @author Pepijn de Geus - Initial contribution + */ +public class PressureDevice extends AbstractNumericValueDevice { + + public PressureDevice(Item item) { + super(DeviceType.PRESSURE, item, "mbar"); + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + DecimalType value = (DecimalType) item.getStateAs(DecimalType.class); + addParam(new NumericValueParam(ParamType.PRESSURE_VALUE, getUnit(), value)); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/RainDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/RainDevice.java new file mode 100644 index 0000000000000..9b322f1a65ad4 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/RainDevice.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.apache.commons.lang.StringUtils; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.NumericValueParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; +import org.openhab.io.imperihome.internal.processor.ItemProcessor; + +/** + * Rain sensor device. + * @author Pepijn de Geus - Initial contribution + */ +public class RainDevice extends AbstractNumericValueDevice { + + private static final String LINK_ACCUMULATION = "accum"; + + public RainDevice(Item item) { + super(DeviceType.RAIN, item, "mm/h"); + } + + @Override + public void updateParams() { + super.updateParams(); + + if (getLinks().containsKey(LINK_ACCUMULATION)) { + String deviceName = getLinks().get(LINK_ACCUMULATION); + String deviceId = ItemProcessor.getDeviceId(deviceName); + AbstractDevice accumDevice = getDeviceRegistry().getDevice(deviceId); + if (accumDevice == null) { + LOGGER.error("Couldn't resolve linked accumulation device '{}', make sure the Item has iss tags", deviceName); + return; + } + + NumericValueParam valueParam = (NumericValueParam) accumDevice.getParams().get(ParamType.GENERIC_VALUE); + if (valueParam == null) { + LOGGER.warn("Linked Accumulation device has no Value parameter: {}", accumDevice); + return; + } + + NumericValueParam accumParam = new NumericValueParam(ParamType.ACCUMULATION, valueParam.getUnit(), null); + if (StringUtils.isEmpty(accumParam.getUnit())) { + accumParam.setUnit("mm"); + } + + accumParam.setValue(valueParam.getValue()); + addParam(accumParam); + } + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + DecimalType value = (DecimalType) item.getStateAs(DecimalType.class); + addParam(new NumericValueParam(ParamType.RAIN_VALUE, getUnit(), value)); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/RgbLightDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/RgbLightDevice.java new file mode 100644 index 0000000000000..b22a08222d8e5 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/RgbLightDevice.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import java.math.BigDecimal; + +import org.apache.commons.lang.StringUtils; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.HSBType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.DeviceParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; + +/** + * RGB light device. + * @author Pepijn de Geus - Initial contribution + */ +public class RgbLightDevice extends AbstractEnergyLinkDevice { + + public RgbLightDevice(Item item) { + super(DeviceType.RGB_LIGHT, item); + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + boolean status = false; + int level = 0; + String color = "00000000"; + + State state = item.getStateAs(HSBType.class); + boolean isHsbState = state instanceof HSBType; + + //State can be of UndefType, even with the getStateAs above + if (isHsbState) { + HSBType hsbState = (HSBType) state; + PercentType[] rgb = hsbState.toRGB(); + + //Set state to ON if any channel > 0 + boolean isOn = rgb[0].doubleValue() > 0 || rgb[1].doubleValue() > 0 || rgb[2].doubleValue() > 0; + if (isOn) { + status = true; + } + + //Build hex string + int r = convertPercentToByte(rgb[0]) & 0xFF; + int g = convertPercentToByte(rgb[1]) & 0xFF; + int b = convertPercentToByte(rgb[2]) & 0xFF; + color = (isOn ? "FF" : "00") + toHex(r) + toHex(g) + toHex(b); + } + + State pState = item.getStateAs(PercentType.class); + if (pState instanceof PercentType) { + level = ((PercentType) pState).intValue(); + if (level > 0) { + status = true; + } + } + + addParam(new DeviceParam(ParamType.STATUS, status ^ isInverted() ? "1" : "0")); + addParam(new DeviceParam(ParamType.LEVEL, String.valueOf(level))); + addParam(new DeviceParam(ParamType.DIMMABLE, "0")); + addParam(new DeviceParam(ParamType.WHITE_CHANNEL, "1")); + addParam(new DeviceParam(ParamType.COLOR, color)); + } + + private String toHex(int value) { + String hex = Integer.toHexString(value); + return StringUtils.leftPad(hex, 2, '0'); + } + + private int convertPercentToByte(PercentType percent) { + return percent.toBigDecimal().multiply(BigDecimal.valueOf(255)) + .divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP).intValue(); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/SceneDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/SceneDevice.java new file mode 100644 index 0000000000000..6aa16ed7a325c --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/SceneDevice.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; + +/** + * Scene activation device. + * @author Pepijn de Geus - Initial contribution + */ +public class SceneDevice extends AbstractDevice { + + public SceneDevice(Item item) { + super(DeviceType.SCENE, item); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/SwitchDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/SwitchDevice.java new file mode 100644 index 0000000000000..f522fa64b0209 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/SwitchDevice.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.DeviceParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; + +/** + * Simple on/off switch device. + * @author Pepijn de Geus - Initial contribution + */ +public class SwitchDevice extends AbstractEnergyLinkDevice { + + public SwitchDevice(Item item) { + super(DeviceType.SWITCH, item); + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + if (getParams().get(ParamType.STATUS) == null) { + PercentType percentState = (PercentType) item.getStateAs(PercentType.class); + if (percentState != null) { + boolean value = percentState.intValue() > 0; + DeviceParam param = new DeviceParam(ParamType.STATUS, value ^ isInverted() ? "1" : "0"); + addParam(param); + } + } + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/TempHygroDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/TempHygroDevice.java new file mode 100644 index 0000000000000..f1774ce2138c3 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/TempHygroDevice.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.NumericValueParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; +import org.openhab.io.imperihome.internal.processor.ItemProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Combined temperature/hygro sensor device. Can be specified on either a temp or hygro Item, with a link to the other one. + * The linked value will be retrieved in {@link #updateParams()}. + * @author Pepijn de Geus - Initial contribution + */ +public class TempHygroDevice extends AbstractNumericValueDevice { + + private static final Logger LOGGER = LoggerFactory.getLogger(TempHygroDevice.class); + + private static final String LINK_HYGRO = "hygro"; + private static final String LINK_TEMP = "temp"; + + public TempHygroDevice(Item item) { + super(DeviceType.TEMP_HYGRO, item, null); + } + + @Override + public void addLink(String linkType, String deviceId) { + super.addLink(linkType, deviceId); + + if (getLinks().containsKey(LINK_HYGRO)) { + setUnit("°C"); + } else if (getLinks().containsKey(LINK_TEMP)) { + setUnit("%"); + } + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + DecimalType value = (DecimalType) item.getStateAs(DecimalType.class); + + if (getLinks().containsKey(LINK_HYGRO)) { + addParam(new NumericValueParam(ParamType.TEMPERATURE_DUAL, getUnit(), value)); + } else if (getLinks().containsKey(LINK_TEMP)) { + addParam(new NumericValueParam(ParamType.HYGROMETRY_DUAL, getUnit(), value)); + } + } + + @Override + public void updateParams() { + super.updateParams(); + + boolean foundLink = false; + + if (getLinks().containsKey(LINK_HYGRO)) { + String deviceName = getLinks().get(LINK_HYGRO); + String deviceId = ItemProcessor.getDeviceId(deviceName); + AbstractDevice hygroDevice = getDeviceRegistry().getDevice(deviceId); + if (hygroDevice == null) { + LOGGER.error("Couldn't resolve linked hygro device '{}', make sure the Item has iss tags", deviceName); + } else { + setHygroParam(hygroDevice); + foundLink = true; + } + } + + if (getLinks().containsKey(LINK_TEMP)) { + String deviceName = getLinks().get(LINK_TEMP); + String deviceId = ItemProcessor.getDeviceId(deviceName); + AbstractDevice tempDevice = getDeviceRegistry().getDevice(deviceId); + if (tempDevice == null) { + LOGGER.error("Couldn't resolve linked temp device '{}', make sure the Item has iss tags", deviceName); + } else { + setTempParam(tempDevice); + foundLink = true; + } + } + + if (!foundLink) { + LOGGER.warn("DevTempHygro device contains no valid 'hygro' or 'temp' link. Add a link to another item using 'iss:link::'"); + } + } + + private void setHygroParam(AbstractDevice device) { + NumericValueParam valueParam = (NumericValueParam) device.getParams().get(ParamType.HYGROMETRY_VALUE); + if (valueParam == null) { + LOGGER.warn("Linked Hygro device has no Value parameter: {}", device); + return; + } + + NumericValueParam hygroParam = new NumericValueParam(ParamType.HYGROMETRY_DUAL, valueParam.getUnit(), null); + hygroParam.setValue(valueParam.getValue()); + addParam(hygroParam); + } + + private void setTempParam(AbstractDevice device) { + NumericValueParam valueParam = (NumericValueParam) device.getParams().get(ParamType.TEMPERATURE_VALUE); + if (valueParam == null) { + LOGGER.warn("Linked Temperature device has no Value parameter: {}", device); + return; + } + + NumericValueParam tempParam = new NumericValueParam(ParamType.TEMPERATURE_DUAL, valueParam.getUnit(), null); + tempParam.setValue(valueParam.getValue()); + addParam(tempParam); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/TemperatureDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/TemperatureDevice.java new file mode 100644 index 0000000000000..021f6f4b7d8e3 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/TemperatureDevice.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.NumericValueParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; + +/** + * Temperature sensor device. + * @author Pepijn de Geus - Initial contribution + */ +public class TemperatureDevice extends AbstractNumericValueDevice { + + public TemperatureDevice(Item item) { + super(DeviceType.TEMPERATURE, item, "°C"); + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + DecimalType value = (DecimalType) item.getStateAs(DecimalType.class); + addParam(new NumericValueParam(ParamType.TEMPERATURE_VALUE, getUnit(), value)); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/TrippableDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/TrippableDevice.java new file mode 100644 index 0000000000000..08b82fb6f3006 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/TrippableDevice.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.OpenClosedType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.DeviceParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; + +/** + * Abstraction of devices that are trippable, i.e. DevDoor, DevFlood, DevMotion, DevSmoke, DevCO2Alert. + * @author Pepijn de Geus - Initial contribution + */ +public class TrippableDevice extends AbstractDevice { + + public TrippableDevice(DeviceType type, Item item) { + super(type, item); + + addParam(new DeviceParam(ParamType.ARMABLE, "0")); + addParam(new DeviceParam(ParamType.ARMED, "1")); + addParam(new DeviceParam(ParamType.ACKABLE, "0")); + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + boolean tripped = false; + + List> acceptedDataTypes = item.getAcceptedDataTypes(); + if (acceptedDataTypes.contains(OpenClosedType.class)) { + OpenClosedType state = (OpenClosedType) item.getStateAs(OpenClosedType.class); + tripped = state == OpenClosedType.CLOSED; + } else if (acceptedDataTypes.contains(OnOffType.class)) { + OnOffType state = (OnOffType) item.getStateAs(OnOffType.class); + tripped = state == OnOffType.ON; + } else if (acceptedDataTypes.contains(DecimalType.class)) { + DecimalType state = (DecimalType) item.getStateAs(DecimalType.class); + tripped = state.intValue() != 0; + } else if (acceptedDataTypes.contains(StringType.class)) { + StringType state = (StringType) item.getStateAs(StringType.class); + tripped = StringUtils.isNotBlank(state.toString()) && !state.toString().trim().equals("ok"); + } else { + LOGGER.warn("Can't interpret state {} as tripped status", item.getState()); + } + + addParam(new DeviceParam(ParamType.TRIPPED, tripped ^ isInverted() ? "1" : "0")); + + if (tripped) { + addParam(new DeviceParam(ParamType.LAST_TRIP, String.valueOf(System.currentTimeMillis()))); + } + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/UvDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/UvDevice.java new file mode 100644 index 0000000000000..a921957af5800 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/UvDevice.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.NumericValueParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; + +/** + * UV sensor device. + * @author Pepijn de Geus - Initial contribution + */ +public class UvDevice extends AbstractNumericValueDevice { + + public UvDevice(Item item) { + super(DeviceType.PRESSURE, item, "index"); + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + DecimalType value = (DecimalType) item.getStateAs(DecimalType.class); + addParam(new NumericValueParam(ParamType.UV_VALUE, getUnit(), value)); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/WindDevice.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/WindDevice.java new file mode 100644 index 0000000000000..da46737ec113a --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/device/WindDevice.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.device; + +import org.apache.commons.lang.StringUtils; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.model.param.NumericValueParam; +import org.openhab.io.imperihome.internal.model.param.ParamType; +import org.openhab.io.imperihome.internal.processor.ItemProcessor; + +/** + * Wind sensor device. + * @author Pepijn de Geus - Initial contribution + */ +public class WindDevice extends AbstractNumericValueDevice { + + private static final String LINK_DIRECTION = "direction"; + + public WindDevice(Item item) { + super(DeviceType.WIND, item, "km/h"); + } + + @Override + public void updateParams() { + super.updateParams(); + + if (getLinks().containsKey(LINK_DIRECTION)) { + String deviceName = getLinks().get(LINK_DIRECTION); + String deviceId = ItemProcessor.getDeviceId(deviceName); + AbstractDevice dirDevice = getDeviceRegistry().getDevice(deviceId); + if (dirDevice == null) { + LOGGER.error("Couldn't resolve linked wind direction device '{}', make sure the Item has iss tags", deviceName); + return; + } + + NumericValueParam valueParam = (NumericValueParam) dirDevice.getParams().get(ParamType.GENERIC_VALUE); + if (valueParam == null) { + LOGGER.warn("Linked Wind direction device has no Value parameter: {}", dirDevice); + return; + } + + NumericValueParam dirParam = new NumericValueParam(ParamType.DIRECTION, valueParam.getUnit(), null); + if (StringUtils.isEmpty(dirParam.getUnit())) { + dirParam.setUnit("Degrees"); + } + + dirParam.setValue(valueParam.getValue()); + addParam(dirParam); + } + } + + @Override + public void stateUpdated(Item item, State newState) { + super.stateUpdated(item, newState); + + DecimalType value = (DecimalType) item.getStateAs(DecimalType.class); + addParam(new NumericValueParam(ParamType.SPEED, getUnit(), value)); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/param/DeviceParam.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/param/DeviceParam.java new file mode 100644 index 0000000000000..52c87a491ca81 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/param/DeviceParam.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.param; + +/** + * Basic device key/value parameter. + * @author Pepijn de Geus - Initial contribution + */ +public class DeviceParam { + + private ParamType key; + private Object value; + + public DeviceParam(ParamType type) { + this.key = type; + } + + public DeviceParam(ParamType type, Object value) { + this.key = type; + this.value = value; + } + + public ParamType getKey() { + return key; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof DeviceParam)) { + return false; + } + + DeviceParam that = (DeviceParam) o; + + if (key != that.key) { + return false; + } + return value != null ? value.equals(that.value) : that.value == null; + + } + + @Override + public int hashCode() { + return key.hashCode(); + } + + @Override + public String toString() { + return "DeviceParam{" + + "key=" + key + + ", value='" + value + '\'' + + '}'; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/param/DeviceParameters.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/param/DeviceParameters.java new file mode 100644 index 0000000000000..78cd91c91d34b --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/param/DeviceParameters.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.param; + +import java.util.HashMap; + +/** + * No-op extension of HashMap storing device parameters. This class exists because it allows the use of a Map in Device and at the same + * time makes it possible to expose the values as a JSON array using a custom serializer. + * @author Pepijn de Geus - Initial contribution + */ +public class DeviceParameters extends HashMap { + + public void set(DeviceParam param) { + put(param.getKey(), param); + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/param/NumericValueParam.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/param/NumericValueParam.java new file mode 100644 index 0000000000000..9cb5b256b2302 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/param/NumericValueParam.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.param; + +import org.eclipse.smarthome.core.library.types.DecimalType; + +/** + * Numeric value param + * @author Pepijn de Geus - Initial contribution + */ +public class NumericValueParam extends DeviceParam { + + private String unit; + private boolean graphable = true; + + public NumericValueParam(ParamType type, String unit) { + super(type); + setUnit(unit); + } + + public NumericValueParam(ParamType type, String unit, DecimalType value) { + this(type, unit); + setValue(value == null ? 0 : value.doubleValue()); + } + + public Number getValue() { + return (Number) super.getValue(); + } + + public void setValue(Number value) { + super.setValue(value); + } + + public String getUnit() { + return unit; + } + + public void setUnit(String unit) { + this.unit = unit; + } + + public boolean isGraphable() { + return graphable; + } + + public void setGraphable(boolean graphable) { + this.graphable = graphable; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof NumericValueParam)) { + return false; + } + if (!super.equals(o)) { + return false; + } + + NumericValueParam that = (NumericValueParam) o; + + if (graphable != that.graphable) { + return false; + } + return unit != null ? unit.equals(that.unit) : that.unit == null; + } + + @Override + public String toString() { + return "NumericValueParam{" + + "super=" + super.toString() + + ", unit='" + unit + '\'' + + ", graphable=" + graphable + + '}'; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/param/ParamType.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/param/ParamType.java new file mode 100644 index 0000000000000..64e1074bbb6d4 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/model/param/ParamType.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.model.param; + +/** + * Parameter type enumeration. Contains the ISS API parameter key string. + * @author Pepijn de Geus - Initial contribution + */ +public enum ParamType { + + GENERIC_VALUE("Value"), + TEMPERATURE_VALUE("Value"), + TEMPERATURE_DUAL("temp"), + LUMINOSITY_VALUE("Value"), + HYGROMETRY_DUAL("hygro"), + HYGROMETRY_VALUE("Value"), + CO2_VALUE("Value"), + PRESSURE_VALUE("Value"), + NOISE_VALUE("Value"), + RAIN_VALUE("Value"), + UV_VALUE("Value"), + DIMMABLE ("dimmable"), + ENERGY ("Energy"), + STATUS ("Status"), + MULTISWITCH_VALUE("Value"), + CHOICES("Choices"), + COLOR ("color"), + LEVEL ("Level"), + WHITE_CHANNEL ("whitechannel"), + WATTS ("Watts"), + KWH ("ConsoTotal"), + ARMABLE ("armable"), + ARMED ("Armed"), + ACKABLE ("ackable"), + TRIPPED ("Tripped"), + LAST_TRIP ("lasttrip"), + ACCUMULATION("Accumulation"), + SPEED ("Speed"), + DIRECTION ("Direction"); + + private final String apiString; + + ParamType(String apiString) { + this.apiString = apiString; + } + + public String getApiString() { + return apiString; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/processor/DeviceRegistry.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/processor/DeviceRegistry.java new file mode 100644 index 0000000000000..8797f2dcada22 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/processor/DeviceRegistry.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.processor; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.openhab.io.imperihome.internal.model.Room; +import org.openhab.io.imperihome.internal.model.device.AbstractDevice; + +/** + * The device registry stores created devices by ID. + * @author Pepijn de Geus - Initial contribution + */ +public class DeviceRegistry implements Iterable { + + private final Map devices; + private Set rooms; + + public DeviceRegistry() { + devices = new ConcurrentHashMap<>(); + } + + public AbstractDevice getDevice(String deviceId) { + return devices.get(deviceId); + } + + public Map getDevices() { + return new HashMap<>(devices); + } + + public Collection getRooms() { + return new HashSet<>(rooms); + } + + public boolean hasDevices() { + return !devices.isEmpty(); + } + + public boolean hasDevice(String deviceId) { + return devices.containsKey(deviceId); + } + + public void add(AbstractDevice device) { + devices.put(device.getId(), device); + updateRooms(); + } + + public AbstractDevice remove(String deviceId) { + AbstractDevice removed = devices.remove(deviceId); + updateRooms(); + return removed; + } + + @Override + public Iterator iterator() { + return devices.values().iterator(); + } + + public void clear() { + devices.clear(); + + if (rooms != null) { + rooms.clear(); + } + } + + private void updateRooms() { + Set newRooms = new HashSet<>(); + for (AbstractDevice device : devices.values()) { + Room room = new Room(); + room.setId(device.getRoom()); + room.setName(device.getRoomName()); + newRooms.add(room); + } + rooms = newRooms; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/processor/ItemProcessor.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/processor/ItemProcessor.java new file mode 100644 index 0000000000000..287752811ad93 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/processor/ItemProcessor.java @@ -0,0 +1,360 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.processor; + +import java.util.Collection; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang.StringUtils; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.ItemRegistry; +import org.eclipse.smarthome.core.items.ItemRegistryChangeListener; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.HSBType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.OpenClosedType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.imperihome.internal.action.ActionRegistry; +import org.openhab.io.imperihome.internal.model.device.*; +import org.openhab.io.imperihome.internal.util.DigestUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Processor of openHAB Items. Parses ISS tags and creates and registers {@link AbstractDevice} implementations where applicable. + * @author Pepijn de Geus - Initial contribution + */ +public class ItemProcessor implements ItemRegistryChangeListener { + + private static final Logger LOGGER = LoggerFactory.getLogger(ItemProcessor.class); + + private static final String PREFIX_ISS = "iss:"; + + private final ItemRegistry itemRegistry; + private final DeviceRegistry deviceRegistry; + private final ActionRegistry actionRegistry; + + public ItemProcessor(ItemRegistry itemRegistry, DeviceRegistry deviceRegistry, ActionRegistry actionRegistry) { + this.itemRegistry = itemRegistry; + this.deviceRegistry = deviceRegistry; + this.actionRegistry = actionRegistry; + + allItemsChanged(null); + itemRegistry.addRegistryChangeListener(this); + } + + public void destroy() { + itemRegistry.removeRegistryChangeListener(this); + + //Destroy all Devices (unregisters state listeners) + for (Item item : itemRegistry.getItems()) { + String deviceId = getDeviceId(item); + if (deviceRegistry.hasDevice(deviceId)) { + deviceRegistry.remove(deviceId).destroy(); + } + } + } + + private void parseItem(Item item) { + Map> issTags = getIssTags(item); + if (!issTags.isEmpty()) { + LOGGER.debug("Found item {} with ISS tags: {}", item, issTags); + + DeviceType deviceType = getDeviceType(item, issTags); + if (deviceType == null) { + LOGGER.warn("Unrecognized device type for item: {}", item); + } else { + AbstractDevice device = getDeviceInstance(deviceType, item); + device.setId(getDeviceId(item)); + device.setName(getLabel(item, issTags)); + device.setInverted(isInverted(issTags)); + device.setActionRegistry(actionRegistry); + + setDeviceRoom(device, issTags); + setDeviceLinks(device, item, issTags); + setMapping(device, item, issTags); + setUnit(device, issTags); + + //Set initial state + device.stateUpdated(item, item.getState()); + + LOGGER.debug("Item parsed to device: {}", device); + deviceRegistry.add(device); + } + } + } + + private AbstractDevice getDeviceInstance(DeviceType deviceType, Item item) { + switch (deviceType) { + case SWITCH: + return new SwitchDevice(item); + case DIMMER: + return new DimmerDevice(item); + case RGB_LIGHT: + return new RgbLightDevice(item); + case TEMPERATURE: + return new TemperatureDevice(item); + case TEMP_HYGRO: + return new TempHygroDevice(item); + case LUMINOSITY: + return new LuminosityDevice(item); + case HYGROMETRY: + return new HygrometryDevice(item); + case CO2: + return new Co2SensorDevice(item); + case ELECTRICITY: + return new ElectricityDevice(item); + case SCENE: + return new SceneDevice(item); + case MULTI_SWITCH: + return new MultiSwitchDevice(item); + case GENERIC_SENSOR: + return new GenericSensorDevice(item); + case PRESSURE: + return new PressureDevice(item); + case UV: + return new UvDevice(item); + case NOISE: + return new NoiseDevice(item); + case RAIN: + return new RainDevice(item); + case WIND: + return new WindDevice(item); + case LOCK: + return new LockDevice(item); + case CO2_ALERT: + case SMOKE: + case DOOR: + case MOTION: + case FLOOD: + return new TrippableDevice(deviceType, item); + } + + throw new IllegalArgumentException("Unknown device type: " + deviceType); + } + + private String getLabel(Item item, Map> issTags) { + if (issTags.containsKey(TagType.LABEL)) { + return issTags.get(TagType.LABEL).get(0); + } + + if (StringUtils.isNotBlank(item.getLabel())) { + String label = item.getLabel().trim(); + if (label.matches("\\[.*\\]$")) { + label = label.substring(0, label.indexOf("[")); + } + return label; + } + + return item.getName(); + } + + private boolean isInverted(Map> issTags) { + return issTags.containsKey(TagType.INVERT) && BooleanUtils.toBoolean(issTags.get(TagType.INVERT).get(0)); + } + + private void setDeviceRoom(AbstractDevice device, Map> issTags) { + String roomName = "No room"; + if (issTags.containsKey(TagType.ROOM)) { + roomName = issTags.get(TagType.ROOM).get(0); + } + + device.setRoom(DigestUtil.sha1(roomName)); + device.setRoomName(roomName); + } + + private void setDeviceLinks(AbstractDevice device, Item item, Map> issTags) { + if (issTags.containsKey(TagType.LINK)) { + //Pass device registry to device for linked device lookup + device.setDeviceRegistry(deviceRegistry); + + //Parse link tags + for (String link : issTags.get(TagType.LINK)) { + String[] parts = link.split(":"); + if (parts.length == 2) { + device.addLink(parts[0].toLowerCase().trim(), parts[1].trim()); + } else { + LOGGER.error("Item has incorrect link format (should be 'iss:link::'): {}", item); + } + } + } + } + + /** + * Parses a mapping tag, if it exists. Format: "iss:mapping:1=Foo,2=Bar,3=Foobar". + */ + private void setMapping(AbstractDevice device, Item item, Map> issTags) { + if (issTags.containsKey(TagType.MAPPING)) { + String mapItems = issTags.get(TagType.MAPPING).get(0); + + Map mapping = new HashMap<>(); + for (String mapItem : mapItems.split(",")) { + String[] keyVal = mapItem.split("=", 2); + if (keyVal.length != 2) { + LOGGER.error("Invalid mapping syntax for Item {}", item); + return; + } + mapping.put(keyVal[0].trim(), keyVal[1].trim()); + } + + device.setMapping(mapping); + } + } + + /** + * Parses the unit tag, if it exists. Format: "iss:unit:°C". + */ + private void setUnit(AbstractDevice device, Map> issTags) { + if (issTags.containsKey(TagType.UNIT)) { + if (!(device instanceof AbstractNumericValueDevice)) { + LOGGER.warn("Unit tag is not supported for device {}", device); + return; + } + + ((AbstractNumericValueDevice) device).setUnit(issTags.get(TagType.UNIT).get(0)); + } + } + + /** + * Determines the Device type for the given Item. Uses the 'type' tag first, tries to auto-detect the type if no such tag exists. + */ + private DeviceType getDeviceType(Item item, Map> issTags) { + if (issTags.containsKey(TagType.TYPE)) { + return DeviceType.forApiString(issTags.get(TagType.TYPE).get(0)); + } + + List> acceptedDataTypes = item.getAcceptedDataTypes(); + String name = item.getName().toLowerCase(); + + if (acceptedDataTypes.contains(DecimalType.class)) { + if (name.contains("tempe")) { + return DeviceType.TEMPERATURE; + } else if (name.contains("lumi")) { + return DeviceType.LUMINOSITY; + } else if (name.contains("hygro")) { + return DeviceType.HYGROMETRY; + } else if (name.contains("wind")) { + return DeviceType.WIND; + } else { + return DeviceType.GENERIC_SENSOR; + } + } + + if (acceptedDataTypes.contains(HSBType.class)) { + return DeviceType.RGB_LIGHT; + } + + if (acceptedDataTypes.contains(OpenClosedType.class)) { + return DeviceType.DOOR; + } + if (acceptedDataTypes.contains(OnOffType.class)) { + return DeviceType.SWITCH; + } + + return null; + } + + private Map> getIssTags(Item item) { + Map> tags = new EnumMap<>(TagType.class); + + for (String tag : item.getTags()) { + if (tag.startsWith(PREFIX_ISS)) { + String issTag = tag.substring(PREFIX_ISS.length()); + for (TagType tagType : TagType.values()) { + if (issTag.startsWith(tagType.getPrefix() + ":")) { + String tagValue = issTag.substring(tagType.getPrefix().length() + 1); + if (!tags.containsKey(tagType)) { + tags.put(tagType, new LinkedList()); + } else if (!tagType.isMultiValue()) { + LOGGER.error("Found multiple values for tag " + tagType.getPrefix() + " - only first value is used"); + } + tags.get(tagType).add(tagValue); + break; + } + } + } + } + + return tags; + } + + /** + * Removes the given item for the device list. + * @param item Item to remove. + */ + private void removeItem(Item item) { + removeItem(item.getName()); + } + + /** + * Removes the given item for the device list. + * @param itemName Name of the Item to remove. + */ + private void removeItem(String itemName) { + String deviceId = getDeviceId(itemName); + if (deviceRegistry.hasDevice(deviceId)) { + LOGGER.debug("Removing Device from ISS registry for Item: {}", itemName); + deviceRegistry.remove(deviceId).destroy(); + } + } + + /** + * Generates an unique device ID for the given item. + */ + public static String getDeviceId(Item item) { + return getDeviceId(item.getName()); + } + + /** + * Generates an unique device ID for the given item name. + */ + public static String getDeviceId(String itemName) { + return DigestUtil.sha1(itemName); + } + + @Override + public void added(Item item) { + parseItem(item); + } + + @Override + public void removed(Item item) { + removeItem(item); + } + + @Override + public void updated(Item oldItem, Item newItem) { + removeItem(oldItem); + parseItem(newItem); + } + + @Override + public void allItemsChanged(Collection oldItems) { + if (oldItems != null) { + for (String oldItem : oldItems) { + removeItem(oldItem); + } + } + + if (deviceRegistry.hasDevices()) { + LOGGER.warn("There are still Devices left after processing all Items from allItemsChanged(): " + deviceRegistry.getDevices()); + deviceRegistry.clear(); + } + + for (Item item : itemRegistry.getItems()) { + parseItem(item); + } + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/processor/TagType.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/processor/TagType.java new file mode 100644 index 0000000000000..1da33f54ce8ed --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/processor/TagType.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.processor; + +/** + * ISS tag types enumeration. + * @author Pepijn de Geus - Initial contribution + */ +public enum TagType { + + LABEL ("label", false), + ROOM ("room", false), + TYPE ("type", false), + MAPPING ("mapping", false), + LINK ("link", true), + UNIT("unit", false), + INVERT("invert", false); + + private final String prefix; + private final boolean multiValue; + + TagType(String prefix, boolean multiValue) { + this.prefix = prefix; + this.multiValue = multiValue; + } + + /** + * @return Tag prefix. + */ + public String getPrefix() { + return prefix; + } + + /** + * @return True if this tag may exist multiple times on a single item. + */ + public boolean isMultiValue() { + return multiValue; + } + +} diff --git a/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/util/DigestUtil.java b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/util/DigestUtil.java new file mode 100644 index 0000000000000..ef86fdfdb6cc9 --- /dev/null +++ b/addons/io/org.openhab.io.imperihome/src/main/java/org/openhab/io/imperihome/internal/util/DigestUtil.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.io.imperihome.internal.util; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * Digest utility. + * @author Pepijn de Geus - Initial contribution + */ +public final class DigestUtil { + + public static String sha1(String input) { + try { + MessageDigest crypt = MessageDigest.getInstance("SHA-1"); + crypt.reset(); + crypt.update(input.getBytes("UTF-8")); + + return new BigInteger(1, crypt.digest()).toString(16); + } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { + throw new RuntimeException("Could not generate SHA-1 hash", e); + } + } + + //Hidden constructor + private DigestUtil() { + } + +} diff --git a/addons/io/pom.xml b/addons/io/pom.xml index f262be01c6ea1..f2c4bf3ddf364 100644 --- a/addons/io/pom.xml +++ b/addons/io/pom.xml @@ -19,10 +19,12 @@ org.openhab.io.myopenhab org.openhab.io.hueemulation org.openhab.io.transport.feed + org.openhab.io.imperihome + jdk8 [1.8,) diff --git a/features/openhab-addons/src/main/feature/feature.xml b/features/openhab-addons/src/main/feature/feature.xml index 153218aaa925d..6b9ddc5397df4 100644 --- a/features/openhab-addons/src/main/feature/feature.xml +++ b/features/openhab-addons/src/main/feature/feature.xml @@ -271,6 +271,12 @@ mvn:org.openhab.io/org.openhab.io.homekit/${project.version} + + openhab-runtime-base + esh-model-item + mvn:org.openhab.io/org.openhab.io.imperihome/${project.version} + +