[omnilink] Initial contribution (#8922)

Signed-off-by: Ethan Dye <mrtops03@gmail.com>

Co-authored-by: Dan Cunningham <dan@digitaldan.com>
Co-authored-by: Craig Hamilton <craigh@quailholdings.com>
Co-authored-by: Brian O'Connell <boc@us.ibm.com>

Co-authored-by: Dan Cunningham <dan@digitaldan.com>
Co-authored-by: Craig Hamilton <craigh@quailholdings.com>
Co-authored-by: Brian O'Connell <boc@us.ibm.com>
This commit is contained in:
Ethan Dye 2021-01-19 16:31:10 -07:00 committed by GitHub
parent 25434a7f18
commit 63b81792a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 6819 additions and 0 deletions

View File

@ -185,6 +185,7 @@
/bundles/org.openhab.binding.oceanic/ @kgoderis
/bundles/org.openhab.binding.ojelectronics/ @EvilPingu
/bundles/org.openhab.binding.omnikinverter/ @hansbogert
/bundles/org.openhab.binding.omnilink/ @ecdye
/bundles/org.openhab.binding.onebusaway/ @sdwilsh
/bundles/org.openhab.binding.onewire/ @J-N-K
/bundles/org.openhab.binding.onewiregpio/ @aogorek

View File

@ -911,6 +911,11 @@
<artifactId>org.openhab.binding.omnikinverter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.omnilink</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.onebusaway</artifactId>

View File

@ -0,0 +1,20 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons
== Third-party Content
jomnilink
* License: EPL-2.0
* Project: https://github.com/digitaldan/jomnilink
* Source: https://github.com/digitaldan/jomnilink

View File

@ -0,0 +1,321 @@
# HAI/Leviton OmniLink Binding
This binding integrates the [OmniPro and Lumina](http://www.leviton.com/en/products/security-automation/automation-av-controllers/omni-security-systems) line of home automation systems.
At its core the OmniPro is a hardware board that provides security and access features.
It connects to many other devices through serial ports or wired contacts and exposes them through a single TCP based API.
## Supported Things
The OmniPro/Lumina controller acts as a "bridge" for accessing other connected devices.
| Omni type | Hardware Type | Things |
|:---------------------------|:-------------------------------------------------|:----------------------------------|
| Controller | Omni (Pro II, IIe, LTe), Lumina | `controller` (omni, lumina) |
| Lights | Built-in, UPB, HLC | `unit`, `dimmable`, `upb`, `room` |
| Thermostats | Omnistat, Omnistat2 | `thermostat` |
| Temperature Sensors | 31A00-1/31A00-7 | `temp_sensor` |
| Humidity Sensors | 31A00-2 | `humidity_sensor` |
| Zones | Built-in/Hardwire, GE Wireless | `zone` |
| Audio Zones/Sources | HAI Hi-Fi, Russound, NuVo, Xantech, Speakercraft | `audio_zone`, `audio_source` |
| Consoles | HAI Omni Console, HAI Lumina Console | `console` |
| Areas | Built-in | `area`, `lumina_area` |
| Buttons | Built-in | `button` |
| Flags | Built-in | `flag` |
| Output | Built-in/Hardwire | `output` |
| Access Control Reader Lock | Leviton Access Control Reader | `lock` |
## Discovery
### Controller
Omni and Lumina controllers must be manually added using the IP and port of the controller as well as the 2 encryption keys required for network access.
### Devices
Once a connection can be established to a controller, all connected devices will be automatically discovered and added to the inbox.
## Thing Configuration
An Omni or Lumina controller requires the IP address (`ipAddress`), optional port (`port` defaults to 4369), and 2 encryption keys (`key1`, `key2`).
The hexadecimal pairs in the encryption keys are typically delimited using a colon`:`, but dashes `-`, spaces ` ` or no delimiter may be used.
In the thing file, this looks like:
```
Bridge omnilink:controller:home [ ipAddress="127.0.0.1", port=4369, key1="XXXXXXXXXXXXXXXX", key2="XXXXXXXXXXXXXXXX" ] {
// Add your things here
}
```
The devices are identified by the device number that the OmniLink bridge assigns to them, see the [Full Example](#full-example) section below for a manual configuration example.
## Channels
The devices support some of the following channels:
| Channel Type ID | Item Type | Description | Thing types supporting this channel |
|-----------------------------|----------------------|--------------------------------------------------------------------------------------|-----------------------------------------------------|
| `activate_keypad_emergency` | Number | Activate a burglary, fire, or auxiliary keypad emergency alarm on Omni based models. | `area` |
| `alarm_burglary` | Switch | Indicates if a burglary alarm is active. | `area` |
| `alarm_fire` | Switch | Indicates if a fire alarm is active. | `area` |
| `alarm_gas` | Switch | Indicates if a gas alarm is active. | `area` |
| `alarm_auxiliary` | Switch | Indicates if a auxiliary alarm is active. | `area` |
| `alarm_freeze` | Switch | Indicates if a freeze alarm is active. | `area` |
| `alarm_water` | Switch | Indicates if a water alarm is active. | `area` |
| `alarm_duress` | Switch | Indicates if a duress alarm is active. | `area` |
| `alarm_temperature` | Switch | Indicates if a temperature alarm is active. | `area` |
| `mode` | Number | Represents the area security mode. | `area`, `lumina_area` |
| `disarm` | String | Send a 4 digit user code to disarm the system. | `area` |
| `day` | String | Send a 4 digit user code to arm the system to day. | `area` |
| `night` | String | Send a 4 digit user code to arm the system to night. | `area` |
| `away` | String | Send a 4 digit user code to arm the system to away. | `area` |
| `vacation` | String | Send a 4 digit user code to arm the system to vacation. | `area` |
| `day_instant` | String | Send a 4 digit user code to arm the system to day instant. | `area` |
| `night_delayed` | String | Send a 4 digit user code to arm the system to night delayed. | `area` |
| `home` | String | Send a 4 digit user code to set the system to home. | `lumina_area` |
| `sleep` | String | Send a 4 digit user code to set the system to sleep. | `lumina_area` |
| `away` | String | Send a 4 digit user code to set the system to away. | `lumina_area` |
| `vacation` | String | Send a 4 digit user code to set the system to vacation. | `lumina_area` |
| `party` | String | Send a 4 digit user code to set the system to party. | `lumina_area` |
| `special` | String | Send a 4 digit user code to set the system to special. | `lumina_area` |
| `source_text_{1,2,3,4,5,6}` | String | A line of metadata from this audio source. | `audio_source` |
| `polling` | Switch | Enable or disable polling of this audio source. | `audio_source` |
| `zone_power` | Switch | Power status of this audio zone. | `audio_zone` |
| `zone_mute` | Switch | Mute status of this audio zone. | `audio_zone` |
| `zone_volume` | Dimmer | Volume level of this audio zone. | `audio_zone` |
| `zone_source` | Number | Source for this audio zone. | `audio_zone` |
| `zone_control` | Player | Control the audio zone, e.g. start/stop/next/previous. | `audio_zone` |
| `sysdate` | DateTime | Set controller date/time. | `controller` |
| `last_log` | String | Last log message on the controller, represented in JSON. | `controller` |
| `enable_disable_beeper` | Switch | Enable/Disable the beeper for this/all console(s). | `controller`, `console` |
| `beep` | Switch | Send a beep command to this/all console(s). | `controller`, `console` |
| `press` | Switch | Sends a button event to the controller. | `button` |
| `low_setpoint` | Number | The current low setpoint for this humidity/temperature sensor. | `temp_sensor`, `humidity_sensor` |
| `high_setpoint` | Number | The current high setpoint for this humidity/temperature sensor. | `temp_sensor`, `humidity_sensor` |
| `temperature` | Number:Temperature | The current temperature at this thermostat/temperature sensor. | `thermostat`, `temp_sensor` |
| `humidity` | Number:Dimensionless | The current relative humidity at this thermostat/humidity sensor. | `thermostat`, `humidity_sensor` |
| `freeze_alarm` | Contact | Closed when freeze alarm is triggered by this thermostat. | `thermostat` |
| `comm_failure` | Contact | Closed during a communications failure with this thermostat. | `thermostat` |
| `outdoor_temperature` | Number:Temperature | The current outdoor temperature detected by this thermostat. | `thermostat` |
| `heat_setpoint` | Number:Temperature | The current low/heating setpoint of this thermostat. | `thermostat` |
| `cool_setpoint` | Number:Temperature | The current high/cooling setpoint of this thermostat. | `thermostat` |
| `humidify_setpoint` | Number:Dimensionless | The current low/humidify setpoint for this thermostat. | `thermostat` |
| `dehumidify_setpoint` | Number:Dimensionless | The current high/dehumidify setpoint for this thermostat. | `thermostat` |
| `system_mode` | Number | The current system mode of this thermostat. | `thermostat` |
| `fan_mode` | Number | The current fan mode of this thermostat. | `thermostat` |
| `hold_status` | Number | The current hold status of this thermostat. | `thermostat` |
| `status` | Number | The current numeric status of this thermostat. | `thermostat` |
| `level` | Dimmer | Increase/Decrease the level of this unit/dimmable unit/UPB unit. | `unit`, `dimmable`, `upb` |
| `switch` | Switch | Turn this unit/dimmable unit/flag/output/room on/off. | `unit`, `dimmable`, `upb`, `flag`, `output`, `room` |
| `on_for_seconds` | Number | Turn on this unit for a specified number of seconds. | `unit`, `dimmable`, `upb`, `flag`, `output` |
| `off_for_seconds` | Number | Turn off this unit for a specified number of seconds. | `unit`, `dimmable`, `upb`, `flag`, `output` |
| `on_for_minutes` | Number | Turn on this unit for a specified number of minutes. | `unit`, `dimmable`, `upb`, `flag`, `output` |
| `off_for_minutes` | Number | Turn off this unit for a specified number of minutes. | `unit`, `dimmable`, `upb`, `flag`, `output` |
| `on_for_hours` | Number | Turn on this unit for a specified number of hours. | `unit`, `dimmable`, `upb`, `flag`, `output` |
| `off_for_hours` | Number | Turn off this unit for a specified number of hours. | `unit`, `dimmable`, `upb`, `flag`, `output` |
| `upb_status` | String | Send a UPB status request message for this UPB unit to the controller. | `upb` |
| `value` | Number | Numeric value of this flag. | `flag` |
| `scene_{a,b,c,d}` | Switch | Turn this scene on/off. | `room` |
| `state` | Number | The current state of this room. | `room` |
| `contact` | Contact | Contact state information of this zone. | `zone` |
| `current_condition` | Number | Current condition of this zone. | `zone` |
| `latched_alarm_status` | Number | Latched alarm status of this zone. | `zone` |
| `arming_status` | Number | Arming status of this zone. | `zone` |
| `bypass` | String | Send a 4 digit user code to bypass this zone. | `zone` |
| `restore` | String | Send a 4 digit user code to restore this zone. | `zone` |
### Trigger Channels
The devices support some of the following trigger channels:
| Channel Type ID | Description | Thing types supporting this channel |
|-------------------------------|--------------------------------------------------------------------------------------|-------------------------------------|
| `all_on_off_event` | Event sent when an all on/off event occurs. | `area`, `lumina_area` |
| `phone_line_event` | Event sent when the phone line changes state. | `controller` |
| `ac_power_event` | Event sent when AC trouble conditions are detected. | `controller` |
| `battery_event` | Event sent when battery trouble conditions are detected. | `controller` |
| `dcm_event` | Event sent when digital communicator trouble conditions are detected. | `controller` |
| `energy_cost_event` | Event sent when the cost of energy changes. | `controller` |
| `camera_trigger_event` | Event sent when a camera trigger is detected. | `controller` |
| `upb_link_activated_event` | Event sent when a UPB link is activated. | `controller` |
| `upb_link_deactivated_event` | Event sent when a UPB link is deactivated. | `controller` |
| `activated_event` | Event sent when a button is activated. | `button` |
| `switch_press_event` | Event sent when an ALC, UPB, Radio RA, or Starlite switch is pressed. | `dimmable`, `upb` |
## Full Example
### Example `omnilink.things`
```
Bridge omnilink:controller:home [ ipAddress="127.0.0.1", port=4369, key1="XXXXXXXXXXXXXXXX", key2="XXXXXXXXXXXXXXXX" ] {
Thing area MainArea "Main Area" @ "Home" [ number=1 ]
Thing upb UpKitTable "Table Lights" @ "Upstairs Kitchen" [ number=4 ]
Thing upb UpOfcDesk "Desk Lights" @ "Upstairs Office" [ number=10 ]
Thing thermostat UpstrsThermo "Upstairs Temperature" @ "Upstairs Entry" [ number=1 ]
Thing zone FrontDoor "Front Door" @ "Upstairs Entry" [ number=2 ]
Thing zone GarageDoor "Garage Door" @ "Laundry Room" [ number=3 ]
Thing zone BackDoor "Back Door" @ "Upstairs Kitchen" [ number=4 ]
Thing zone OneCarGarageDo "One Car Garage" @ "Garage" [ number=5 ]
Thing zone TwoCarGarageDo "Two Car Garage" @ "Garage" [ number=6 ]
Thing zone BsmtBackDoor "Back Door" @ "Basement Workout Room" [ number=8 ]
Thing zone MBRDeckDoor "Deck Door" @ "Master Bedroom" [ number=9 ]
Thing zone MBRMotion "Motion" @ "Master Bedroom" [ number=10 ]
Thing zone PorchDoor "Porch Door" @ "Upstairs Office" [ number=11 ]
Thing zone UpOffMotion "Motion" @ "Upstairs Office" [ number=12 ]
Thing zone UpLivMotion "Motion" @ "Upstairs Living Room" [ number=13 ]
Thing zone BsmtWORMotion "Motion" @ "Basement Workout Room" [ number=14 ]
Thing zone GarageMotion "Motion" @ "Garage" [ number=15 ]
Thing console UpstrsConsole "Console" @ "Laundry Room" [ number=1 ]
Thing button MainButton "Button" @ "Home" [ number=1 ]
}
```
### Example `omnilink.items`
```
/*
* Alarms / Areas
*/
Group:Switch:OR(ON, OFF) Alarms "All Alarms [%s]"
String AlarmMode "Alarm [%s]" <alarm> {channel="omnilink:area:home:MainArea:mode" [profile="transform:MAP", function="area-modes.map", sourceFormat="%s"]}
Switch AlarmBurglary "Burglary Alarm [%s]" (Alarms) {channel="omnilink:area:home:MainArea:alarm_burglary"}
Switch AlarmFire "Fire Alarm [%s]" (Alarms) {channel="omnilink:area:home:MainArea:alarm_fire"}
Switch alarm_gas "Gas Alarm [%s]" (Alarms) {channel="omnilink:area:home:MainArea:alarm_gas"}
Switch AlarmAuxiliary "Auxiliary Alarm [%s]" (Alarms) {channel="omnilink:area:home:MainArea:alarm_auxiliary"}
Switch AlarmFreeze "Freeze Alarm [%s]" (Alarms) {channel="omnilink:area:home:MainArea:alarm_freeze"}
Switch AlarmWater "Water Alarm [%s]" (Alarms) {channel="omnilink:area:home:MainArea:alarm_water"}
Switch AlarmDuress "Duress Alarm [%s]" (Alarms) {channel="omnilink:area:home:MainArea:alarm_duress"}
Switch AlarmTemperature "Temperature Alarm [%s]" (Alarms) {channel="omnilink:area:home:MainArea:alarm_temperature"}
Number AlarmModeDisarm {channel="omnilink:area:home:MainArea:disarm"}
Number AlarmModeDay {channel="omnilink:area:home:MainArea:day"}
Number AlarmModeNight {channel="omnilink:area:home:MainArea:night"}
Number AlarmModeAway {channel="omnilink:area:home:MainArea:away"}
Number AlarmModeVacation {channel="omnilink:area:home:MainArea:vacation"}
Number AlarmModeDayInstant {channel="omnilink:area:home:MainArea:day_instant"}
Number AlarmModeNightDelayed {channel="omnilink:area:home:MainArea:night_delayed"}
/*
* Lights
*/
Switch UpKitTable "Table Lights [%s]" <switch> {channel="omnilink:upb:home:UpKitTable:level"}
Dimmer UpOfcDesk "Desk Lights [%d]" <slider> {channel="omnilink:upb:home:UpOfcDesk:level"}
/*
* Thermostat
*/
Group UpstrsThermo "Upstairs Thermostat"
Number:Temperature UpstrsThermo_Temp "Temperature [%.1f %unit%]" <temperature> (UpstrsThermo) {channel="omnilink:thermostat:home:UpstrsThermo:temperature"}
Number UpstrsThermo_Status "Status [MAP(therm-status.map):%s]" <heating> (UpstrsThermo) {channel="omnilink:thermostat:home:UpstrsThermo:status"}
Number UpstrsThermo_System "System Mode [MAP(therm-tempmode.map):%s]" <temperature> (UpstrsThermo) {channel="omnilink:thermostat:home:UpstrsThermo:system_mode"}
Number UpstrsThermo_Fan "Fan Mode [MAP(therm-fanmode.map):%s]" <fan> (UpstrsThermo) {channel="omnilink:thermostat:home:UpstrsThermo:fan_mode"}
Number UpstrsThermo_Hold "Hold Mode [MAP(therm-holdmode.map):%s]" <fan> (UpstrsThermo) {channel="omnilink:thermostat:home:UpstrsThermo:hold_mode"}
Number UpstrsThermo_HeatPoint "System HeatPoint [%d]" <temperature_hot> (UpstrsThermo) {channel="omnilink:thermostat:home:UpstrsThermo:heat_setpoint"}
Number UpstrsThermo_CoolPoint "System CoolPoint [%d]" <temperature_cool> (UpstrsThermo) {channel="omnilink:thermostat:home:UpstrsThermo:cool_setpoint"}
/*
* Motion and Doors
*/
Group:Contact:OR(OPEN, CLOSED) Doors "All Doors [%s]"
Contact FrontDoor "Front Door" <door> (Doors) {channel="omnilink:zone:home:FrontDoor:contact"}
Contact GarageDoor "Garage Door" <door> (Doors) {channel="omnilink:zone:home:GarageDoor:contact"}
Contact BackDoor "Back Door" <door> (Doors) {channel="omnilink:zone:home:BackDoor:contact"}
Contact BsmtBackDoor "Back Door" <door> (Doors) {channel="omnilink:zone:home:BsmtBackDoor:contact"}
Contact MBRDeckDoor "Deck Door" <door> (Doors) {channel="omnilink:zone:home:MBRDeckDoor:contact"}
Contact PorchDoor "Porch Door" <door> (Doors) {channel="omnilink:zone:home:PorchDoor:contact"}
Group:Contact:OR(OPEN, CLOSED) GarageDoors "All Garage Doors [%s]"
Contact TwoCarGarageDo "Two Car Garage Door" <garagedoor> (GarageDoors) {channel="omnilink:zone:home:TwoCarGarageDo:contact"}
Contact OneCarGarageDo "One Car Garage Door" <garagedoor> (GarageDoors) {channel="omnilink:zone:home:OneCarGarageDo:contact"}
Group:Contact:OR(OPEN, CLOSED) Motion "All Motion Sensors [%s]"
Contact MBRMotion "Motion" <presence> (Motion) {homekit="MotionSensor", channel="omnilink:zone:home:MBRMotion:contact"}
Contact UpOffMotion "Motion" <presence> (Motion) {homekit="MotionSensor", channel="omnilink:zone:home:UpOffMotion:contact"}
Contact UpLivMotion "Motion" <presence> (Motion) {homekit="MotionSensor", channel="omnilink:zone:home:UpLivMotion:contact"}
Contact BsmtWORMotion "Motion" <presence> (Motion) {homekit="MotionSensor", channel="omnilink:zone:home:BsmtWORMotion:contact"}
Contact GarageMotion "Motion" <presence> (Motion) {homekit="MotionSensor", channel="omnilink:zone:home:GarageMotion:contact"}
/*
* Console
*/
String UpstrsConsole_Beeper "Enable/Disable Beeper [%s]" {channel="omnilink:console:home:UpstrsConsole:enable_disable_beeper"}
Number UpstrsConsole_Beep "Beep Console" {channel="omnilink:console:home:UpstrsConsole:beep"}
/*
* Button
*/
Switch MainButton "Toggle button [%s]" <switch> {channel="omnilink:button:home:MainButton:press"}
/*
* Other OmniPro items
*/
DateTime OmniProTime "Last Time Update [%1$ta %1$tR]" <time> {channel="omnilink:controller:home:sysdate"}
```
### Example `therm-status.map`
```
0=Idle
1=Heating
2=Cooling
```
### Example `therm-tempmode.map`
```
0=Off
1=Heat
2=Cool
3=Auto
5=Emergency heat
```
### Example `therm-fanmode.map`
```
0=Auto
1=On
2=Cycle
```
### Example `therm-holdmode.map`
```
0=Off
1=Hold
2=Vacation hold
```
### Example `area-modes.map`
```
0=Off
1=Day
2=Night
3=Away
4=Vacation
5=Day instant
6=Night delayed
9=Arming day
10=Arming night
11=Arming away
12=Arming vacation
13=Arming day instant
14=Arming night delay
=Unknown
```
### Example `omnilink.rules`
```
rule "Update OmniPro Time"
when
Time cron "0 0 0/1 1/1 * ? *"
then
OmniProTime.sendCommand( new DateTimeType() )
end
```

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.omnilink</artifactId>
<name>openHAB Add-ons :: Bundles :: OmniLink Binding</name>
<dependencies>
<dependency>
<groupId>com.github.digitaldan</groupId>
<artifactId>jomnilink</artifactId>
<version>1.4.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2010-2021 Contributors to the openHAB project
See the NOTICE file(s) distributed with this work for additional
information.
This program and the accompanying materials are made available under the
terms of the Eclipse Public License 2.0 which is available at
http://www.eclipse.org/legal/epl-2.0
SPDX-License-Identifier: EPL-2.0
-->
<features name="org.openhab.binding.omnilink-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-omnilink" description="OmniLink Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.omnilink/${project.version}</bundle>
</feature>
</features>

View File

@ -0,0 +1,57 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.math.BigInteger;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link AreaAlarm} class defines the different types of alarms supported
* by the OmniLink Protocol.
*
* @author Craig Hamilton - Initial contribution
*/
@NonNullByDefault
public enum AreaAlarm {
BURGLARY(CHANNEL_AREA_ALARM_BURGLARY, 0),
FIRE(CHANNEL_AREA_ALARM_FIRE, 1),
GAS(CHANNEL_AREA_ALARM_GAS, 2),
AUXILIARY(CHANNEL_AREA_ALARM_AUXILIARY, 3),
FREEZE(CHANNEL_AREA_ALARM_FREEZE, 4),
WATER(CHANNEL_AREA_ALARM_WATER, 5),
DURESS(CHANNEL_AREA_ALARM_DURESS, 6),
TEMPERATURE(CHANNEL_AREA_ALARM_TEMPERATURE, 7);
private final String channelUID;
private final int bit;
AreaAlarm(String channelUID, int bit) {
this.channelUID = channelUID;
this.bit = bit;
}
public boolean isSet(BigInteger alarmBits) {
return alarmBits.testBit(bit);
}
public boolean isSet(int alarmBits) {
return isSet(BigInteger.valueOf(alarmBits));
}
public String getChannelUID() {
return channelUID;
}
}

View File

@ -0,0 +1,76 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal;
import java.util.Arrays;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link AudioPlayer} defines some methods that are used to
* interface with an OmniLink Audio Player.
*
* @author Brian O'Connell - Initial contribution
*/
@NonNullByDefault
public enum AudioPlayer {
NUVO(1, 6, 8, 7, 9, 10),
NUVO_GRAND_ESSENTIA_SIMPLESE(2, 6, 8, 7, 9, 10),
NUVO_GRAND_GRAND_CONCERTO(3, 6, 6, 6, 9, 10),
RUSSOUND(4, 6, 8, 7, 11, 12),
XANTECH(6, 13, 15, 14, 16, 17),
SPEAKERCRAFT(7, 45, 44, 46, 42, 43),
PROFICIENT(8, 45, 44, 46, 42, 43);
private final int featureCode;
private final int playCommand;
private final int pauseCommand;
private final int stopCommand;
private final int previousCommand;
private final int nextCommand;
AudioPlayer(int featureCode, int playCommand, int pauseCommand, int stopCommand, int previousCommand,
int nextCommand) {
this.featureCode = featureCode;
this.playCommand = playCommand;
this.pauseCommand = pauseCommand;
this.stopCommand = stopCommand;
this.previousCommand = previousCommand;
this.nextCommand = nextCommand;
}
public int getPlayCommand() {
return playCommand;
}
public int getPauseCommand() {
return pauseCommand;
}
public int getStopCommand() {
return stopCommand;
}
public int getPreviousCommand() {
return previousCommand;
}
public int getNextCommand() {
return nextCommand;
}
public static Optional<AudioPlayer> getAudioPlayerForFeatureCode(int featureCode) {
return Arrays.stream(values()).filter(v -> v.featureCode == featureCode).findAny();
}
}

View File

@ -0,0 +1,188 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link OmnilinkBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class OmnilinkBindingConstants {
public static final String BINDING_ID = "omnilink";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "controller");
public static final ThingTypeUID THING_TYPE_OMNI_AREA = new ThingTypeUID(BINDING_ID, "area");
public static final ThingTypeUID THING_TYPE_LUMINA_AREA = new ThingTypeUID(BINDING_ID, "lumina_area");
public static final ThingTypeUID THING_TYPE_ZONE = new ThingTypeUID(BINDING_ID, "zone");
public static final ThingTypeUID THING_TYPE_LOCK = new ThingTypeUID(BINDING_ID, "lock");
public static final ThingTypeUID THING_TYPE_UNIT_UPB = new ThingTypeUID(BINDING_ID, "upb");
public static final ThingTypeUID THING_TYPE_UNIT = new ThingTypeUID(BINDING_ID, "unit");
public static final ThingTypeUID THING_TYPE_DIMMABLE = new ThingTypeUID(BINDING_ID, "dimmable");
public static final ThingTypeUID THING_TYPE_FLAG = new ThingTypeUID(BINDING_ID, "flag");
public static final ThingTypeUID THING_TYPE_OUTPUT = new ThingTypeUID(BINDING_ID, "output");
public static final ThingTypeUID THING_TYPE_ROOM = new ThingTypeUID(BINDING_ID, "room");
public static final ThingTypeUID THING_TYPE_BUTTON = new ThingTypeUID(BINDING_ID, "button");
public static final ThingTypeUID THING_TYPE_THERMOSTAT = new ThingTypeUID(BINDING_ID, "thermostat");
public static final ThingTypeUID THING_TYPE_AUDIO_ZONE = new ThingTypeUID(BINDING_ID, "audio_zone");
public static final ThingTypeUID THING_TYPE_AUDIO_SOURCE = new ThingTypeUID(BINDING_ID, "audio_source");
public static final ThingTypeUID THING_TYPE_CONSOLE = new ThingTypeUID(BINDING_ID, "console");
public static final ThingTypeUID THING_TYPE_TEMP_SENSOR = new ThingTypeUID(BINDING_ID, "temp_sensor");
public static final ThingTypeUID THING_TYPE_HUMIDITY_SENSOR = new ThingTypeUID(BINDING_ID, "humidity_sensor");
// List of all Channel ids
// zones
public static final String CHANNEL_ZONE_CONTACT = "contact";
public static final String CHANNEL_ZONE_CURRENT_CONDITION = "current_condition";
public static final String CHANNEL_ZONE_LATCHED_ALARM_STATUS = "latched_alarm_status";
public static final String CHANNEL_ZONE_ARMING_STATUS = "arming_status";
public static final String CHANNEL_ZONE_BYPASS = "bypass";
public static final String CHANNEL_ZONE_RESTORE = "restore";
// areas
public static final String CHANNEL_AREA_MODE = "mode";
public static final String CHANNEL_AREA_ACTIVATE_KEYPAD_EMERGENCY = "activate_keypad_emergency";
public static final String CHANNEL_AREA_ALARM_BURGLARY = "alarm_burglary";
public static final String CHANNEL_AREA_ALARM_FIRE = "alarm_fire";
public static final String CHANNEL_AREA_ALARM_GAS = "alarm_gas";
public static final String CHANNEL_AREA_ALARM_AUXILIARY = "alarm_auxiliary";
public static final String CHANNEL_AREA_ALARM_FREEZE = "alarm_freeze";
public static final String CHANNEL_AREA_ALARM_WATER = "alarm_water";
public static final String CHANNEL_AREA_ALARM_DURESS = "alarm_duress";
public static final String CHANNEL_AREA_ALARM_TEMPERATURE = "alarm_temperature";
public static final String CHANNEL_AREA_SECURITY_MODE_DISARM = "disarm";
public static final String CHANNEL_AREA_SECURITY_MODE_DAY = "day";
public static final String CHANNEL_AREA_SECURITY_MODE_NIGHT = "night";
public static final String CHANNEL_AREA_SECURITY_MODE_AWAY = "away";
public static final String CHANNEL_AREA_SECURITY_MODE_VACATION = "vacation";
public static final String CHANNEL_AREA_SECURITY_MODE_DAY_INSTANT = "day_instant";
public static final String CHANNEL_AREA_SECURITY_MODE_NIGHT_DELAYED = "night_delayed";
public static final String CHANNEL_AREA_SECURITY_MODE_HOME = "home";
public static final String CHANNEL_AREA_SECURITY_MODE_SLEEP = "sleep";
public static final String CHANNEL_AREA_SECURITY_MODE_PARTY = "party";
public static final String CHANNEL_AREA_SECURITY_MODE_SPECIAL = "special";
// units
public static final String CHANNEL_UNIT_LEVEL = "level";
public static final String CHANNEL_UNIT_SWITCH = "switch";
public static final String CHANNEL_UNIT_ON_FOR_SECONDS = "on_for_seconds";
public static final String CHANNEL_UNIT_ON_FOR_MINUTES = "on_for_minutes";
public static final String CHANNEL_UNIT_ON_FOR_HOURS = "on_for_hours";
public static final String CHANNEL_UNIT_OFF_FOR_SECONDS = "off_for_seconds";
public static final String CHANNEL_UNIT_OFF_FOR_MINUTES = "off_for_minutes";
public static final String CHANNEL_UNIT_OFF_FOR_HOURS = "off_for_hours";
public static final String CHANNEL_FLAG_VALUE = "value";
public static final String CHANNEL_FLAG_SWITCH = "switch";
public static final String CHANNEL_UPB_STATUS = "upb_status";
public static final String CHANNEL_ROOM_SWITCH = "switch";
public static final String CHANNEL_ROOM_SCENE_A = "scene_a";
public static final String CHANNEL_ROOM_SCENE_B = "scene_b";
public static final String CHANNEL_ROOM_SCENE_C = "scene_c";
public static final String CHANNEL_ROOM_SCENE_D = "scene_d";
public static final String CHANNEL_ROOM_STATE = "state";
public static final String CHANNEL_SYSTEMDATE = "sysdate";
public static final String CHANNEL_EVENT_LOG = "last_log";
// buttons
public static final String CHANNEL_BUTTON_PRESS = "press";
// locks
public static final String CHANNEL_LOCK_SWITCH = "switch";
// thermostats
public static final String CHANNEL_THERMO_FREEZE_ALARM = "freeze_alarm";
public static final String CHANNEL_THERMO_COMM_FAILURE = "comm_failure";
public static final String CHANNEL_THERMO_STATUS = "status";
public static final String CHANNEL_THERMO_CURRENT_TEMP = "temperature";
public static final String CHANNEL_THERMO_OUTDOOR_TEMP = "outdoor_temperature";
public static final String CHANNEL_THERMO_HUMIDITY = "humidity";
public static final String CHANNEL_THERMO_HUMIDIFY_SETPOINT = "humidify_setpoint";
public static final String CHANNEL_THERMO_DEHUMIDIFY_SETPOINT = "dehumidify_setpoint";
public static final String CHANNEL_THERMO_SYSTEM_MODE = "system_mode";
public static final String CHANNEL_THERMO_FAN_MODE = "fan_mode";
public static final String CHANNEL_THERMO_HOLD_STATUS = "hold_status";
public static final String CHANNEL_THERMO_COOL_SETPOINT = "cool_setpoint";
public static final String CHANNEL_THERMO_HEAT_SETPOINT = "heat_setpoint";
// temp / humidity sensors
public static final String CHANNEL_AUX_TEMP = "temperature";
public static final String CHANNEL_AUX_HUMIDITY = "humidity";
public static final String CHANNEL_AUX_LOW_SETPOINT = "low_setpoint";
public static final String CHANNEL_AUX_HIGH_SETPOINT = "high_setpoint";
// consoles
public static final String CHANNEL_CONSOLE_BEEP = "beep";
public static final String CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER = "enable_disable_beeper";
// audio zones
public static final String CHANNEL_AUDIO_ZONE_POWER = "zone_power";
public static final String CHANNEL_AUDIO_ZONE_MUTE = "zone_mute";
public static final String CHANNEL_AUDIO_ZONE_VOLUME = "zone_volume";
public static final String CHANNEL_AUDIO_ZONE_SOURCE = "zone_source";
public static final String CHANNEL_AUDIO_ZONE_CONTROL = "zone_control";
// audio sources
public static final String CHANNEL_AUDIO_SOURCE_TEXT1 = "source_text_1";
public static final String CHANNEL_AUDIO_SOURCE_TEXT2 = "source_text_2";
public static final String CHANNEL_AUDIO_SOURCE_TEXT3 = "source_text_3";
public static final String CHANNEL_AUDIO_SOURCE_TEXT4 = "source_text_4";
public static final String CHANNEL_AUDIO_SOURCE_TEXT5 = "source_text_5";
public static final String CHANNEL_AUDIO_SOURCE_TEXT6 = "source_text_6";
public static final String CHANNEL_AUDIO_SOURCE_POLLING = "polling";
// trigger channels
public static final String TRIGGER_CHANNEL_BUTTON_ACTIVATED_EVENT = "activated_event";
public static final String TRIGGER_CHANNEL_PHONE_LINE_EVENT = "phone_line_event";
public static final String TRIGGER_CHANNEL_AC_POWER_EVENT = "ac_power_event";
public static final String TRIGGER_CHANNEL_BATTERY_EVENT = "battery_event";
public static final String TRIGGER_CHANNEL_DCM_EVENT = "dcm_event";
public static final String TRIGGER_CHANNEL_ENERGY_COST_EVENT = "energy_cost_event";
public static final String TRIGGER_CHANNEL_CAMERA_TRIGGER_EVENT = "camera_trigger_event";
public static final String TRIGGER_CHANNEL_ACCESS_CONTROL_READER_EVENT = "access_control_reader_event";
public static final String TRIGGER_CHANNEL_AREA_ALL_ON_OFF_EVENT = "all_on_off_Event";
public static final String TRIGGER_CHANNEL_ZONE_STATE_EVENT = "zone_state_Event";
public static final String TRIGGER_CHANNEL_SWITCH_PRESS_EVENT = "switch_press_event";
public static final String TRIGGER_CHANNEL_UPB_LINK_ACTIVATED_EVENT = "upb_link_activated_event";
public static final String TRIGGER_CHANNEL_UPB_LINK_DEACTIVATED_EVENT = "upb_link_deactivated_event";
// thing configuration and properties keys
public static final String THING_PROPERTIES_NAME = "name";
public static final String THING_PROPERTIES_NUMBER = "number";
public static final String THING_PROPERTIES_AREA = "area";
public static final String THING_PROPERTIES_AUTO_START = "autostart";
public static final String THING_PROPERTIES_MODEL_NUMBER = "modelNumber";
public static final String THING_PROPERTIES_MAJOR_VERSION = "majorVersion";
public static final String THING_PROPERTIES_MINOR_VERSION = "minorVersion";
public static final String THING_PROPERTIES_REVISION = "revision";
public static final String THING_PROPERTIES_PHONE_NUMBER = "phoneNumber";
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_OMNI_AREA,
THING_TYPE_LUMINA_AREA, THING_TYPE_ZONE, THING_TYPE_BRIDGE, THING_TYPE_FLAG, THING_TYPE_ROOM,
THING_TYPE_BUTTON, THING_TYPE_UNIT_UPB, THING_TYPE_THERMOSTAT, THING_TYPE_CONSOLE, THING_TYPE_AUDIO_ZONE,
THING_TYPE_AUDIO_SOURCE, THING_TYPE_TEMP_SENSOR, THING_TYPE_HUMIDITY_SENSOR, THING_TYPE_LOCK,
THING_TYPE_OUTPUT, THING_TYPE_UNIT, THING_TYPE_DIMMABLE);
}

View File

@ -0,0 +1,105 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.handler.AudioSourceHandler;
import org.openhab.binding.omnilink.internal.handler.AudioZoneHandler;
import org.openhab.binding.omnilink.internal.handler.ButtonHandler;
import org.openhab.binding.omnilink.internal.handler.ConsoleHandler;
import org.openhab.binding.omnilink.internal.handler.HumiditySensorHandler;
import org.openhab.binding.omnilink.internal.handler.LockHandler;
import org.openhab.binding.omnilink.internal.handler.LuminaAreaHandler;
import org.openhab.binding.omnilink.internal.handler.OmniAreaHandler;
import org.openhab.binding.omnilink.internal.handler.OmnilinkBridgeHandler;
import org.openhab.binding.omnilink.internal.handler.TempSensorHandler;
import org.openhab.binding.omnilink.internal.handler.ThermostatHandler;
import org.openhab.binding.omnilink.internal.handler.UnitHandler;
import org.openhab.binding.omnilink.internal.handler.ZoneHandler;
import org.openhab.binding.omnilink.internal.handler.units.DimmableUnitHandler;
import org.openhab.binding.omnilink.internal.handler.units.FlagHandler;
import org.openhab.binding.omnilink.internal.handler.units.OutputHandler;
import org.openhab.binding.omnilink.internal.handler.units.UpbRoomHandler;
import org.openhab.binding.omnilink.internal.handler.units.dimmable.UpbUnitHandler;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Component;
/**
* The {@link OmnilinkHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.omnilink")
public class OmnilinkHandlerFactory extends BaseThingHandlerFactory {
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(THING_TYPE_AUDIO_SOURCE)) {
return new AudioSourceHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_AUDIO_ZONE)) {
return new AudioZoneHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_BRIDGE)) {
return new OmnilinkBridgeHandler((Bridge) thing);
} else if (thingTypeUID.equals(THING_TYPE_BUTTON)) {
return new ButtonHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_CONSOLE)) {
return new ConsoleHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_DIMMABLE)) {
return new DimmableUnitHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_FLAG)) {
return new FlagHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_HUMIDITY_SENSOR)) {
return new HumiditySensorHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_LOCK)) {
return new LockHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_LUMINA_AREA)) {
return new LuminaAreaHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_OMNI_AREA)) {
return new OmniAreaHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_OUTPUT)) {
return new OutputHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_ROOM)) {
return new UpbRoomHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_TEMP_SENSOR)) {
return new TempSensorHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_THERMOSTAT)) {
return new ThermostatHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_UNIT)) {
return new UnitHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_UNIT_UPB)) {
return new UpbUnitHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_ZONE)) {
return new ZoneHandler(thing);
} else {
return null;
}
}
}

View File

@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal;
import java.util.Arrays;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link SystemType} enum defines the two supported system types which can
* interface with the binding
*
* @author Craig Hamilton - Initial contribution
*/
@NonNullByDefault
public enum SystemType {
OMNI(16, 30, 38),
LUMINA(36, 37);
private final Set<Integer> modelNumbers;
SystemType(Integer... modelNumbers) {
this.modelNumbers = Set.of(modelNumbers);
}
public static SystemType getType(int modelNumber) {
return Arrays.stream(values()).filter(s -> s.modelNumbers.contains(modelNumber)).findFirst()
.orElseThrow(IllegalArgumentException::new);
}
}

View File

@ -0,0 +1,73 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link OmnilinkBridgeConfig} sets the authentication settings of the
* OmniLink Controller that will allow for proper communication.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class OmnilinkBridgeConfig {
private @Nullable String key1;
private @Nullable String key2;
private @Nullable String ipAddress;
private int port;
private int logPollingInterval;
public int getLogPollingInterval() {
return logPollingInterval;
}
public void setLogPollingInterval(int logPollingInterval) {
this.logPollingInterval = logPollingInterval;
}
public @Nullable String getKey1() {
return key1;
}
public void setKey1(String key1) {
this.key1 = key1;
}
public @Nullable String getKey2() {
return key2;
}
public void setKey2(String key2) {
this.key2 = key2;
}
public @Nullable String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}

View File

@ -0,0 +1,129 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.discovery;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.omnilink.internal.handler.BridgeOfflineException;
import org.openhab.binding.omnilink.internal.handler.OmnilinkBridgeHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.Message;
import com.digitaldan.jomnilinkII.MessageTypes.ObjectProperties;
import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
/**
* @author Craig Hamilton - Initial contribution
*
* @param <T>
*/
@NonNullByDefault
public class ObjectPropertyRequest<T extends ObjectProperties> implements Iterable<T> {
private final Logger logger = LoggerFactory.getLogger(ObjectPropertyRequest.class);
public static <T extends ObjectProperties, U extends ObjectPropertyRequests<T>> Builder<T> builder(
OmnilinkBridgeHandler bridgeHandler, U request, int objectNumber, int offset) {
return new Builder<>(bridgeHandler, request, objectNumber, offset);
}
private final OmnilinkBridgeHandler bridgeHandler;
private final ObjectPropertyRequests<T> request;
private final int objectNumber;
private final int filter1;
private final int filter2;
private final int filter3;
private final int offset;
private ObjectPropertyRequest(OmnilinkBridgeHandler bridgeHandler, ObjectPropertyRequests<T> request,
int objectNumber, int filter1, int filter2, int filter3, int offset) {
this.bridgeHandler = bridgeHandler;
this.request = request;
this.objectNumber = objectNumber;
this.filter1 = filter1;
this.filter2 = filter2;
this.filter3 = filter3;
this.offset = offset;
}
@Override
public Iterator<T> iterator() {
List<T> messages = new ArrayList<T>();
int currentObjectNumber = objectNumber;
while (true) {
try {
Message message = bridgeHandler.reqObjectProperties(request.getPropertyRequest(), currentObjectNumber,
offset, filter1, filter2, filter3);
if (message.getMessageType() == Message.MESG_TYPE_OBJ_PROP) {
ObjectProperties objectProperties = (ObjectProperties) message;
messages.add(request.getResponseType().cast(objectProperties));
if (offset == 0) {
break;
} else if (offset == 1) {
currentObjectNumber++;
} else if (offset == -1) {
currentObjectNumber--;
}
} else {
break;
}
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.warn("Error retrieving object properties: {}", e.getMessage());
}
}
return messages.iterator();
}
public static class Builder<T extends ObjectProperties> {
private final OmnilinkBridgeHandler bridgeHandler;
private final ObjectPropertyRequests<T> request;
private final int objectNumber;
private final int offset;
private int filter1 = ObjectProperties.FILTER_1_NONE;
private int filter2 = ObjectProperties.FILTER_2_NONE;
private int filter3 = ObjectProperties.FILTER_3_NONE;
private Builder(OmnilinkBridgeHandler bridgeHandler, ObjectPropertyRequests<T> request, int objectNumber,
int offset) {
this.bridgeHandler = bridgeHandler;
this.request = request;
this.objectNumber = objectNumber;
this.offset = offset;
}
public Builder<T> selectNamed() {
this.filter1 = ObjectProperties.FILTER_1_NAMED;
return this;
}
public Builder<T> areaFilter(int area) {
this.filter2 = area;
return this;
}
public Builder<T> selectAnyLoad() {
this.filter3 = ObjectProperties.FILTER_3_ANY_LOAD;
return this;
}
public ObjectPropertyRequest<T> build() {
return new ObjectPropertyRequest<T>(bridgeHandler, request, objectNumber, filter1, filter2, filter3,
offset);
}
}
}

View File

@ -0,0 +1,79 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.discovery;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.digitaldan.jomnilinkII.Message;
import com.digitaldan.jomnilinkII.MessageTypes.ObjectProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AccessControlReaderProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AudioSourceProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AudioZoneProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AuxSensorProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.ButtonProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.ThermostatProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.UnitProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.ZoneProperties;
/**
* @author Craig Hamilton - Initial contribution
*
* @param <T>
*/
@NonNullByDefault
public class ObjectPropertyRequests<T extends ObjectProperties> {
public static final ObjectPropertyRequests<ThermostatProperties> THERMOSTAT = new ObjectPropertyRequests<>(
Message.OBJ_TYPE_THERMO, ThermostatProperties.class);
public static final ObjectPropertyRequests<ButtonProperties> BUTTONS = new ObjectPropertyRequests<>(
Message.OBJ_TYPE_BUTTON, ButtonProperties.class);
public static final ObjectPropertyRequests<AreaProperties> AREA = new ObjectPropertyRequests<>(
Message.OBJ_TYPE_AREA, AreaProperties.class);
public static final ObjectPropertyRequests<ZoneProperties> ZONE = new ObjectPropertyRequests<>(
Message.OBJ_TYPE_ZONE, ZoneProperties.class);
public static final ObjectPropertyRequests<UnitProperties> UNIT = new ObjectPropertyRequests<>(
Message.OBJ_TYPE_UNIT, UnitProperties.class);
public static final ObjectPropertyRequests<AudioZoneProperties> AUDIO_ZONE = new ObjectPropertyRequests<>(
Message.OBJ_TYPE_AUDIO_ZONE, AudioZoneProperties.class);
public static final ObjectPropertyRequests<AudioSourceProperties> AUDIO_SOURCE = new ObjectPropertyRequests<>(
Message.OBJ_TYPE_AUDIO_SOURCE, AudioSourceProperties.class);
public static final ObjectPropertyRequests<AuxSensorProperties> AUX_SENSORS = new ObjectPropertyRequests<>(
Message.OBJ_TYPE_AUX_SENSOR, AuxSensorProperties.class);
public static final ObjectPropertyRequests<AccessControlReaderProperties> LOCK = new ObjectPropertyRequests<>(
Message.OBJ_TYPE_CONTROL_READER, AccessControlReaderProperties.class);
private final int propertyRequest;
private final Class<T> responseType;
private ObjectPropertyRequests(int propertyRequest, Class<T> type) {
this.propertyRequest = propertyRequest;
this.responseType = type;
}
public int getPropertyRequest() {
return propertyRequest;
}
public Class<T> getResponseType() {
return responseType;
}
}

View File

@ -0,0 +1,542 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.discovery;
import static com.digitaldan.jomnilinkII.MessageTypes.properties.AuxSensorProperties.*;
import static com.digitaldan.jomnilinkII.MessageTypes.properties.UnitProperties.*;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.SystemType;
import org.openhab.binding.omnilink.internal.handler.BridgeOfflineException;
import org.openhab.binding.omnilink.internal.handler.OmnilinkBridgeHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.MessageTypes.SystemInformation;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AccessControlReaderProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AudioSourceProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AudioZoneProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AuxSensorProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.ButtonProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.ThermostatProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.UnitProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.ZoneProperties;
import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
/**
* The {@link OmnilinkDiscoveryService} creates things based on the configured bridge.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class OmnilinkDiscoveryService extends AbstractDiscoveryService
implements DiscoveryService, ThingHandlerService {
private final Logger logger = LoggerFactory.getLogger(OmnilinkDiscoveryService.class);
private static final int DISCOVER_TIMEOUT_SECONDS = 30;
private @Nullable OmnilinkBridgeHandler bridgeHandler;
private @Nullable SystemType systemType;
private @Nullable List<AreaProperties> areas;
/**
* Creates an OmnilinkDiscoveryService.
*/
public OmnilinkDiscoveryService() {
super(SUPPORTED_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS, false);
}
@Override
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof OmnilinkBridgeHandler) {
bridgeHandler = (OmnilinkBridgeHandler) handler;
}
}
@Override
public @Nullable ThingHandler getThingHandler() {
return bridgeHandler;
}
@Override
public void activate() {
}
@Override
public void deactivate() {
}
@Override
protected synchronized void startScan() {
final OmnilinkBridgeHandler handler = bridgeHandler;
if (handler != null) {
logger.debug("Starting scan");
try {
SystemInformation systemInformation = handler.reqSystemInformation();
this.systemType = SystemType.getType(systemInformation.getModel());
this.areas = discoverAreas();
discoverUnits();
discoverZones();
discoverButtons();
discoverThermostats();
discoverAudioZones();
discoverAudioSources();
discoverTempSensors();
discoverHumiditySensors();
discoverLocks();
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Received error during discovery: {}", e.getMessage());
}
}
}
@Override
protected synchronized void stopScan() {
super.stopScan();
removeOlderResults(getTimestampOfLastScan());
}
/**
* Calculate the area filter the a supplied area
*
* @param area Area to calculate filter for.
* @return Calculated Bit Filter for the supplied area. Bit 0 is area 1, bit 2 is area 2 and so on.
*/
private static int bitFilterForArea(AreaProperties areaProperties) {
return BigInteger.ZERO.setBit(areaProperties.getNumber() - 1).intValue();
}
/**
* Discovers OmniLink buttons
*/
private void discoverButtons() {
final OmnilinkBridgeHandler handler = bridgeHandler;
if (handler != null) {
final ThingUID bridgeUID = handler.getThing().getUID();
final List<AreaProperties> areas = this.areas;
if (areas != null) {
for (AreaProperties areaProperties : areas) {
int areaFilter = bitFilterForArea(areaProperties);
ObjectPropertyRequest<ButtonProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(handler, ObjectPropertyRequests.BUTTONS, 0, 1).selectNamed().areaFilter(areaFilter)
.build();
for (ButtonProperties buttonProperties : objectPropertyRequest) {
String thingName = buttonProperties.getName();
String thingID = Integer.toString(buttonProperties.getNumber());
Map<String, Object> properties = new HashMap<>();
properties.put(THING_PROPERTIES_NAME, thingName);
properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
ThingUID thingUID = new ThingUID(THING_TYPE_BUTTON, bridgeUID, thingID);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
.withProperties(properties).withProperty(THING_PROPERTIES_NUMBER, thingID)
.withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID)
.withLabel(thingName).build();
thingDiscovered(discoveryResult);
}
}
}
}
}
/**
* Discovers OmniLink locks
*/
private void discoverLocks() {
final OmnilinkBridgeHandler handler = bridgeHandler;
if (handler != null) {
final ThingUID bridgeUID = handler.getThing().getUID();
ObjectPropertyRequest<AccessControlReaderProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(handler, ObjectPropertyRequests.LOCK, 0, 1).selectNamed().build();
for (AccessControlReaderProperties lockProperties : objectPropertyRequest) {
String thingName = lockProperties.getName();
String thingID = Integer.toString(lockProperties.getNumber());
Map<String, Object> properties = Map.of(THING_PROPERTIES_NAME, thingName);
ThingUID thingUID = new ThingUID(THING_TYPE_LOCK, bridgeUID, thingID);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withProperty(THING_PROPERTIES_NUMBER, thingID)
.withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID).withLabel(thingName)
.build();
thingDiscovered(discoveryResult);
}
}
}
/**
* Discovers OmniLink audio zones
*/
private void discoverAudioZones() {
final OmnilinkBridgeHandler handler = bridgeHandler;
if (handler != null) {
final ThingUID bridgeUID = handler.getThing().getUID();
ObjectPropertyRequest<AudioZoneProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(handler, ObjectPropertyRequests.AUDIO_ZONE, 0, 1).selectNamed().build();
for (AudioZoneProperties audioZoneProperties : objectPropertyRequest) {
String thingName = audioZoneProperties.getName();
String thingID = Integer.toString(audioZoneProperties.getNumber());
Map<String, Object> properties = Map.of(THING_PROPERTIES_NAME, thingName);
ThingUID thingUID = new ThingUID(THING_TYPE_AUDIO_ZONE, bridgeUID, thingID);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withProperty(THING_PROPERTIES_NUMBER, thingID)
.withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID).withLabel(thingName)
.build();
thingDiscovered(discoveryResult);
}
}
}
/**
* Discovers OmniLink audio sources
*/
private void discoverAudioSources() {
final OmnilinkBridgeHandler handler = bridgeHandler;
if (handler != null) {
final ThingUID bridgeUID = handler.getThing().getUID();
ObjectPropertyRequest<AudioSourceProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(handler, ObjectPropertyRequests.AUDIO_SOURCE, 0, 1).selectNamed().build();
for (AudioSourceProperties audioSourceProperties : objectPropertyRequest) {
String thingName = audioSourceProperties.getName();
String thingID = Integer.toString(audioSourceProperties.getNumber());
Map<String, Object> properties = Map.of(THING_PROPERTIES_NAME, thingName);
ThingUID thingUID = new ThingUID(THING_TYPE_AUDIO_SOURCE, bridgeUID, thingID);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withProperty(THING_PROPERTIES_NUMBER, thingID)
.withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID).withLabel(thingName)
.build();
thingDiscovered(discoveryResult);
}
}
}
/**
* Discovers OmniLink temperature sensors
*/
private void discoverTempSensors() {
final OmnilinkBridgeHandler handler = bridgeHandler;
if (handler != null) {
final ThingUID bridgeUID = handler.getThing().getUID();
final List<AreaProperties> areas = this.areas;
if (areas != null) {
for (AreaProperties areaProperties : areas) {
int areaFilter = bitFilterForArea(areaProperties);
ObjectPropertyRequest<AuxSensorProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(handler, ObjectPropertyRequests.AUX_SENSORS, 0, 1).selectNamed()
.areaFilter(areaFilter).build();
for (AuxSensorProperties auxSensorProperties : objectPropertyRequest) {
if (auxSensorProperties.getSensorType() != SENSOR_TYPE_PROGRAMMABLE_ENERGY_SAVER_MODULE
&& auxSensorProperties.getSensorType() != SENSOR_TYPE_HUMIDITY) {
String thingName = auxSensorProperties.getName();
String thingID = Integer.toString(auxSensorProperties.getNumber());
Map<String, Object> properties = new HashMap<>();
properties.put(THING_PROPERTIES_NAME, thingName);
properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
ThingUID thingUID = new ThingUID(THING_TYPE_TEMP_SENSOR, bridgeUID, thingID);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
.withProperties(properties).withProperty(THING_PROPERTIES_NUMBER, thingID)
.withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID)
.withLabel(thingName).build();
thingDiscovered(discoveryResult);
}
}
}
}
}
}
/**
* Discovers OmniLink humidity sensors
*/
private void discoverHumiditySensors() {
final OmnilinkBridgeHandler handler = bridgeHandler;
if (handler != null) {
final ThingUID bridgeUID = handler.getThing().getUID();
final List<AreaProperties> areas = this.areas;
if (areas != null) {
for (AreaProperties areaProperties : areas) {
int areaFilter = bitFilterForArea(areaProperties);
ObjectPropertyRequest<AuxSensorProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(handler, ObjectPropertyRequests.AUX_SENSORS, 0, 1).selectNamed()
.areaFilter(areaFilter).build();
for (AuxSensorProperties auxSensorProperties : objectPropertyRequest) {
if (auxSensorProperties.getSensorType() == SENSOR_TYPE_HUMIDITY) {
String thingName = auxSensorProperties.getName();
String thingID = Integer.toString(auxSensorProperties.getNumber());
Map<String, Object> properties = new HashMap<>();
properties.put(THING_PROPERTIES_NAME, thingName);
properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
ThingUID thingUID = new ThingUID(THING_TYPE_HUMIDITY_SENSOR, bridgeUID, thingID);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
.withProperties(properties).withProperty(THING_PROPERTIES_NUMBER, thingID)
.withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID)
.withLabel(thingName).build();
thingDiscovered(discoveryResult);
}
}
}
}
}
}
/**
* Discovers OmniLink thermostats
*/
private void discoverThermostats() {
final OmnilinkBridgeHandler handler = bridgeHandler;
if (handler != null) {
final ThingUID bridgeUID = handler.getThing().getUID();
final List<AreaProperties> areas = this.areas;
if (areas != null) {
for (AreaProperties areaProperties : areas) {
int areaFilter = bitFilterForArea(areaProperties);
ObjectPropertyRequest<ThermostatProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(handler, ObjectPropertyRequests.THERMOSTAT, 0, 1).selectNamed()
.areaFilter(areaFilter).build();
for (ThermostatProperties thermostatProperties : objectPropertyRequest) {
String thingName = thermostatProperties.getName();
String thingID = Integer.toString(thermostatProperties.getNumber());
ThingUID thingUID = new ThingUID(THING_TYPE_THERMOSTAT, bridgeUID, thingID);
Map<String, Object> properties = new HashMap<>();
properties.put(THING_PROPERTIES_NAME, thingName);
properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
.withProperties(properties).withProperty(THING_PROPERTIES_NUMBER, thingID)
.withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID)
.withLabel(thingName).build();
thingDiscovered(discoveryResult);
}
}
}
}
}
/**
* Discovers OmniLink areas
*/
private @Nullable List<AreaProperties> discoverAreas() {
final OmnilinkBridgeHandler handler = bridgeHandler;
if (handler != null) {
final ThingUID bridgeUID = handler.getThing().getUID();
List<AreaProperties> areas = new LinkedList<>();
ObjectPropertyRequest<AreaProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(handler, ObjectPropertyRequests.AREA, 0, 1).build();
for (AreaProperties areaProperties : objectPropertyRequest) {
int thingNumber = areaProperties.getNumber();
String thingName = areaProperties.getName();
String thingID = Integer.toString(thingNumber);
ThingUID thingUID = null;
/*
* It seems that for simple OmniLink Controller configurations there
* is only 1 area, without a name. So if there is no name for the
* first area, we will call that Main Area. If other area's name is
* blank, we will not create a thing.
*/
if (thingNumber == 1 && "".equals(thingName)) {
thingName = "Main Area";
} else if ("".equals(thingName)) {
break;
}
Map<String, Object> properties = Map.of(THING_PROPERTIES_NAME, thingName);
final SystemType systemType = this.systemType;
if (systemType != null) {
switch (systemType) {
case LUMINA:
thingUID = new ThingUID(THING_TYPE_LUMINA_AREA, bridgeUID, thingID);
break;
case OMNI:
thingUID = new ThingUID(THING_TYPE_OMNI_AREA, bridgeUID, thingID);
break;
default:
throw new IllegalStateException("Unknown System Type");
}
}
if (thingUID != null) {
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withProperty(THING_PROPERTIES_NUMBER, thingID)
.withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID)
.withLabel(thingName).build();
thingDiscovered(discoveryResult);
}
areas.add(areaProperties);
}
return areas;
} else {
return null;
}
}
/**
* Discovers OmniLink supported units
*/
private void discoverUnits() {
final OmnilinkBridgeHandler handler = bridgeHandler;
if (handler != null) {
final ThingUID bridgeUID = handler.getThing().getUID();
final List<AreaProperties> areas = this.areas;
if (areas != null) {
for (AreaProperties areaProperties : areas) {
int areaFilter = bitFilterForArea(areaProperties);
ObjectPropertyRequest<UnitProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(handler, ObjectPropertyRequests.UNIT, 0, 1).selectNamed().areaFilter(areaFilter)
.selectAnyLoad().build();
for (UnitProperties unitProperties : objectPropertyRequest) {
int thingType = unitProperties.getUnitType();
String thingName = unitProperties.getName();
String thingID = Integer.toString(unitProperties.getNumber());
ThingUID thingUID = null;
Map<String, Object> properties = new HashMap<>();
properties.put(THING_PROPERTIES_NAME, thingName);
properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
switch (thingType) {
case UNIT_TYPE_HLC_ROOM:
case UNIT_TYPE_VIZIARF_ROOM:
thingUID = new ThingUID(THING_TYPE_ROOM, bridgeUID, thingID);
break;
case UNIT_TYPE_FLAG:
thingUID = new ThingUID(THING_TYPE_FLAG, bridgeUID, thingID);
break;
case UNIT_TYPE_OUTPUT:
thingUID = new ThingUID(THING_TYPE_OUTPUT, bridgeUID, thingID);
break;
case UNIT_TYPE_UPB:
case UNIT_TYPE_HLC_LOAD:
thingUID = new ThingUID(THING_TYPE_UNIT_UPB, bridgeUID, thingID);
break;
case UNIT_TYPE_CENTRALITE:
case UNIT_TYPE_RADIORA:
case UNIT_TYPE_VIZIARF_LOAD:
case UNIT_TYPE_COMPOSE:
thingUID = new ThingUID(THING_TYPE_DIMMABLE, bridgeUID, thingID);
break;
default:
thingUID = new ThingUID(THING_TYPE_UNIT, bridgeUID, thingID);
logger.debug("Generic unit type: {}", thingType);
break;
}
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
.withProperties(properties).withProperty(THING_PROPERTIES_NUMBER, thingID)
.withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID)
.withLabel(thingName).build();
thingDiscovered(discoveryResult);
}
}
}
}
}
/**
* Generates zone items
*/
private void discoverZones() {
final OmnilinkBridgeHandler handler = bridgeHandler;
if (handler != null) {
final ThingUID bridgeUID = handler.getThing().getUID();
final List<AreaProperties> areas = this.areas;
if (areas != null) {
for (AreaProperties areaProperties : areas) {
int areaFilter = bitFilterForArea(areaProperties);
ObjectPropertyRequest<ZoneProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(handler, ObjectPropertyRequests.ZONE, 0, 1).selectNamed().areaFilter(areaFilter)
.build();
for (ZoneProperties zoneProperties : objectPropertyRequest) {
if (zoneProperties.getZoneType() <= SENSOR_TYPE_PROGRAMMABLE_ENERGY_SAVER_MODULE) {
String thingName = zoneProperties.getName();
String thingID = Integer.toString(zoneProperties.getNumber());
Map<String, Object> properties = new HashMap<>();
properties.put(THING_PROPERTIES_NAME, thingName);
properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
ThingUID thingUID = new ThingUID(THING_TYPE_ZONE, bridgeUID, thingID);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
.withProperties(properties).withProperty(THING_PROPERTIES_NUMBER, thingID)
.withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID)
.withLabel(thingName).build();
thingDiscovered(discoveryResult);
}
}
}
}
}
}
}

View File

@ -0,0 +1,240 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.math.BigInteger;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.omnilink.internal.AreaAlarm;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.Message;
import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
import com.digitaldan.jomnilinkII.MessageTypes.SecurityCodeValidation;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAreaStatus;
import com.digitaldan.jomnilinkII.MessageTypes.systemevents.AllOnOffEvent;
import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
/**
* The {@link AbstractAreaHandler} defines some methods that can be used across
* the many different areas defined in an OmniLink Controller.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public abstract class AbstractAreaHandler extends AbstractOmnilinkStatusHandler<ExtendedAreaStatus> {
private final Logger logger = LoggerFactory.getLogger(AbstractAreaHandler.class);
private final int thingID = getThingNumber();
public AbstractAreaHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
super.initialize();
if (bridgeHandler != null) {
updateAreaProperties(bridgeHandler);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Received null bridge while initializing Area!");
}
}
private void updateAreaProperties(OmnilinkBridgeHandler bridgeHandler) {
final List<AreaProperties> areas = getAreaProperties();
if (areas != null) {
for (AreaProperties areaProperties : areas) {
String thingName = areaProperties.getName();
if (areaProperties.getNumber() == 1 && "".equals(thingName)) {
thingName = "Main Area";
}
Map<String, String> properties = editProperties();
properties.put(THING_PROPERTIES_NAME, thingName);
updateProperties(properties);
}
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand: {}, command: {}", channelUID, command);
if (command instanceof RefreshType) {
retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null staus update!"));
return;
}
switch (channelUID.getId()) {
case CHANNEL_AREA_ACTIVATE_KEYPAD_EMERGENCY:
handleKeypadEmergency(channelUID, command);
break;
default:
handleSecurityMode(channelUID, command);
break;
}
}
private void handleSecurityMode(ChannelUID channelUID, Command command) {
int mode = getMode(channelUID);
if (!(command instanceof StringType)) {
logger.debug("Invalid command: {}, must be StringType", command);
return;
}
logger.debug("Received mode: {}, on area: {}", mode, thingID);
char[] code = command.toFullString().toCharArray();
if (code.length != 4) {
logger.warn("Invalid code length, code must be 4 digits");
} else {
// mode, codeNum, areaNum
try {
final OmnilinkBridgeHandler bridge = getOmnilinkBridgeHandler();
if (bridge != null) {
SecurityCodeValidation codeValidation = bridge.reqSecurityCodeValidation(thingID,
Character.getNumericValue(code[0]), Character.getNumericValue(code[1]),
Character.getNumericValue(code[2]), Character.getNumericValue(code[3]));
/*
* 0 Invalid code
* 1 Master
* 2 Manager
* 3 User
*/
logger.debug("User code number: {}, level: {}", codeValidation.getCodeNumber(),
codeValidation.getAuthorityLevel());
/*
* Valid user code number are 1-99, 251 is duress code, 0 means code does not exist
*/
if ((codeValidation.getCodeNumber() > 0 && codeValidation.getCodeNumber() <= 99)
&& codeValidation.getAuthorityLevel() > 0) {
sendOmnilinkCommand(mode, codeValidation.getCodeNumber(), thingID);
} else {
logger.warn("System reported an invalid code");
}
} else {
logger.debug("Received null bridge while sending area command!");
}
} catch (OmniInvalidResponseException e) {
logger.debug("Could not arm area: {}, are all zones closed?", e.getMessage());
} catch (OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Could not send area command: {}", e.getMessage());
}
}
// This is a send only channel, so don't store the user code
updateState(channelUID, UnDefType.UNDEF);
}
/**
* Get the specific mode for the OmniLink type
*
* @param channelUID Channel that maps to a mode
* @return OmniLink representation of mode.
*/
protected abstract int getMode(ChannelUID channelUID);
/**
* Get the set of alarms supported by this area handler.
*
* @return Set of alarms for this handler.
*/
protected abstract EnumSet<AreaAlarm> getAlarms();
private void handleKeypadEmergency(ChannelUID channelUID, Command command) {
if (command instanceof DecimalType) {
try {
final OmnilinkBridgeHandler bridge = getOmnilinkBridgeHandler();
if (bridge != null) {
bridge.activateKeypadEmergency(thingID, ((DecimalType) command).intValue());
} else {
logger.debug("Received null bridge while sending Keypad Emergency command!");
}
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Received exception while sending command to OmniLink Controller: {}", e.getMessage());
}
} else {
logger.debug("Invalid command: {}, must be DecimalType", command);
}
}
@Override
public void updateChannels(ExtendedAreaStatus status) {
logger.debug("Handle area event: mode: {}, alarms: {}, entryTimer: {}, exitTimer: {}", status.getMode(),
status.getAlarms(), status.getEntryTimer(), status.getExitTimer());
/*
* According to the specification, if the 3rd bit is set on a area mode, then that mode is in a delayed state.
* Unfortunately, this is not the case, but we can fix that by looking to see if the exit timer
* is set and do this manually.
*/
int mode = status.getExitTimer() > 0 ? status.getMode() | 1 << 3 : status.getMode();
updateState(new ChannelUID(thing.getUID(), CHANNEL_AREA_MODE), new DecimalType(mode));
/*
* Alarm status is actually 8 status, packed into each bit, so we loop through to see if a bit is set, note that
* this means you can have multiple alarms set at once
*/
BigInteger alarmBits = BigInteger.valueOf(status.getAlarms());
for (AreaAlarm alarm : getAlarms()) {
OnOffType alarmState = OnOffType.from(alarm.isSet(alarmBits));
updateState(new ChannelUID(thing.getUID(), alarm.getChannelUID()), alarmState);
}
}
public void handleAllOnOffEvent(AllOnOffEvent event) {
ChannelUID activateChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_AREA_ALL_ON_OFF_EVENT);
triggerChannel(activateChannel, event.isOn() ? "ON" : "OFF");
}
@Override
protected Optional<ExtendedAreaStatus> retrieveStatus() {
try {
final OmnilinkBridgeHandler bridge = getOmnilinkBridgeHandler();
if (bridge != null) {
ObjectStatus objStatus = bridge.requestObjectStatus(Message.OBJ_TYPE_AREA, thingID, thingID, true);
return Optional.of((ExtendedAreaStatus) objStatus.getStatuses()[0]);
} else {
logger.debug("Received null bridge while updating Area status!");
return Optional.empty();
}
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Received exception while refreshing Area status: {}", e.getMessage());
return Optional.empty();
}
}
}

View File

@ -0,0 +1,122 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
/**
* The {@link AbstractOmnilinkHandler} defines some methods that can be used across
* the many different things exposed by the OmniLink protocol
*
* @author Brian O'Connell - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public abstract class AbstractOmnilinkHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(AbstractOmnilinkHandler.class);
public AbstractOmnilinkHandler(Thing thing) {
super(thing);
}
public @Nullable OmnilinkBridgeHandler getOmnilinkBridgeHandler() {
Bridge bridge = getBridge();
if (bridge != null) {
return (OmnilinkBridgeHandler) bridge.getHandler();
} else {
return null;
}
}
protected void sendOmnilinkCommand(int message, int param1, int param2) {
try {
final OmnilinkBridgeHandler bridge = getOmnilinkBridgeHandler();
if (bridge != null) {
bridge.sendOmnilinkCommand(message, param1, param2);
} else {
logger.debug("Received null bridge while sending OmniLink command!");
}
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Could not send command to OmniLink Controller: {}", e.getMessage());
}
}
/**
* Calculate the area filter the a supplied area
*
* @param area Area to calculate filter for.
* @return Calculated Bit Filter for the supplied area. Bit 0 is area 1, bit 2 is area 2 and so on.
*/
protected static int bitFilterForArea(AreaProperties areaProperties) {
return BigInteger.ZERO.setBit(areaProperties.getNumber() - 1).intValue();
}
protected @Nullable List<AreaProperties> getAreaProperties() {
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
List<AreaProperties> areas = new LinkedList<>();
if (bridgeHandler != null) {
ObjectPropertyRequest<AreaProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(bridgeHandler, ObjectPropertyRequests.AREA, 0, 1).build();
for (AreaProperties areaProperties : objectPropertyRequest) {
String thingName = areaProperties.getName();
if (areaProperties.getNumber() == 1 && "".equals(thingName)) {
areas.add(areaProperties);
break;
} else if ("".equals(thingName)) {
break;
} else {
areas.add(areaProperties);
}
}
}
return areas;
}
/**
* Gets the configured number for a thing.
*
* @return Configured number for a thing.
*/
protected int getThingNumber() {
return ((Number) getThing().getConfiguration().get(THING_PROPERTIES_NUMBER)).intValue();
}
/**
* Gets the configured area number for a thing.
*
* @return Configured area number for a thing.
*/
protected int getAreaNumber() {
return ((Number) getThing().getConfiguration().get(THING_PROPERTIES_AREA)).intValue();
}
}

View File

@ -0,0 +1,83 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.Status;
/**
* The {@link AbstractOmnilinkStatusHandler} defines some methods that can be used across
* the many different units exposed by the OmniLink protocol to retrive updated status information.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public abstract class AbstractOmnilinkStatusHandler<T extends Status> extends AbstractOmnilinkHandler {
public AbstractOmnilinkStatusHandler(Thing thing) {
super(thing);
}
private volatile Optional<T> status = Optional.empty();
@Override
public void initialize() {
updateHandlerStatus();
}
/**
* Attempt to retrieve an updated status for this handler type.
*
* @return Optional with updated status if possible, empty optional otherwise.
*/
protected abstract Optional<T> retrieveStatus();
/**
* Update channels associated with handler
*
* @param t Status object to update channels with
*/
protected abstract void updateChannels(T t);
/**
* Process a status update for this handler. This will dispatch updateChannels where appropriate.
*
* @param t Status to process.
*/
public void handleStatus(T t) {
this.status = Optional.of(t);
updateChannels(t);
}
@Override
public void channelLinked(ChannelUID channelUID) {
status.ifPresent(this::updateChannels);
}
private void updateHandlerStatus() {
Bridge bridge = getBridge();
if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null staus update!"));
}
}
}

View File

@ -0,0 +1,182 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.Message;
import com.digitaldan.jomnilinkII.MessageTypes.AudioSourceStatus;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AudioSourceProperties;
import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
/**
* The {@link AudioSourceHandler} defines some methods that are used to
* interface with an OmniLink Audio Source. This by extension also defines the
* Audio Source thing that openHAB will be able to pick up and interface with.
*
* @author Brian O'Connell - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class AudioSourceHandler extends AbstractOmnilinkHandler {
private final Logger logger = LoggerFactory.getLogger(AudioSourceHandler.class);
private final int POLL_DELAY_SECONDS = 5;
private final int thingID = getThingNumber();
private @Nullable ScheduledFuture<?> scheduledPolling = null;
public @Nullable String number;
public AudioSourceHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
updateStatus(ThingStatus.ONLINE);
if (((Boolean) getThing().getConfiguration().get(THING_PROPERTIES_AUTO_START)).booleanValue()) {
logger.debug("Autostart enabled, scheduling polling for Audio Source: {}", thingID);
schedulePolling();
} else {
logger.debug("Autostart disabled, not scheduling polling for Audio Source: {}", thingID);
cancelPolling();
}
updateAudioSourceProperties(bridgeHandler);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Received null bridge while initializing Audio Source!");
}
}
private void updateAudioSourceProperties(OmnilinkBridgeHandler bridgeHandler) {
ObjectPropertyRequest<AudioSourceProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(bridgeHandler, ObjectPropertyRequests.AUDIO_SOURCE, thingID, 0).selectNamed().build();
for (AudioSourceProperties audioSourceProperties : objectPropertyRequest) {
Map<String, String> properties = editProperties();
properties.put(THING_PROPERTIES_NAME, audioSourceProperties.getName());
updateProperties(properties);
}
}
@Override
public synchronized void dispose() {
cancelPolling();
super.dispose();
}
private synchronized void cancelPolling() {
final ScheduledFuture<?> scheduledPolling = this.scheduledPolling;
if (scheduledPolling != null) {
logger.debug("Cancelling polling for Audio Source: {}", thingID);
scheduledPolling.cancel(false);
}
}
private synchronized void schedulePolling() {
cancelPolling();
logger.debug("Scheduling polling for Audio Source: {}", thingID);
scheduledPolling = super.scheduler.scheduleWithFixedDelay(this::pollAudioSource, 0, POLL_DELAY_SECONDS,
TimeUnit.SECONDS);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
final ScheduledFuture<?> scheduledPolling = this.scheduledPolling;
switch (channelUID.getId()) {
case CHANNEL_AUDIO_SOURCE_POLLING:
if (command instanceof RefreshType) {
updateState(CHANNEL_AUDIO_SOURCE_POLLING,
OnOffType.from((scheduledPolling != null && !scheduledPolling.isDone())));
} else if (command instanceof OnOffType) {
handlePolling(channelUID, (OnOffType) command);
} else {
logger.debug("Invalid command: {}, must be RefreshType or OnOffType", command);
}
break;
default:
logger.warn("Unknown channel for Audio Source thing: {}", channelUID);
}
}
private void handlePolling(ChannelUID channelUID, OnOffType command) {
logger.debug("handlePolling called for channel: {}, command: {}", channelUID, command);
if (OnOffType.ON.equals(command)) {
schedulePolling();
} else {
cancelPolling();
}
}
public void pollAudioSource() {
try {
final OmnilinkBridgeHandler bridge = getOmnilinkBridgeHandler();
if (bridge != null) {
Message message;
int position = 0;
while ((message = bridge.requestAudioSourceStatus(thingID, position))
.getMessageType() == Message.MESG_TYPE_AUDIO_SOURCE_STATUS) {
logger.trace("Polling for Audio Source statuses on thing: {}", thingID);
AudioSourceStatus audioSourceStatus = (AudioSourceStatus) message;
position = audioSourceStatus.getPosition();
switch (position) {
case 1:
updateState(CHANNEL_AUDIO_SOURCE_TEXT1, new StringType(audioSourceStatus.getSourceData()));
break;
case 2:
updateState(CHANNEL_AUDIO_SOURCE_TEXT2, new StringType(audioSourceStatus.getSourceData()));
break;
case 3:
updateState(CHANNEL_AUDIO_SOURCE_TEXT3, new StringType(audioSourceStatus.getSourceData()));
break;
case 4:
updateState(CHANNEL_AUDIO_SOURCE_TEXT4, new StringType(audioSourceStatus.getSourceData()));
break;
case 5:
updateState(CHANNEL_AUDIO_SOURCE_TEXT5, new StringType(audioSourceStatus.getSourceData()));
break;
case 6:
updateState(CHANNEL_AUDIO_SOURCE_TEXT6, new StringType(audioSourceStatus.getSourceData()));
break;
}
}
} else {
logger.debug("Received null bridge while polling Audio Source statuses!");
}
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Exception recieved while polling for Audio Source statuses: {}", e.getMessage());
}
}
}

View File

@ -0,0 +1,208 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.AudioPlayer;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.NextPreviousType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.PlayPauseType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.Message;
import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AudioZoneProperties;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAudioZoneStatus;
import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
/**
* The {@link AudioZoneHandler} defines some methods that are used to
* interface with an OmniLink Audio Zone. This by extension also defines the
* Audio Zone thing that openHAB will be able to pick up and interface with.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class AudioZoneHandler extends AbstractOmnilinkStatusHandler<ExtendedAudioZoneStatus> {
private final Logger logger = LoggerFactory.getLogger(AudioZoneHandler.class);
private final int thingID = getThingNumber();
public @Nullable String number;
public AudioZoneHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
super.initialize();
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
updateAudioZoneProperties(bridgeHandler);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Received null bridge while initializing Audio Zone!");
}
}
private void updateAudioZoneProperties(OmnilinkBridgeHandler bridgeHandler) {
ObjectPropertyRequest<AudioZoneProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(bridgeHandler, ObjectPropertyRequests.AUDIO_ZONE, thingID, 0).selectNamed().build();
for (AudioZoneProperties audioZoneProperties : objectPropertyRequest) {
Map<String, String> properties = editProperties();
properties.put(THING_PROPERTIES_NAME, audioZoneProperties.getName());
updateProperties(properties);
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
if (command instanceof RefreshType) {
retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null staus update!"));
return;
}
switch (channelUID.getId()) {
case CHANNEL_AUDIO_ZONE_POWER:
if (command instanceof OnOffType) {
sendOmnilinkCommand(OmniLinkCmd.CMD_AUDIO_ZONE_SET_ON_MUTE.getNumber(),
OnOffType.OFF.equals(command) ? 0 : 1, thingID);
} else {
logger.debug("Invalid command: {}, must be OnOffType", command);
}
break;
case CHANNEL_AUDIO_ZONE_MUTE:
if (command instanceof OnOffType) {
sendOmnilinkCommand(OmniLinkCmd.CMD_AUDIO_ZONE_SET_ON_MUTE.getNumber(),
OnOffType.OFF.equals(command) ? 2 : 3, thingID);
} else {
logger.debug("Invalid command: {}, must be OnOffType", command);
}
break;
case CHANNEL_AUDIO_ZONE_VOLUME:
if (command instanceof PercentType) {
sendOmnilinkCommand(OmniLinkCmd.CMD_AUDIO_ZONE_SET_VOLUME.getNumber(),
((PercentType) command).intValue(), thingID);
} else {
logger.debug("Invalid command: {}, must be PercentType", command);
}
break;
case CHANNEL_AUDIO_ZONE_SOURCE:
if (command instanceof DecimalType) {
sendOmnilinkCommand(OmniLinkCmd.CMD_AUDIO_ZONE_SET_SOURCE.getNumber(),
((DecimalType) command).intValue(), thingID);
} else {
logger.debug("Invalid command: {}, must be DecimalType", command);
}
break;
case CHANNEL_AUDIO_ZONE_CONTROL:
if (command instanceof PlayPauseType) {
handlePlayPauseCommand(channelUID, (PlayPauseType) command);
} else if (command instanceof NextPreviousType) {
handleNextPreviousCommand(channelUID, (NextPreviousType) command);
} else {
logger.debug("Invalid command: {}, must be PlayPauseType or NextPreviousType", command);
}
break;
default:
logger.warn("Unknown channel for Audio Zone thing: {}", channelUID);
}
}
private void handlePlayPauseCommand(ChannelUID channelUID, PlayPauseType command) {
logger.debug("handlePlayPauseCommand called for channel: {}, command: {}", channelUID, command);
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
Optional<AudioPlayer> audioPlayer = bridgeHandler.getAudioPlayer();
if (audioPlayer.isPresent()) {
AudioPlayer player = audioPlayer.get();
sendOmnilinkCommand(OmniLinkCmd.CMD_AUDIO_ZONE_SET_SOURCE.getNumber(),
PlayPauseType.PLAY.equals(command) ? player.getPlayCommand() : player.getPauseCommand(),
thingID);
} else {
logger.warn("No Audio Player was detected!");
}
} else {
logger.debug("Received null bridge while sending Audio Zone command!");
}
}
private void handleNextPreviousCommand(ChannelUID channelUID, NextPreviousType command) {
logger.debug("handleNextPreviousCommand called for channel: {}, command: {}", channelUID, command);
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
Optional<AudioPlayer> audioPlayer = bridgeHandler.getAudioPlayer();
if (audioPlayer.isPresent()) {
AudioPlayer player = audioPlayer.get();
sendOmnilinkCommand(OmniLinkCmd.CMD_AUDIO_ZONE_SET_SOURCE.getNumber(),
NextPreviousType.NEXT.equals(command) ? player.getNextCommand() : player.getPreviousCommand(),
thingID);
} else {
logger.warn("Audio Player could not be found!");
}
} else {
logger.debug("Received null bridge while sending Audio Zone command!");
}
}
@Override
public void updateChannels(ExtendedAudioZoneStatus status) {
logger.debug("updateChannels called for Audio Zone status: {}", status);
updateState(CHANNEL_AUDIO_ZONE_POWER, OnOffType.from(status.isPower()));
updateState(CHANNEL_AUDIO_ZONE_MUTE, OnOffType.from(status.isMute()));
updateState(CHANNEL_AUDIO_ZONE_VOLUME, new PercentType(status.getVolume()));
updateState(CHANNEL_AUDIO_ZONE_SOURCE, new DecimalType(status.getSource()));
}
@Override
protected Optional<ExtendedAudioZoneStatus> retrieveStatus() {
try {
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
ObjectStatus objStatus = bridgeHandler.requestObjectStatus(Message.OBJ_TYPE_AUDIO_ZONE, thingID,
thingID, true);
return Optional.of((ExtendedAudioZoneStatus) objStatus.getStatuses()[0]);
} else {
logger.debug("Received null bridge while updating Audio Zone status!");
return Optional.empty();
}
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Received exception while refreshing Audio Zone status: {}", e.getMessage());
return Optional.empty();
}
}
}

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link BridgeOfflineException} defines an exception for when the OmniLink
* Bridge is offline or unavailable.
*
* @author Craig Hamilton - Initial contribution
*/
@NonNullByDefault
public class BridgeOfflineException extends Exception {
private static final long serialVersionUID = -9081729691518514097L;
public BridgeOfflineException(Exception e) {
super(e);
}
}

View File

@ -0,0 +1,119 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.ButtonProperties;
/**
* The {@link ButtonHandler} defines some methods that are used to
* interface with an OmniLink Button. This by extension also defines the
* Button thing that openHAB will be able to pick up and interface with.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class ButtonHandler extends AbstractOmnilinkHandler {
private final Logger logger = LoggerFactory.getLogger(ButtonHandler.class);
private final int thingID = getThingNumber();
public @Nullable String number;
public ButtonHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
updateStatus(ThingStatus.ONLINE);
updateChannels();
updateButtonProperties(bridgeHandler);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Received null bridge while initializing Button!");
}
}
private void updateButtonProperties(OmnilinkBridgeHandler bridgeHandler) {
final List<AreaProperties> areas = getAreaProperties();
if (areas != null) {
for (AreaProperties areaProperties : areas) {
int areaFilter = bitFilterForArea(areaProperties);
ObjectPropertyRequest<ButtonProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(bridgeHandler, ObjectPropertyRequests.BUTTONS, thingID, 0).selectNamed()
.areaFilter(areaFilter).build();
for (ButtonProperties buttonProperties : objectPropertyRequest) {
Map<String, String> properties = editProperties();
properties.put(THING_PROPERTIES_NAME, buttonProperties.getName());
properties.put(THING_PROPERTIES_AREA, Integer.toString(areaProperties.getNumber()));
updateProperties(properties);
}
}
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
if (command instanceof RefreshType) {
updateChannels();
return;
}
switch (channelUID.getId()) {
case CHANNEL_BUTTON_PRESS:
if (command instanceof OnOffType) {
sendOmnilinkCommand(OmniLinkCmd.CMD_BUTTON.getNumber(), 0, thingID);
updateChannels();
} else {
logger.debug("Invalid command: {}, must be OnOffType", command);
}
break;
default:
logger.warn("Unknown channel for Button thing: {}", channelUID);
}
}
public void buttonActivated() {
ChannelUID activateChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_BUTTON_ACTIVATED_EVENT);
triggerChannel(activateChannel);
}
public void updateChannels() {
updateState(CHANNEL_BUTTON_PRESS, OnOffType.OFF);
}
}

View File

@ -0,0 +1,94 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link ConsoleHandler} defines some methods that are used to
* interface with an OmniLink Console. This by extension also defines the
* Console thing that openHAB will be able to pick up and interface with.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class ConsoleHandler extends AbstractOmnilinkHandler {
private final Logger logger = LoggerFactory.getLogger(ConsoleHandler.class);
private final int thingID = getThingNumber();
public ConsoleHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
if (getOmnilinkBridgeHandler() != null) {
updateStatus(ThingStatus.ONLINE);
updateChannels();
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Received null bridge while initializing Console!");
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
if (command instanceof RefreshType) {
updateChannels();
return;
}
switch (channelUID.getId()) {
case CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER:
if (command instanceof StringType) {
sendOmnilinkCommand(OmniLinkCmd.CMD_CONSOLE_ENABLE_DISABLE_BEEPER.getNumber(),
((StringType) command).equals(StringType.valueOf("OFF")) ? 0 : 1, thingID);
} else {
logger.debug("Invalid command: {}, must be StringType", command);
}
break;
case CHANNEL_CONSOLE_BEEP:
if (command instanceof DecimalType) {
sendOmnilinkCommand(OmniLinkCmd.CMD_CONSOLE_BEEP.getNumber(), ((DecimalType) command).intValue(),
thingID);
} else {
logger.debug("Invalid command: {}, must be DecimalType", command);
}
break;
default:
logger.warn("Unknown channel for Console thing: {}", channelUID);
}
updateChannels();
}
public void updateChannels() {
updateState(CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER, UnDefType.UNDEF);
updateState(CHANNEL_CONSOLE_BEEP, UnDefType.UNDEF);
}
}

View File

@ -0,0 +1,157 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.measure.quantity.Dimensionless;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.Message;
import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AuxSensorProperties;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAuxSensorStatus;
import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
/**
* The {@link HumiditySensorHandler} defines some methods that are used to
* interface with an OmniLink Humidity Sensor. This by extension also defines
* the Humidity Sensor thing that openHAB will be able to pick up and interface
* with.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class HumiditySensorHandler extends AbstractOmnilinkStatusHandler<ExtendedAuxSensorStatus> {
private final Logger logger = LoggerFactory.getLogger(HumiditySensorHandler.class);
private final int thingID = getThingNumber();
public @Nullable String number;
public HumiditySensorHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
super.initialize();
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
updateHumiditySensorProperties(bridgeHandler);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Received null bridge while initializing Humidity Sensor!");
}
}
private void updateHumiditySensorProperties(OmnilinkBridgeHandler bridgeHandler) {
final List<AreaProperties> areas = getAreaProperties();
if (areas != null) {
for (AreaProperties areaProperties : areas) {
int areaFilter = bitFilterForArea(areaProperties);
ObjectPropertyRequest<AuxSensorProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(bridgeHandler, ObjectPropertyRequests.AUX_SENSORS, thingID, 0).selectNamed()
.areaFilter(areaFilter).build();
for (AuxSensorProperties auxSensorProperties : objectPropertyRequest) {
Map<String, String> properties = editProperties();
properties.put(THING_PROPERTIES_NAME, auxSensorProperties.getName());
properties.put(THING_PROPERTIES_AREA, Integer.toString(areaProperties.getNumber()));
updateProperties(properties);
}
}
}
}
@Override
@SuppressWarnings("unchecked")
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
if (command instanceof RefreshType) {
retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null staus update!"));
return;
}
if (!(command instanceof QuantityType)) {
logger.debug("Invalid command: {}, must be QuantityType", command);
return;
}
switch (channelUID.getId()) {
case CHANNEL_AUX_LOW_SETPOINT:
sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HEAT_LOW_POINT.getNumber(),
TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType<Dimensionless>) command).intValue()),
thingID);
break;
case CHANNEL_AUX_HIGH_SETPOINT:
sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_COOL_HIGH_POINT.getNumber(),
TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType<Dimensionless>) command).intValue()),
thingID);
break;
default:
logger.warn("Unknown channel for Humdity Sensor thing: {}", channelUID);
}
}
@Override
public void updateChannels(ExtendedAuxSensorStatus status) {
logger.debug("updateChannels called for Humidity Sensor status: {}", status);
updateState(CHANNEL_AUX_HUMIDITY,
new QuantityType<>(TemperatureFormat.FAHRENHEIT.omniToFormat(status.getTemperature()), Units.PERCENT));
updateState(CHANNEL_AUX_LOW_SETPOINT,
new QuantityType<>(TemperatureFormat.FAHRENHEIT.omniToFormat(status.getHeatSetpoint()), Units.PERCENT));
updateState(CHANNEL_AUX_HIGH_SETPOINT,
new QuantityType<>(TemperatureFormat.FAHRENHEIT.omniToFormat(status.getCoolSetpoint()), Units.PERCENT));
}
@Override
protected Optional<ExtendedAuxSensorStatus> retrieveStatus() {
try {
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
ObjectStatus objStatus = bridgeHandler.requestObjectStatus(Message.OBJ_TYPE_AUX_SENSOR, thingID,
thingID, true);
return Optional.of((ExtendedAuxSensorStatus) objStatus.getStatuses()[0]);
} else {
logger.debug("Received null bridge while updating Humidity Sensor status!");
return Optional.empty();
}
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Received exception while refreshing Humidity Sensor status: {}", e.getMessage());
return Optional.empty();
}
}
}

View File

@ -0,0 +1,129 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.Message;
import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AccessControlReaderProperties;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAccessControlReaderLockStatus;
import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
/**
* The {@link LockHandler} defines some methods that are used to
* interface with an OmniLink Lock. This by extension also defines the
* Lock thing that openHAB will be able to pick up and interface with.
*
* @author Brian O'Connell - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class LockHandler extends AbstractOmnilinkStatusHandler<ExtendedAccessControlReaderLockStatus> {
private final Logger logger = LoggerFactory.getLogger(LockHandler.class);
private final int thingID = getThingNumber();
public @Nullable String number;
public LockHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
super.initialize();
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
updateLockProperties(bridgeHandler);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Received null bridge while initializing Lock!");
}
}
private void updateLockProperties(OmnilinkBridgeHandler bridgeHandler) {
ObjectPropertyRequest<AccessControlReaderProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(bridgeHandler, ObjectPropertyRequests.LOCK, thingID, 0).selectNamed().build();
for (AccessControlReaderProperties lockProperties : objectPropertyRequest) {
Map<String, String> properties = editProperties();
properties.put(THING_PROPERTIES_NAME, lockProperties.getName());
updateProperties(properties);
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
if (command instanceof RefreshType) {
retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null staus update!"));
return;
}
switch (channelUID.getId()) {
case CHANNEL_LOCK_SWITCH:
if (command instanceof OnOffType) {
sendOmnilinkCommand(OnOffType.OFF.equals(command) ? OmniLinkCmd.CMD_UNLOCK_DOOR.getNumber()
: OmniLinkCmd.CMD_LOCK_DOOR.getNumber(), 0, thingID);
} else {
logger.debug("Invalid command {}, must be OnOffType", command);
}
break;
default:
logger.warn("Unknown channel for Lock thing: {}", channelUID);
}
}
@Override
public void updateChannels(ExtendedAccessControlReaderLockStatus status) {
logger.debug("updateChannels called for Lock status: {}", status);
updateState(CHANNEL_LOCK_SWITCH, OnOffType.from(status.isLocked()));
}
@Override
protected Optional<ExtendedAccessControlReaderLockStatus> retrieveStatus() {
try {
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
ObjectStatus objStatus = bridgeHandler.requestObjectStatus(Message.OBJ_TYPE_CONTROL_LOCK, thingID,
thingID, true);
return Optional.of((ExtendedAccessControlReaderLockStatus) objStatus.getStatuses()[0]);
} else {
logger.debug("Received null bridge while updating Lock status!");
return Optional.empty();
}
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Received exception while refreshing Lock status: {}", e.getMessage());
return Optional.empty();
}
}
}

View File

@ -0,0 +1,67 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static org.openhab.binding.omnilink.internal.AreaAlarm.*;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.util.EnumSet;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.AreaAlarm;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
/**
* The {@link LuminaAreaHandler} defines some methods that are used to
* interface with an OmniLink Lumina Area. This by extension also defines the
* Lumina Area thing that openHAB will be able to pick up and interface with.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class LuminaAreaHandler extends AbstractAreaHandler {
private static final EnumSet<AreaAlarm> LUMINA_ALARMS = EnumSet.of(FREEZE, WATER, TEMPERATURE);
public @Nullable String number;
public LuminaAreaHandler(Thing thing) {
super(thing);
}
@Override
protected int getMode(ChannelUID channelUID) {
switch (channelUID.getId()) {
case CHANNEL_AREA_SECURITY_MODE_HOME:
return OmniLinkCmd.CMD_SECURITY_LUMINA_HOME_MODE.getNumber();
case CHANNEL_AREA_SECURITY_MODE_SLEEP:
return OmniLinkCmd.CMD_SECURITY_LUMINA_SLEEP_MODE.getNumber();
case CHANNEL_AREA_SECURITY_MODE_AWAY:
return OmniLinkCmd.CMD_SECURITY_LUMINA_AWAY_MODE.getNumber();
case CHANNEL_AREA_SECURITY_MODE_VACATION:
return OmniLinkCmd.CMD_SECURITY_LUMINA_VACATION_MODE.getNumber();
case CHANNEL_AREA_SECURITY_MODE_PARTY:
return OmniLinkCmd.CMD_SECURITY_LUMINA_PARTY_MODE.getNumber();
case CHANNEL_AREA_SECURITY_MODE_SPECIAL:
return OmniLinkCmd.CMD_SECURITY_LUMINA_SPECIAL_MODE.getNumber();
default:
throw new IllegalStateException("Unknown channel for area thing " + channelUID);
}
}
@Override
protected EnumSet<AreaAlarm> getAlarms() {
return LUMINA_ALARMS;
}
}

View File

@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static org.openhab.binding.omnilink.internal.AreaAlarm.*;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.util.EnumSet;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.AreaAlarm;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
/**
* The {@link OmniAreaHandler} defines some methods that are used to
* interface with an OmniLink OmniPro Area. This by extension also defines the
* OmniPro Area thing that openHAB will be able to pick up and interface with.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class OmniAreaHandler extends AbstractAreaHandler {
private static final EnumSet<AreaAlarm> OMNI_ALARMS = EnumSet.of(BURGLARY, FIRE, GAS, AUXILIARY, FREEZE, WATER,
DURESS, TEMPERATURE);
public @Nullable String number;
public OmniAreaHandler(Thing thing) {
super(thing);
}
@Override
protected int getMode(ChannelUID channelUID) {
switch (channelUID.getId()) {
case CHANNEL_AREA_SECURITY_MODE_DISARM:
return OmniLinkCmd.CMD_SECURITY_OMNI_DISARM.getNumber();
case CHANNEL_AREA_SECURITY_MODE_DAY:
return OmniLinkCmd.CMD_SECURITY_OMNI_DAY_MODE.getNumber();
case CHANNEL_AREA_SECURITY_MODE_NIGHT:
return OmniLinkCmd.CMD_SECURITY_OMNI_NIGHT_MODE.getNumber();
case CHANNEL_AREA_SECURITY_MODE_AWAY:
return OmniLinkCmd.CMD_SECURITY_OMNI_AWAY_MODE.getNumber();
case CHANNEL_AREA_SECURITY_MODE_VACATION:
return OmniLinkCmd.CMD_SECURITY_OMNI_VACATION_MODE.getNumber();
case CHANNEL_AREA_SECURITY_MODE_DAY_INSTANT:
return OmniLinkCmd.CMD_SECURITY_OMNI_DAY_INSTANCE_MODE.getNumber();
case CHANNEL_AREA_SECURITY_MODE_NIGHT_DELAYED:
return OmniLinkCmd.CMD_SECURITY_OMNI_NIGHT_DELAYED_MODE.getNumber();
default:
throw new IllegalStateException("Unknown channel for area thing " + channelUID);
}
}
@Override
protected EnumSet<AreaAlarm> getAlarms() {
return OMNI_ALARMS;
}
}

View File

@ -0,0 +1,199 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* OmniLink commands
*
* @author Dan Cunningham - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
* @since 1.5.0
*/
@NonNullByDefault
public enum OmniLinkCmd {
CMD_UNIT_OFF(0),
CMD_UNIT_ON(1),
CMD_UNIT_AREA_ALL_OFF(2),
CMD_UNIT_AREA_ALL_ON(3),
CMD_UNIT_PERCENT(9),
CMD_UNIT_LO9_LEVEL_HIGH7(101),
CMD_UNIT_DECREMENT_COUNTER(10),
CMD_UNIT_INCREMENT_COUNTER(11),
CMD_UNIT_SET_COUNTER(12),
CMD_UNIT_LO9_RAMP_HIGH7(13),
CMD_UNIT_LIGHTOLIER(14),
CMD_UNIT_UPB_REQ_STATUS(15),
CMD_UNIT_UNIT_DIM_STEP_1(17),
CMD_UNIT_UNIT_DIM_STEP_2(18),
CMD_UNIT_UNIT_DIM_STEP_3(19),
CMD_UNIT_UNIT_DIM_STEP_4(20),
CMD_UNIT_UNIT_DIM_STEP_5(21),
CMD_UNIT_UNIT_DIM_STEP_6(22),
CMD_UNIT_UNIT_DIM_STEP_7(23),
CMD_UNIT_UNIT_DIM_STEP_8(24),
CMD_UNIT_UNIT_DIM_STEP_9(25),
CMD_UNIT_UNIT_BRIGHTEN_STEP_1(33),
CMD_UNIT_UNIT_BRIGHTEN_STEP_2(34),
CMD_UNIT_UNIT_BRIGHTEN_STEP_3(35),
CMD_UNIT_UNIT_BRIGHTEN_STEP_4(36),
CMD_UNIT_UNIT_BRIGHTEN_STEP_5(37),
CMD_UNIT_UNIT_BRIGHTEN_STEP_6(38),
CMD_UNIT_UNIT_BRIGHTEN_STEP_7(39),
CMD_UNIT_UNIT_BRIGHTEN_STEP_8(40),
CMD_UNIT_UNIT_BRIGHTEN_STEP_9(41),
CMD_UNIT_UPB_LO9_BLINK_HIGH7(26),
CMD_UNIT_UPB_STOP_BLINK(27),
CMD_UNIT_UPB_LINK_OFF(28),
CMD_UNIT_UPB_LINK_ON(29),
CMD_UNIT_UPB_LINK_SET(30),
CMD_UNIT_CENTRALITE_SCENE_OFF(42),
CMD_UNIT_CENTRALITE_SCENE_ON(43),
CMD_UNIT_UPB_LED_OFF(44),
CMD_UNIT_UPB_LED_ON(45),
CMD_UNIT_RADIORA_PHANTOM_BUTTON_OFF(46),
CMD_UNIT_RADIORA_PHANTM_BUTTON_ON(46),
CMD_UNIT_LEVITON_SCENE_OFF(60),
CMD_UNIT_LEVITON_SCENE_ON(61),
CMD_UNIT_LEVITON_SCENE_SET(62),
CMD_SECURITY_OMNI_DISARM(48),
CMD_SECURITY_OMNI_DAY_MODE(49),
CMD_SECURITY_OMNI_NIGHT_MODE(50),
CMD_SECURITY_OMNI_AWAY_MODE(51),
CMD_SECURITY_OMNI_VACATION_MODE(52),
CMD_SECURITY_OMNI_DAY_INSTANCE_MODE(53),
CMD_SECURITY_OMNI_NIGHT_DELAYED_MODE(54),
CMD_SECURITY_BYPASS_ZONE(4),
CMD_SECURITY_RESTORE_ZONE(5),
CMD_SECURITY_RESTORE_ALL_ZONES(6),
CMD_SECURITY_LUMINA_HOME_MODE(49),
CMD_SECURITY_LUMINA_SLEEP_MODE(50),
CMD_SECURITY_LUMINA_AWAY_MODE(51),
CMD_SECURITY_LUMINA_VACATION_MODE(52),
CMD_SECURITY_LUMINA_PARTY_MODE(53),
CMD_SECURITY_LUMINA_SPECIAL_MODE(54),
CMD_BUTTON(7),
CMD_ENERGY_SAVER_OFF(64),
CMD_ENERGY_SAVER_ON(65),
CMD_THERMO_SET_HEAT_LOW_POINT(66),
CMD_THERMO_SET_COOL_HIGH_POINT(67),
CMD_THERMO_SET_SYSTEM_MODE(68),
CMD_THERMO_SET_FAN_MODE(69),
CMD_THERMO_SET_HOLD_MODE(70),
CMD_THERMO_RAISE_LOWER_HEAT(71),
CMD_THERMO_RAISE_LOWER_COOL(72),
CMD_THERMO_SET_HUMDIFY_POINT(73),
CMD_THERMO_SET_DEHUMIDIFY_POINT(74),
CMD_MESSAGE_SHOW_MESSAGE_WITH_BEEP_AND_LED(80),
CMD_MESSAGE_SHOW_MESSAGE_WITH_BEEP_OR_LED(86),
CMD_MESSAGE_LOG_MESSAGE(81),
CMD_MESSAGE_CLEAR_MESSAGE(82),
CMD_MESSAGE_SAY_MESSAGE(83),
CMD_MESSAGE_PHONE_AND_SAY_MESSAGE(84),
CMD_MESSAGE_SEND_MESSAGE_TO_SERIAL_PORT(85),
CMD_CONSOLE_ENABLE_DISABLE_BEEPER(102),
CMD_CONSOLE_BEEP(103),
CMD_LOCK_DOOR(105),
CMD_UNLOCK_DOOR(106),
CMD_AUDIO_ZONE_SET_ON_MUTE(112),
CMD_AUDIO_ZONE_SET_VOLUME(113),
CMD_AUDIO_ZONE_SET_SOURCE(114),
CMD_AUDIO_ZONE_SELECT_KEY(115),
SENSOR_UNIT_POWER(1001),
SENSOR_UNIT_LEVEL(1002),
SENSOR_UNIT_DISPLAY(1003),
SENSOR_THERMO_HEAT_POINTC(2001),
SENSOR_THERMO_HEAT_POINTF(2002),
SENSOR_THERMO_COOL_POINTC(2003),
SENSOR_THERMO_COOL_POINTF(2004),
SENSOR_THERMO_SYSTEM_MODE(2005),
SENSOR_THERMO_FAN_MODE(2006),
SENSOR_THERMO_HOLD_MODE(2007),
SENSOR_THERMO_TEMPC(2006),
SENSOR_THERMO_TEMPF(2007),
SENSOR_ZONE_STATUS_CURRENT(3001),
SENSOR_ZONE_STATUS_LATCHED(3002),
SENSOR_ZONE_STATUS_ARMING(3003),
SENSOR_AREA_STATUS_MODE(4001),
SENSOR_AREA_STATUS_ALARM(4002),
SENSOR_AREA_STATUS_EXIT_DELAY(4003),
SENSOR_AREA_STATUS_ENTRY_DELAY(4003),
SENSOR_AREA_EXIT_TIMER(4004),
SENSOR_AREA_ENTRY_TIMER(4005),
SENSOR_AUX_STATUS(5001),
SENSOR_AUX_CURRENTC(5002),
SENSOR_AUX_CURRENTF(5003),
SENSOR_AUX_LOWC(5004),
SENSOR_AUX_LOWF(5005),
SENSOR_AUX_HIGHC(5006),
SENSOR_AUX_HIGHF(5007),
SENSOR_AUDIOZONE_POWER(6001),
SENSOR_AUDIOZONE_SOURCE(6002),
SENSOR_AUDIOZONE_VOLUME(6003),
SENSOR_AUDIOZONE_MUTE(6004),
SENSOR_AUDIOZONE_TEXT(6005),
SENSOR_AUDIOZONE_TEXT_FIELD1(6006),
SENSOR_AUDIOZONE_TEXT_FIELD2(6007),
SENSOR_AUDIOZONE_TEXT_FIELD3(6008),
SENSOR_AUDIOSOURCE_TEXT(7001),
SENSOR_AUDIOSOURCE_TEXT_FIELD1(7002),
SENSOR_AUDIOSOURCE_TEXT_FIELD2(7003),
SENSOR_AUDIOSOURCE_TEXT_FIELD3(7004);
private int number;
OmniLinkCmd(int number) {
this.number = number;
}
public int getNumber() {
return number;
}
public static @Nullable OmniLinkCmd getCommand(String name) {
for (OmniLinkCmd command : OmniLinkCmd.values()) {
if (name.equals(command.toString())) {
return command;
}
}
return null;
}
public static @Nullable OmniLinkCmd getCommand(int ordinal) {
OmniLinkCmd[] values = OmniLinkCmd.values();
if (ordinal < values.length) {
return values[ordinal];
} else {
return null;
}
}
public static String toList() {
StringBuilder sb = new StringBuilder();
for (OmniLinkCmd command : OmniLinkCmd.values()) {
sb.append(command.toString()).append(",");
}
return sb.toString();
}
}

View File

@ -0,0 +1,696 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.io.IOException;
import java.net.UnknownHostException;
import java.time.ZonedDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.AudioPlayer;
import org.openhab.binding.omnilink.internal.SystemType;
import org.openhab.binding.omnilink.internal.config.OmnilinkBridgeConfig;
import org.openhab.binding.omnilink.internal.discovery.OmnilinkDiscoveryService;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.Connection;
import com.digitaldan.jomnilinkII.DisconnectListener;
import com.digitaldan.jomnilinkII.Message;
import com.digitaldan.jomnilinkII.MessageTypes.EventLogData;
import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
import com.digitaldan.jomnilinkII.MessageTypes.SecurityCodeValidation;
import com.digitaldan.jomnilinkII.MessageTypes.SystemFeatures;
import com.digitaldan.jomnilinkII.MessageTypes.SystemFormats;
import com.digitaldan.jomnilinkII.MessageTypes.SystemInformation;
import com.digitaldan.jomnilinkII.MessageTypes.SystemStatus;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAccessControlReaderLockStatus;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAreaStatus;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAudioZoneStatus;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAuxSensorStatus;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedThermostatStatus;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedUnitStatus;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedZoneStatus;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.Status;
import com.digitaldan.jomnilinkII.MessageTypes.systemevents.AllOnOffEvent;
import com.digitaldan.jomnilinkII.MessageTypes.systemevents.ButtonEvent;
import com.digitaldan.jomnilinkII.MessageTypes.systemevents.SwitchPressEvent;
import com.digitaldan.jomnilinkII.MessageTypes.systemevents.SystemEvent;
import com.digitaldan.jomnilinkII.MessageTypes.systemevents.UPBLinkEvent;
import com.digitaldan.jomnilinkII.NotificationListener;
import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
import com.digitaldan.jomnilinkII.OmniNotConnectedException;
import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
import com.google.gson.Gson;
/**
* The {@link OmnilinkBridgeHandler} defines some methods that are used to
* interface with an OmniLink Controller. This by extension also defines the
* OmniLink bridge that openHAB will be able to pick up and interface with.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class OmnilinkBridgeHandler extends BaseBridgeHandler implements NotificationListener, DisconnectListener {
private final Logger logger = LoggerFactory.getLogger(OmnilinkBridgeHandler.class);
private @Nullable Connection omniConnection = null;
private @Nullable ScheduledFuture<?> connectJob;
private @Nullable ScheduledFuture<?> eventPollingJob;
private final int autoReconnectPeriod = 60;
private Optional<AudioPlayer> audioPlayer = Optional.empty();
private @Nullable SystemType systemType = null;
private final Gson gson = new Gson();
private int eventLogNumber = 0;
public OmnilinkBridgeHandler(Bridge bridge) {
super(bridge);
}
@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Collections.singleton(OmnilinkDiscoveryService.class);
}
public void sendOmnilinkCommand(final int message, final int param1, final int param2)
throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
try {
getOmniConnection().controllerCommand(message, param1, param2);
} catch (IOException | OmniNotConnectedException e) {
setOfflineAndReconnect(e.getMessage());
throw new BridgeOfflineException(e);
}
}
public SecurityCodeValidation reqSecurityCodeValidation(int area, int digit1, int digit2, int digit3, int digit4)
throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
try {
return getOmniConnection().reqSecurityCodeValidation(area, digit1, digit2, digit3, digit4);
} catch (IOException | OmniNotConnectedException e) {
setOfflineAndReconnect(e.getMessage());
throw new BridgeOfflineException(e);
}
}
public void activateKeypadEmergency(int area, int emergencyType)
throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
try {
getOmniConnection().activateKeypadEmergency(area, emergencyType);
} catch (IOException | OmniNotConnectedException e) {
setOfflineAndReconnect(e.getMessage());
throw new BridgeOfflineException(e);
}
}
public SystemInformation reqSystemInformation()
throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
try {
return getOmniConnection().reqSystemInformation();
} catch (IOException | OmniNotConnectedException e) {
setOfflineAndReconnect(e.getMessage());
throw new BridgeOfflineException(e);
}
}
public SystemFormats reqSystemFormats()
throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
try {
return getOmniConnection().reqSystemFormats();
} catch (IOException | OmniNotConnectedException e) {
setOfflineAndReconnect(e.getMessage());
throw new BridgeOfflineException(e);
}
}
private SystemFeatures reqSystemFeatures()
throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
try {
return getOmniConnection().reqSystemFeatures();
} catch (IOException | OmniNotConnectedException e) {
setOfflineAndReconnect(e.getMessage());
throw new BridgeOfflineException(e);
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
if (command instanceof RefreshType) {
updateChannels();
return;
}
switch (channelUID.getId()) {
case CHANNEL_SYSTEMDATE:
if (command instanceof DateTimeType) {
ZonedDateTime zdt = ((DateTimeType) command).getZonedDateTime();
boolean inDaylightSavings = zdt.getZone().getRules().isDaylightSavings(zdt.toInstant());
try {
getOmniConnection().setTimeCommand(zdt.getYear() - 2000, zdt.getMonthValue(),
zdt.getDayOfMonth(), zdt.getDayOfWeek().getValue(), zdt.getHour(), zdt.getMinute(),
inDaylightSavings);
} catch (IOException | OmniNotConnectedException | OmniInvalidResponseException
| OmniUnknownMessageTypeException e) {
logger.debug("Could not send Set Time command to OmniLink Controller: {}", e.getMessage());
}
} else {
logger.debug("Invalid command: {}, must be DateTimeType", command);
}
break;
case CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER:
if (command instanceof StringType) {
try {
sendOmnilinkCommand(OmniLinkCmd.CMD_CONSOLE_ENABLE_DISABLE_BEEPER.getNumber(),
((StringType) command).equals(StringType.valueOf("OFF")) ? 0 : 1, 0);
updateState(CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER, UnDefType.UNDEF);
} catch (NumberFormatException | OmniInvalidResponseException | OmniUnknownMessageTypeException
| BridgeOfflineException e) {
logger.debug("Could not send Console command to OmniLink Controller: {}", e.getMessage());
}
} else {
logger.debug("Invalid command: {}, must be StringType", command);
}
break;
case CHANNEL_CONSOLE_BEEP:
if (command instanceof DecimalType) {
try {
sendOmnilinkCommand(OmniLinkCmd.CMD_CONSOLE_BEEP.getNumber(),
((DecimalType) command).intValue(), 0);
updateState(CHANNEL_CONSOLE_BEEP, UnDefType.UNDEF);
} catch (NumberFormatException | OmniInvalidResponseException | OmniUnknownMessageTypeException
| BridgeOfflineException e) {
logger.debug("Could not send Console command to OmniLink Controller: {}", e.getMessage());
}
} else {
logger.debug("Invalid command: {}, must be DecimalType", command);
}
break;
default:
logger.warn("Unknown channel for Bridge thing: {}", channelUID);
}
}
private void makeOmnilinkConnection() {
final Connection connection = omniConnection;
if (connection != null && connection.connected()) {
return;
}
logger.debug("Attempting to connect to controller!");
try {
OmnilinkBridgeConfig config = getConfigAs(OmnilinkBridgeConfig.class);
this.omniConnection = new Connection(config.getIpAddress(), config.getPort(),
config.getKey1() + ":" + config.getKey2());
/*
* HAI only supports one audio player - cycle through features until we find a feature that is an audio
* player.
*/
audioPlayer = reqSystemFeatures().getFeatures().stream()
.map(featureCode -> AudioPlayer.getAudioPlayerForFeatureCode(featureCode))
.filter(Optional::isPresent).findFirst().orElse(Optional.empty());
systemType = SystemType.getType(reqSystemInformation().getModel());
if (config.getLogPollingInterval() > 0) {
startEventPolling(config.getLogPollingInterval());
}
final Connection connectionNew = omniConnection;
if (connectionNew != null) {
connectionNew.enableNotifications();
connectionNew.addNotificationListener(OmnilinkBridgeHandler.this);
connectionNew.addDisconnectListener(this);
}
updateStatus(ThingStatus.ONLINE);
cancelReconnectJob(false);
updateChannels();
updateBridgeProperties();
} catch (UnknownHostException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
} catch (IOException e) {
final Throwable cause = e.getCause();
if (cause != null) {
final String causeMessage = cause.getMessage();
if (causeMessage != null && causeMessage.contains("Connection timed out")) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"IP Address probably incorrect, timed out creating connection!");
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, causeMessage);
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
} catch (Exception e) {
setOfflineAndReconnect(e.getMessage());
logger.debug("Error connecting to OmniLink Controller: {}", e.getMessage());
}
}
@Override
public void objectStatusNotification(@Nullable ObjectStatus objectStatus) {
if (objectStatus != null) {
Status[] statuses = objectStatus.getStatuses();
for (Status status : statuses) {
if (status instanceof ExtendedUnitStatus) {
ExtendedUnitStatus unitStatus = (ExtendedUnitStatus) status;
int unitNumber = unitStatus.getNumber();
logger.debug("Received status update for Unit: {}, status: {}", unitNumber, unitStatus);
Optional<Thing> theThing = getUnitThing(unitNumber);
theThing.map(Thing::getHandler)
.ifPresent(theHandler -> ((UnitHandler) theHandler).handleStatus(unitStatus));
} else if (status instanceof ExtendedZoneStatus) {
ExtendedZoneStatus zoneStatus = (ExtendedZoneStatus) status;
int zoneNumber = zoneStatus.getNumber();
logger.debug("Received status update for Zone: {}, status: {}", zoneNumber, zoneStatus);
Optional<Thing> theThing = getChildThing(THING_TYPE_ZONE, zoneNumber);
theThing.map(Thing::getHandler)
.ifPresent(theHandler -> ((ZoneHandler) theHandler).handleStatus(zoneStatus));
} else if (status instanceof ExtendedAreaStatus) {
final SystemType systemType = this.systemType;
ExtendedAreaStatus areaStatus = (ExtendedAreaStatus) status;
int areaNumber = areaStatus.getNumber();
if (systemType != null) {
logger.debug("Received status update for Area: {}, status: {}", areaNumber, areaStatus);
Optional<Thing> theThing;
switch (systemType) {
case OMNI:
theThing = getChildThing(THING_TYPE_OMNI_AREA, areaNumber);
break;
case LUMINA:
theThing = getChildThing(THING_TYPE_LUMINA_AREA, areaNumber);
break;
default:
theThing = Optional.empty();
}
theThing.map(Thing::getHandler)
.ifPresent(theHandler -> ((AbstractAreaHandler) theHandler).handleStatus(areaStatus));
} else {
logger.debug("Received null System Type!");
}
} else if (status instanceof ExtendedAccessControlReaderLockStatus) {
ExtendedAccessControlReaderLockStatus lockStatus = (ExtendedAccessControlReaderLockStatus) status;
int lockNumber = lockStatus.getNumber();
logger.debug("Received status update for Lock: {}, status: {}", lockNumber, lockStatus);
Optional<Thing> theThing = getChildThing(THING_TYPE_LOCK, lockNumber);
theThing.map(Thing::getHandler)
.ifPresent(theHandler -> ((LockHandler) theHandler).handleStatus(lockStatus));
} else if (status instanceof ExtendedThermostatStatus) {
ExtendedThermostatStatus thermostatStatus = (ExtendedThermostatStatus) status;
int thermostatNumber = thermostatStatus.getNumber();
logger.debug("Received status update for Thermostat: {}, status: {}", thermostatNumber,
thermostatStatus);
Optional<Thing> theThing = getChildThing(THING_TYPE_THERMOSTAT, thermostatNumber);
theThing.map(Thing::getHandler)
.ifPresent(theHandler -> ((ThermostatHandler) theHandler).handleStatus(thermostatStatus));
} else if (status instanceof ExtendedAudioZoneStatus) {
ExtendedAudioZoneStatus audioZoneStatus = (ExtendedAudioZoneStatus) status;
int audioZoneNumber = audioZoneStatus.getNumber();
logger.debug("Received status update for Audio Zone: {}, status: {}", audioZoneNumber,
audioZoneStatus);
Optional<Thing> theThing = getChildThing(THING_TYPE_AUDIO_ZONE, audioZoneNumber);
theThing.map(Thing::getHandler)
.ifPresent(theHandler -> ((AudioZoneHandler) theHandler).handleStatus(audioZoneStatus));
} else if (status instanceof ExtendedAuxSensorStatus) {
ExtendedAuxSensorStatus auxSensorStatus = (ExtendedAuxSensorStatus) status;
int auxSensorNumber = auxSensorStatus.getNumber();
// Aux Sensors can be either temperature or humidity, need to check both.
Optional<Thing> tempThing = getChildThing(THING_TYPE_TEMP_SENSOR, auxSensorNumber);
Optional<Thing> humidityThing = getChildThing(THING_TYPE_HUMIDITY_SENSOR, auxSensorNumber);
if (tempThing.isPresent()) {
logger.debug("Received status update for Temperature Sensor: {}, status: {}", auxSensorNumber,
auxSensorStatus);
tempThing.map(Thing::getHandler).ifPresent(
theHandler -> ((TempSensorHandler) theHandler).handleStatus(auxSensorStatus));
}
if (humidityThing.isPresent()) {
logger.debug("Received status update for Humidity Sensor: {}, status: {}", auxSensorNumber,
auxSensorStatus);
humidityThing.map(Thing::getHandler).ifPresent(
theHandler -> ((HumiditySensorHandler) theHandler).handleStatus(auxSensorStatus));
}
} else {
logger.debug("Received Object Status Notification that was not processed: {}", objectStatus);
}
}
} else {
logger.debug("Received null Object Status Notification!");
}
}
@Override
public void systemEventNotification(@Nullable SystemEvent event) {
if (event != null) {
logger.debug("Received System Event Notification of type: {}", event.getType());
switch (event.getType()) {
case PHONE_LINE_DEAD:
case PHONE_LINE_OFF_HOOK:
case PHONE_LINE_ON_HOOK:
case PHONE_LINE_RING:
ChannelUID channel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_PHONE_LINE_EVENT);
triggerChannel(channel, event.getType().toString().replaceAll("^PHONE_LINE_", ""));
break;
case AC_POWER_OFF:
case AC_POWER_RESTORED:
ChannelUID acChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_AC_POWER_EVENT);
triggerChannel(acChannel, event.getType().toString().replaceAll("^AC_POWER_", ""));
break;
case BATTERY_LOW:
case BATTERY_OK:
ChannelUID batteryChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_BATTERY_EVENT);
triggerChannel(batteryChannel, event.getType().toString().replaceAll("^BATTERY_", ""));
break;
case DCM_OK:
case DCM_TROUBLE:
ChannelUID dcmChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_DCM_EVENT);
triggerChannel(dcmChannel, event.getType().toString().replaceAll("^DCM_", ""));
break;
case ENERGY_COST_CRITICAL:
case ENERGY_COST_HIGH:
case ENERGY_COST_LOW:
case ENERGY_COST_MID:
ChannelUID energyChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_ENERGY_COST_EVENT);
triggerChannel(energyChannel, event.getType().toString().replaceAll("^ENERGY_COST_", ""));
break;
case CAMERA_1_TRIGGER:
case CAMERA_2_TRIGGER:
case CAMERA_3_TRIGGER:
case CAMERA_4_TRIGGER:
case CAMERA_5_TRIGGER:
case CAMERA_6_TRIGGER:
ChannelUID cameraChannel = new ChannelUID(getThing().getUID(),
TRIGGER_CHANNEL_CAMERA_TRIGGER_EVENT);
triggerChannel(cameraChannel, String.valueOf(event.getType().toString().charAt(8)));
break;
case BUTTON:
Optional<Thing> buttonThing = getChildThing(THING_TYPE_BUTTON,
((ButtonEvent) event).getButtonNumber());
buttonThing.map(Thing::getHandler)
.ifPresent(theHandler -> ((ButtonHandler) theHandler).buttonActivated());
break;
case ALL_ON_OFF:
Optional<Thing> areaThing = getChildThing(THING_TYPE_OMNI_AREA, ((AllOnOffEvent) event).getArea());
if (areaThing.isPresent()) {
logger.debug("Thing for allOnOff event: {}", areaThing.get().getUID());
areaThing.map(Thing::getHandler).ifPresent(theHandler -> ((AbstractAreaHandler) theHandler)
.handleAllOnOffEvent((AllOnOffEvent) event));
}
break;
case UPB_LINK:
UPBLinkEvent linkEvent = (UPBLinkEvent) event;
UPBLinkEvent.Command command = linkEvent.getLinkCommand();
int link = linkEvent.getLinkNumber();
handleUPBLink(link, command);
break;
case ALC_UPB_RADIORA_STARLITE_SWITCH_PRESS:
SwitchPressEvent switchPressEvent = (SwitchPressEvent) event;
int unitNumber = switchPressEvent.getUnitNumber();
Optional<Thing> unitThing = getUnitThing(unitNumber);
unitThing.map(Thing::getHandler).ifPresent(
theHandler -> ((UnitHandler) theHandler).handleSwitchPressEvent(switchPressEvent));
break;
default:
logger.warn("Ignoring System Event Notification of type: {}", event.getType());
}
} else {
logger.debug("Received null System Event Notification!");
}
}
private void handleUPBLink(int link, UPBLinkEvent.Command command) {
final ChannelUID activateChannel;
if (command == UPBLinkEvent.Command.ACTIVATED) {
activateChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_UPB_LINK_ACTIVATED_EVENT);
} else if (command == UPBLinkEvent.Command.DEACTIVATED) {
activateChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_UPB_LINK_DEACTIVATED_EVENT);
} else {
logger.debug("Received unsupported UPB link event: {}", command);
return;
}
triggerChannel(activateChannel, Integer.toString(link));
}
@Override
public void notConnectedEvent(@Nullable Exception e) {
if (e != null) {
logger.debug("Received an OmniLink Controller not connected event: {}", e.getMessage());
setOfflineAndReconnect(e.getMessage());
}
}
private void getSystemStatus() throws IOException, OmniNotConnectedException, OmniInvalidResponseException,
OmniUnknownMessageTypeException {
SystemStatus status = getOmniConnection().reqSystemStatus();
logger.debug("Received system status: {}", status);
// Let's update system time
String dateString = new StringBuilder().append(2000 + status.getYear()).append("-")
.append(String.format("%02d", status.getMonth())).append("-")
.append(String.format("%02d", status.getDay())).append("T")
.append(String.format("%02d", status.getHour())).append(":")
.append(String.format("%02d", status.getMinute())).append(":")
.append(String.format("%02d", status.getSecond())).toString();
updateState(CHANNEL_SYSTEMDATE, new DateTimeType(dateString));
}
public Message reqObjectProperties(int objectType, int objectNum, int direction, int filter1, int filter2,
int filter3) throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
try {
return getOmniConnection().reqObjectProperties(objectType, objectNum, direction, filter1, filter2, filter3);
} catch (OmniNotConnectedException | IOException e) {
setOfflineAndReconnect(e.getMessage());
throw new BridgeOfflineException(e);
}
}
public Message requestAudioSourceStatus(final int source, final int position)
throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
try {
return getOmniConnection().reqAudioSourceStatus(source, position);
} catch (OmniNotConnectedException | IOException e) {
setOfflineAndReconnect(e.getMessage());
throw new BridgeOfflineException(e);
}
}
public ObjectStatus requestObjectStatus(final int objType, final int startObject, final int endObject,
boolean extended)
throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
try {
return getOmniConnection().reqObjectStatus(objType, startObject, endObject, extended);
} catch (OmniNotConnectedException | IOException e) {
setOfflineAndReconnect(e.getMessage());
throw new BridgeOfflineException(e);
}
}
public Optional<TemperatureFormat> getTemperatureFormat() {
try {
return Optional.of(TemperatureFormat.valueOf(reqSystemFormats().getTempFormat()));
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Could not request temperature format from controller: {}", e.getMessage());
return Optional.empty();
}
}
public void updateChannels() {
try {
getSystemStatus();
updateState(CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER, UnDefType.UNDEF);
updateState(CHANNEL_CONSOLE_BEEP, UnDefType.UNDEF);
} catch (IOException | OmniNotConnectedException | OmniInvalidResponseException
| OmniUnknownMessageTypeException e) {
logger.warn("Unable to update bridge channels: {}", e.getMessage());
}
}
@Override
public void dispose() {
cancelReconnectJob(true);
cancelEventPolling();
final Connection connection = omniConnection;
if (connection != null) {
connection.removeDisconnectListener(this);
connection.disconnect();
}
}
private Optional<Thing> getChildThing(ThingTypeUID type, int number) {
Bridge bridge = getThing();
return bridge.getThings().stream().filter(t -> t.getThingTypeUID().equals(type))
.filter(t -> ((Number) t.getConfiguration().get(THING_PROPERTIES_NUMBER)).intValue() == number)
.findFirst();
}
private Optional<Thing> getUnitThing(int unitId) {
Optional<Thing> theThing = getChildThing(THING_TYPE_UNIT_UPB, unitId);
if (!(theThing.isPresent())) {
theThing = getChildThing(THING_TYPE_ROOM, unitId);
}
if (!(theThing.isPresent())) {
theThing = getChildThing(THING_TYPE_FLAG, unitId);
}
if (!(theThing.isPresent())) {
theThing = getChildThing(THING_TYPE_OUTPUT, unitId);
}
if (!(theThing.isPresent())) {
theThing = getChildThing(THING_TYPE_DIMMABLE, unitId);
}
if (!(theThing.isPresent())) {
theThing = getChildThing(THING_TYPE_UNIT, unitId);
}
return theThing;
}
public Optional<AudioPlayer> getAudioPlayer() {
return audioPlayer;
}
public Message readEventRecord(int eventNumber, int direction)
throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
try {
return getOmniConnection().readEventRecord(eventNumber, direction);
} catch (OmniNotConnectedException | IOException e) {
setOfflineAndReconnect(e.getMessage());
throw new BridgeOfflineException(e);
}
}
private void updateBridgeProperties() {
try {
SystemInformation systemInformation = reqSystemInformation();
Map<String, String> properties = editProperties();
properties.put(THING_PROPERTIES_MODEL_NUMBER, Integer.toString(systemInformation.getModel()));
properties.put(THING_PROPERTIES_MAJOR_VERSION, Integer.toString(systemInformation.getMajor()));
properties.put(THING_PROPERTIES_MINOR_VERSION, Integer.toString(systemInformation.getMinor()));
properties.put(THING_PROPERTIES_REVISION, Integer.toString(systemInformation.getRevision()));
properties.put(THING_PROPERTIES_PHONE_NUMBER, systemInformation.getPhone());
updateProperties(properties);
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Could not request system information from OmniLink Controller: {}", e.getMessage());
}
}
@Override
public void initialize() {
scheduleReconnectJob();
}
private void scheduleReconnectJob() {
ScheduledFuture<?> currentReconnectJob = connectJob;
if (currentReconnectJob == null || currentReconnectJob.isDone()) {
connectJob = super.scheduler.scheduleWithFixedDelay(this::makeOmnilinkConnection, 0, autoReconnectPeriod,
TimeUnit.SECONDS);
}
}
private void cancelReconnectJob(boolean kill) {
ScheduledFuture<?> currentReconnectJob = connectJob;
if (currentReconnectJob != null) {
currentReconnectJob.cancel(kill);
}
}
private void setOfflineAndReconnect(@Nullable String message) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
cancelEventPolling();
final Connection connection = omniConnection;
if (connection != null) {
connection.removeDisconnectListener(this);
}
scheduleReconnectJob();
}
private void startEventPolling(int interval) {
ScheduledFuture<?> eventPollingJobFuture = eventPollingJob;
if (eventPollingJobFuture == null || eventPollingJobFuture.isDone()) {
eventLogNumber = 0;
eventPollingJob = super.scheduler.scheduleWithFixedDelay(this::pollEvents, 0, interval, TimeUnit.SECONDS);
}
}
private void cancelEventPolling() {
ScheduledFuture<?> eventPollingJobFuture = eventPollingJob;
if (eventPollingJobFuture != null) {
eventPollingJobFuture.cancel(true);
}
}
private void pollEvents() {
// On first run, direction is -1 (most recent event), after its 1 for the next log message
try {
Message message;
do {
logger.trace("Polling for event log messages.");
int direction = eventLogNumber == 0 ? -1 : 1;
message = readEventRecord(eventLogNumber, direction);
if (message.getMessageType() == Message.MESG_TYPE_EVENT_LOG_DATA) {
EventLogData logData = (EventLogData) message;
logger.debug("Processing event log message number: {}", logData.getEventNumber());
eventLogNumber = logData.getEventNumber();
String json = gson.toJson(logData);
logger.debug("Receieved event log message: {}", json);
updateState(CHANNEL_EVENT_LOG, new StringType(json));
}
} while (message.getMessageType() != Message.MESG_TYPE_END_OF_DATA);
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Exception recieved while polling for event log messages: {}", e.getMessage());
}
}
private Connection getOmniConnection() throws OmniNotConnectedException {
final Connection connection = omniConnection;
if (connection != null) {
return connection;
} else {
throw new OmniNotConnectedException("Connection not yet established!");
}
}
}

View File

@ -0,0 +1,182 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.Message;
import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AuxSensorProperties;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAuxSensorStatus;
import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
/**
* The {@link TempSensorHandler} defines some methods that are used to interface
* with an OmniLink Temperature Sensor. This by extension also defines the
* Temperature Sensor thing that openHAB will be able to pick up and interface
* with.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class TempSensorHandler extends AbstractOmnilinkStatusHandler<ExtendedAuxSensorStatus> {
private final Logger logger = LoggerFactory.getLogger(TempSensorHandler.class);
private final int thingID = getThingNumber();
public @Nullable String number;
public TempSensorHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
super.initialize();
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
updateTempSensorProperties(bridgeHandler);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Received null bridge while initializing Temperature Sensor!");
}
}
private void updateTempSensorProperties(OmnilinkBridgeHandler bridgeHandler) {
final List<AreaProperties> areas = getAreaProperties();
if (areas != null) {
for (AreaProperties areaProperties : areas) {
int areaFilter = bitFilterForArea(areaProperties);
ObjectPropertyRequest<AuxSensorProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(bridgeHandler, ObjectPropertyRequests.AUX_SENSORS, thingID, 0).selectNamed()
.areaFilter(areaFilter).build();
for (AuxSensorProperties auxSensorProperties : objectPropertyRequest) {
Map<String, String> properties = editProperties();
properties.put(THING_PROPERTIES_NAME, auxSensorProperties.getName());
properties.put(THING_PROPERTIES_AREA, Integer.toString(areaProperties.getNumber()));
updateProperties(properties);
}
}
}
}
@Override
@SuppressWarnings("unchecked")
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
Optional<TemperatureFormat> temperatureFormat = Optional.empty();
if (command instanceof RefreshType) {
retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null staus update!"));
return;
}
if (!(command instanceof QuantityType)) {
logger.debug("Invalid command: {}, must be QuantityType", command);
return;
}
if (bridgeHandler != null) {
temperatureFormat = bridgeHandler.getTemperatureFormat();
if (!temperatureFormat.isPresent()) {
logger.warn("Receieved null temperature format!");
return;
}
} else {
logger.warn("Could not connect to Bridge, failed to get temperature format!");
return;
}
switch (channelUID.getId()) {
case CHANNEL_AUX_LOW_SETPOINT:
sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HEAT_LOW_POINT.getNumber(),
temperatureFormat.get().formatToOmni(((QuantityType<Temperature>) command).intValue()),
thingID);
break;
case CHANNEL_AUX_HIGH_SETPOINT:
sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_COOL_HIGH_POINT.getNumber(),
temperatureFormat.get().formatToOmni(((QuantityType<Temperature>) command).intValue()),
thingID);
break;
default:
logger.warn("Unknown channel for Temperature Sensor thing: {}", channelUID);
}
}
@Override
public void updateChannels(ExtendedAuxSensorStatus status) {
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
Optional<TemperatureFormat> temperatureFormat = bridgeHandler.getTemperatureFormat();
if (temperatureFormat.isPresent()) {
updateState(CHANNEL_AUX_TEMP, new QuantityType<>(
temperatureFormat.get().omniToFormat(status.getTemperature()),
temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
updateState(CHANNEL_AUX_LOW_SETPOINT, new QuantityType<>(
temperatureFormat.get().omniToFormat(status.getCoolSetpoint()),
temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
updateState(CHANNEL_AUX_HIGH_SETPOINT, new QuantityType<>(
temperatureFormat.get().omniToFormat(status.getHeatSetpoint()),
temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
} else {
logger.warn("Receieved null temperature format, could not update Temperature Sensor channels!");
}
} else {
logger.debug("Received null bridge while updating Temperature Sensor channels!");
}
}
@Override
protected Optional<ExtendedAuxSensorStatus> retrieveStatus() {
try {
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
ObjectStatus objStatus = bridgeHandler.requestObjectStatus(Message.OBJ_TYPE_AUX_SENSOR, thingID,
thingID, true);
return Optional.of((ExtendedAuxSensorStatus) objStatus.getStatuses()[0]);
} else {
logger.debug("Received null bridge while updating Temperature Sensor status!");
return Optional.empty();
}
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Received exception while refreshing Temperature Sensor status: {}", e.getMessage());
return Optional.empty();
}
}
}

View File

@ -0,0 +1,92 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.digitaldan.jomnilinkII.MessageUtils;
/**
* The {@link TemperatureFormat} defines some methods that are used to
* convert OmniLink temperature values into Fahrenheit or Celsius.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public enum TemperatureFormat {
// Don't convert zero - it appears that is what omni returns when there is no value.
CELSIUS(2) {
@Override
public float omniToFormat(int omniNumber) {
return MessageUtils.omniToC(omniNumber);
}
@Override
public int formatToOmni(int celsius) {
return MessageUtils.CToOmni(celsius);
}
},
FAHRENHEIT(1) {
@Override
public float omniToFormat(int omniNumber) {
return MessageUtils.omniToF(omniNumber);
}
@Override
public int formatToOmni(int fahrenheit) {
return MessageUtils.FtoOmni(fahrenheit);
}
};
private final int formatNumber;
private TemperatureFormat(int formatNumber) {
this.formatNumber = formatNumber;
}
/**
* Convert a number represented by the omni to the format.
*
* @param omniNumber Number to convert
* @return Number converted to appropriate format.
*/
public abstract float omniToFormat(int omniNumber);
/**
* Convert a number from this format into an omni number.
*
* @param format Number in the current format.
* @return Omni formatted number.
*/
public abstract int formatToOmni(int format);
/**
* Get the number which identifies this format as defined by the omniprotocol.
*
* @return Number which identifies this temperature format.
*/
public int getFormatNumber() {
return formatNumber;
}
public static TemperatureFormat valueOf(int tempFormat) {
if (tempFormat == CELSIUS.formatNumber) {
return CELSIUS;
} else if (tempFormat == FAHRENHEIT.formatNumber) {
return FAHRENHEIT;
} else {
throw new IllegalArgumentException("Invalid temperature format!");
}
}
}

View File

@ -0,0 +1,265 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.Message;
import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.ThermostatProperties;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedThermostatStatus;
import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
/**
* The {@link ThermostatHandler} defines some methods that are used to
* interface with an OmniLink Thermostat. This by extension also defines the
* Thermostat thing that openHAB will be able to pick up and interface with.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class ThermostatHandler extends AbstractOmnilinkStatusHandler<ExtendedThermostatStatus> {
private final Logger logger = LoggerFactory.getLogger(ThermostatHandler.class);
private final int thingID = getThingNumber();
public @Nullable String number;
private enum ThermostatStatus {
HEATING(0, 1),
COOLING(1, 2),
HUMIDIFYING(2, 3),
DEHUMIDIFYING(3, 4);
private final int bit;
private final int modeValue;
private ThermostatStatus(int bit, int modeValue) {
this.bit = bit;
this.modeValue = modeValue;
}
}
public ThermostatHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
super.initialize();
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
updateThermostatProperties(bridgeHandler);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Received null bridge while initializing Thermostat!");
}
}
private void updateThermostatProperties(OmnilinkBridgeHandler bridgeHandler) {
final List<AreaProperties> areas = getAreaProperties();
if (areas != null) {
for (AreaProperties areaProperties : areas) {
int areaFilter = bitFilterForArea(areaProperties);
ObjectPropertyRequest<ThermostatProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(bridgeHandler, ObjectPropertyRequests.THERMOSTAT, thingID, 0).selectNamed()
.areaFilter(areaFilter).build();
for (ThermostatProperties thermostatProperties : objectPropertyRequest) {
Map<String, String> properties = editProperties();
properties.put(THING_PROPERTIES_NAME, thermostatProperties.getName());
properties.put(THING_PROPERTIES_AREA, Integer.toString(areaProperties.getNumber()));
updateProperties(properties);
}
}
}
}
@Override
@SuppressWarnings("unchecked")
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
Optional<TemperatureFormat> temperatureFormat = Optional.empty();
if (command instanceof RefreshType) {
retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null staus update!"));
return;
}
if (!(command instanceof DecimalType) && !(command instanceof QuantityType)) {
logger.debug("Invalid command: {}, must be DecimalType or QuantityType", command);
return;
}
if (bridgeHandler != null) {
temperatureFormat = bridgeHandler.getTemperatureFormat();
if (!temperatureFormat.isPresent()) {
logger.warn("Receieved null temperature format!");
return;
}
} else {
logger.warn("Could not connect to Bridge, failed to get temperature format!");
return;
}
switch (channelUID.getId()) {
case CHANNEL_THERMO_SYSTEM_MODE:
sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_SYSTEM_MODE.getNumber(),
((DecimalType) command).intValue(), thingID);
break;
case CHANNEL_THERMO_FAN_MODE:
sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_FAN_MODE.getNumber(), ((DecimalType) command).intValue(),
thingID);
break;
case CHANNEL_THERMO_HOLD_STATUS:
sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HOLD_MODE.getNumber(),
((DecimalType) command).intValue(), thingID);
break;
case CHANNEL_THERMO_HEAT_SETPOINT:
sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HEAT_LOW_POINT.getNumber(),
temperatureFormat.get().formatToOmni(((QuantityType<Temperature>) command).intValue()),
thingID);
break;
case CHANNEL_THERMO_COOL_SETPOINT:
sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_COOL_HIGH_POINT.getNumber(),
temperatureFormat.get().formatToOmni(((QuantityType<Temperature>) command).intValue()),
thingID);
break;
case CHANNEL_THERMO_HUMIDIFY_SETPOINT:
sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HUMDIFY_POINT.getNumber(),
TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType<Dimensionless>) command).intValue()),
thingID);
break;
case CHANNEL_THERMO_DEHUMIDIFY_SETPOINT:
sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_DEHUMIDIFY_POINT.getNumber(),
TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType<Dimensionless>) command).intValue()),
thingID);
break;
default:
logger.warn("Unknown channel for Thermostat thing: {}", channelUID);
}
}
@Override
protected void updateChannels(ExtendedThermostatStatus status) {
logger.debug("updateChannels called for Thermostat status: {}", status);
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
// Thermostat communication status
BigInteger thermostatAlarms = BigInteger.valueOf(status.getStatus());
updateState(CHANNEL_THERMO_COMM_FAILURE,
thermostatAlarms.testBit(0) ? OpenClosedType.CLOSED : OpenClosedType.OPEN);
updateState(CHANNEL_THERMO_FREEZE_ALARM,
thermostatAlarms.testBit(1) ? OpenClosedType.CLOSED : OpenClosedType.OPEN);
// Thermostat operation status
BigInteger thermostatStatus = BigInteger.valueOf(status.getExtendedStatus());
if (thermostatStatus.testBit(ThermostatStatus.HEATING.bit)) {
updateState(CHANNEL_THERMO_STATUS, new DecimalType(ThermostatStatus.HEATING.modeValue));
} else if (thermostatStatus.testBit(ThermostatStatus.COOLING.bit)) {
updateState(CHANNEL_THERMO_STATUS, new DecimalType(ThermostatStatus.COOLING.modeValue));
} else if (thermostatStatus.testBit(ThermostatStatus.HUMIDIFYING.bit)) {
updateState(CHANNEL_THERMO_STATUS, new DecimalType(ThermostatStatus.HUMIDIFYING.modeValue));
} else if (thermostatStatus.testBit(ThermostatStatus.DEHUMIDIFYING.bit)) {
updateState(CHANNEL_THERMO_STATUS, new DecimalType(ThermostatStatus.DEHUMIDIFYING.modeValue));
} else {
updateState(CHANNEL_THERMO_STATUS, new DecimalType(0));
}
// Thermostat temperature status
if (bridgeHandler != null) {
Optional<TemperatureFormat> temperatureFormat = bridgeHandler.getTemperatureFormat();
if (temperatureFormat.isPresent()) {
updateState(CHANNEL_THERMO_CURRENT_TEMP, new QuantityType<>(
temperatureFormat.get().omniToFormat(status.getCurrentTemperature()),
temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
updateState(CHANNEL_THERMO_OUTDOOR_TEMP, new QuantityType<>(
temperatureFormat.get().omniToFormat(status.getOutdoorTemperature()),
temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
updateState(CHANNEL_THERMO_COOL_SETPOINT, new QuantityType<>(
temperatureFormat.get().omniToFormat(status.getCoolSetpoint()),
temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
updateState(CHANNEL_THERMO_HEAT_SETPOINT, new QuantityType<>(
temperatureFormat.get().omniToFormat(status.getHeatSetpoint()),
temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
} else {
logger.warn("Receieved null temperature format, could not update Thermostat channels!");
}
} else {
logger.warn("Could not connect to Bridge, failed to get temperature format!");
return;
}
// Thermostat humidity status
updateState(CHANNEL_THERMO_HUMIDITY, new QuantityType<>(
TemperatureFormat.FAHRENHEIT.omniToFormat(status.getCurrentHumidity()), Units.PERCENT));
updateState(CHANNEL_THERMO_HUMIDIFY_SETPOINT, new QuantityType<>(
TemperatureFormat.FAHRENHEIT.omniToFormat(status.getHumidifySetpoint()), Units.PERCENT));
updateState(CHANNEL_THERMO_DEHUMIDIFY_SETPOINT, new QuantityType<>(
TemperatureFormat.FAHRENHEIT.omniToFormat(status.getDehumidifySetpoint()), Units.PERCENT));
// Thermostat mode, fan, and hold status
updateState(CHANNEL_THERMO_SYSTEM_MODE, new DecimalType(status.getSystemMode()));
updateState(CHANNEL_THERMO_FAN_MODE, new DecimalType(status.getFanMode()));
updateState(CHANNEL_THERMO_HOLD_STATUS,
new DecimalType(status.getHoldStatus() > 2 ? 1 : status.getHoldStatus()));
}
@Override
protected Optional<ExtendedThermostatStatus> retrieveStatus() {
try {
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
ObjectStatus objStatus = bridgeHandler.requestObjectStatus(Message.OBJ_TYPE_THERMO, thingID, thingID,
true);
return Optional.of((ExtendedThermostatStatus) objStatus.getStatuses()[0]);
} else {
logger.debug("Received null bridge while updating Thermostat status!");
return Optional.empty();
}
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Received exception while refreshing Thermostat status: {}", e.getMessage());
return Optional.empty();
}
}
}

View File

@ -0,0 +1,214 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.Message;
import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.UnitProperties;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedUnitStatus;
import com.digitaldan.jomnilinkII.MessageTypes.systemevents.SwitchPressEvent;
import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
/**
* The {@link AbstractOmnilinkHandler} defines some methods that can be used across
* the many different Units exposed by the OmniLink protocol
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class UnitHandler extends AbstractOmnilinkStatusHandler<ExtendedUnitStatus> {
private final Logger logger = LoggerFactory.getLogger(UnitHandler.class);
private final int thingID = getThingNumber();
public @Nullable String number;
public UnitHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
super.initialize();
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
updateUnitProperties(bridgeHandler);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Received null bridge while initializing Unit!");
}
}
private void updateUnitProperties(OmnilinkBridgeHandler bridgeHandler) {
final List<AreaProperties> areas = getAreaProperties();
if (areas != null) {
for (AreaProperties areaProperties : areas) {
int areaFilter = bitFilterForArea(areaProperties);
ObjectPropertyRequest<UnitProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(bridgeHandler, ObjectPropertyRequests.UNIT, thingID, 0).selectNamed()
.areaFilter(areaFilter).selectAnyLoad().build();
for (UnitProperties unitProperties : objectPropertyRequest) {
Map<String, String> properties = editProperties();
properties.put(THING_PROPERTIES_NAME, unitProperties.getName());
properties.put(THING_PROPERTIES_AREA, Integer.toString(areaProperties.getNumber()));
updateProperties(properties);
}
}
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
if (command instanceof RefreshType) {
retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null staus update!"));
return;
}
switch (channelUID.getId()) {
case CHANNEL_UNIT_LEVEL:
case CHANNEL_UNIT_SWITCH:
if (command instanceof OnOffType) {
handleOnOff(channelUID, (OnOffType) command);
} else {
logger.debug("Invalid command: {}, must be OnOffType", command);
}
break;
case CHANNEL_UNIT_ON_FOR_SECONDS:
case CHANNEL_UNIT_OFF_FOR_SECONDS:
case CHANNEL_UNIT_ON_FOR_MINUTES:
case CHANNEL_UNIT_OFF_FOR_MINUTES:
case CHANNEL_UNIT_ON_FOR_HOURS:
case CHANNEL_UNIT_OFF_FOR_HOURS:
if (command instanceof DecimalType) {
handleUnitDuration(channelUID, (DecimalType) command);
} else {
logger.debug("Invalid command: {}, must be DecimalType", command);
}
break;
default:
logger.warn("Unknown channel for Unit thing: {}", channelUID);
}
}
private void handleUnitDuration(ChannelUID channelUID, DecimalType command) {
logger.debug("handleUnitDuration called for channel: {}, command: {}", channelUID, command);
final String channelID = channelUID.getId();
int duration;
if (channelID.endsWith("seconds")) {
duration = command.intValue();
} else if (channelID.endsWith("minutes")) {
duration = command.intValue() + 100;
} else if (channelID.endsWith("hours")) {
duration = command.intValue() + 200;
} else {
logger.warn("Unknown channel for Unit duration: {}", channelUID);
return;
}
sendOmnilinkCommand(
channelID.startsWith("on") ? OmniLinkCmd.CMD_UNIT_ON.getNumber() : OmniLinkCmd.CMD_UNIT_OFF.getNumber(),
duration, thingID);
}
protected void handleOnOff(ChannelUID channelUID, OnOffType command) {
logger.debug("handleOnOff called for channel: {}, command: {}", channelUID, command);
sendOmnilinkCommand(OnOffType.ON.equals(command) ? OmniLinkCmd.CMD_UNIT_ON.getNumber()
: OmniLinkCmd.CMD_UNIT_OFF.getNumber(), 0, thingID);
}
@Override
public void updateChannels(ExtendedUnitStatus status) {
logger.debug("updateChannels called for Unit status: {}", status);
int unitStatus = status.getStatus();
int level = 0;
if (unitStatus == Status.UNIT_OFF) {
level = 0;
} else if (unitStatus == Status.UNIT_ON) {
level = 100;
} else if ((unitStatus >= Status.UNIT_SCENE_A) && (unitStatus <= Status.UNIT_SCENE_L)) {
level = 100;
} else if ((unitStatus >= Status.UNIT_LEVEL_0) && (unitStatus <= Status.UNIT_LEVEL_100)) {
level = unitStatus - Status.UNIT_LEVEL_0;
}
updateState(CHANNEL_UNIT_LEVEL, PercentType.valueOf(Integer.toString(level)));
updateState(CHANNEL_UNIT_SWITCH, OnOffType.from(level != 0));
}
@Override
protected Optional<ExtendedUnitStatus> retrieveStatus() {
try {
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
ObjectStatus objectStatus = bridgeHandler.requestObjectStatus(Message.OBJ_TYPE_UNIT, thingID, thingID,
true);
return Optional.of((ExtendedUnitStatus) objectStatus.getStatuses()[0]);
} else {
logger.debug("Received null bridge while updating Unit status!");
return Optional.empty();
}
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Received exception while refreshing Unit status: {}", e.getMessage());
return Optional.empty();
}
}
private static class Status {
private static final int UNIT_OFF = 0;
private static final int UNIT_ON = 1;
private static final int UNIT_SCENE_A = 2;
private static final int UNIT_SCENE_L = 13;
private static final int UNIT_LEVEL_0 = 100;
private static final int UNIT_LEVEL_100 = 200;
}
/**
* Handle a switch press event by triggering the appropriate channel.
*
* @param switchPressEvent
*/
public void handleSwitchPressEvent(SwitchPressEvent switchPressEvent) {
ChannelUID activateChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_SWITCH_PRESS_EVENT);
triggerChannel(activateChannel, Integer.toString(switchPressEvent.getSwitchValue()));
}
}

View File

@ -0,0 +1,201 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler;
import static com.digitaldan.jomnilinkII.MessageTypes.properties.AuxSensorProperties.SENSOR_TYPE_PROGRAMMABLE_ENERGY_SAVER_MODULE;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest;
import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.Message;
import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
import com.digitaldan.jomnilinkII.MessageTypes.SecurityCodeValidation;
import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
import com.digitaldan.jomnilinkII.MessageTypes.properties.ZoneProperties;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedZoneStatus;
import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
/**
* The {@link ZoneHandler} defines some methods that are used to
* interface with an OmniLink Zone. This by extension also defines the
* OmniPro Zone thing that openHAB will be able to pick up and interface with.
*
* @author Craig Hamilton - Initial contribution
*/
@NonNullByDefault
public class ZoneHandler extends AbstractOmnilinkStatusHandler<ExtendedZoneStatus> {
private final Logger logger = LoggerFactory.getLogger(ZoneHandler.class);
private final int thingID = getThingNumber();
public @Nullable String number;
public ZoneHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
super.initialize();
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
if (bridgeHandler != null) {
updateZoneProperties(bridgeHandler);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Received null bridge while initializing Zone!");
}
}
private void updateZoneProperties(OmnilinkBridgeHandler bridgeHandler) {
final List<AreaProperties> areas = super.getAreaProperties();
if (areas != null) {
for (AreaProperties areaProperties : areas) {
int areaFilter = super.bitFilterForArea(areaProperties);
ObjectPropertyRequest<ZoneProperties> objectPropertyRequest = ObjectPropertyRequest
.builder(bridgeHandler, ObjectPropertyRequests.ZONE, getThingNumber(), 0).selectNamed()
.areaFilter(areaFilter).build();
for (ZoneProperties zoneProperties : objectPropertyRequest) {
if (zoneProperties.getZoneType() <= SENSOR_TYPE_PROGRAMMABLE_ENERGY_SAVER_MODULE) {
Map<String, String> properties = editProperties();
properties.put(THING_PROPERTIES_NAME, zoneProperties.getName());
properties.put(THING_PROPERTIES_AREA, Integer.toString(areaProperties.getNumber()));
updateProperties(properties);
}
}
}
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
int mode;
if (command instanceof RefreshType) {
retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null staus update!"));
return;
}
if (!(command instanceof StringType)) {
logger.debug("Invalid command: {}, must be StringType", command);
return;
}
switch (channelUID.getId()) {
case CHANNEL_ZONE_BYPASS:
mode = OmniLinkCmd.CMD_SECURITY_BYPASS_ZONE.getNumber();
break;
case CHANNEL_ZONE_RESTORE:
mode = OmniLinkCmd.CMD_SECURITY_RESTORE_ZONE.getNumber();
break;
default:
mode = -1;
}
int areaNumber = getAreaNumber();
logger.debug("mode {} on zone {} with code {}", mode, thingID, command.toFullString());
char[] code = command.toFullString().toCharArray();
if (code.length != 4) {
logger.warn("Invalid code length, code must be 4 digits");
} else {
try {
final OmnilinkBridgeHandler bridge = getOmnilinkBridgeHandler();
if (bridge != null) {
SecurityCodeValidation codeValidation = bridge.reqSecurityCodeValidation(areaNumber,
Character.getNumericValue(code[0]), Character.getNumericValue(code[1]),
Character.getNumericValue(code[2]), Character.getNumericValue(code[3]));
/*
* 0 Invalid code
* 1 Master
* 2 Manager
* 3 User
*/
logger.debug("User code number: {} level: {}", codeValidation.getCodeNumber(),
codeValidation.getAuthorityLevel());
/*
* Valid user code number are 1-99, 251 is duress code, 0 means code does not exist
*/
if ((codeValidation.getCodeNumber() > 0 && codeValidation.getCodeNumber() <= 99)
&& codeValidation.getAuthorityLevel() > 0) {
sendOmnilinkCommand(mode, codeValidation.getCodeNumber(), thingID);
} else {
logger.warn("System reported an invalid code");
}
} else {
logger.debug("Received null bridge while sending zone command!");
}
} catch (OmniInvalidResponseException e) {
logger.debug("Zone command failed: {}", e.getMessage());
} catch (OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Could not send zone command: {}", e.getMessage());
}
}
// This is a send only channel, so don't store the user code
updateState(channelUID, UnDefType.UNDEF);
}
@Override
protected void updateChannels(ExtendedZoneStatus zoneStatus) {
// 0 Secure. 1 Not ready, 3 Trouble
int current = ((zoneStatus.getStatus() >> 0) & 0x03);
// 0 Secure, 1 Tripped, 2 Reset, but previously tripped
int latched = ((zoneStatus.getStatus() >> 2) & 0x03);
// 0 Disarmed, 1 Armed, 2 Bypass user, 3 Bypass system
int arming = ((zoneStatus.getStatus() >> 4) & 0x03);
State contactState = Integer.valueOf(current).equals(0) ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
logger.debug("handling Zone Status change to state: {}, current: {}, latched: {}, arming: {}", contactState,
current, latched, arming);
updateState(CHANNEL_ZONE_CONTACT, contactState);
updateState(CHANNEL_ZONE_CURRENT_CONDITION, new DecimalType(current));
updateState(CHANNEL_ZONE_LATCHED_ALARM_STATUS, new DecimalType(latched));
updateState(CHANNEL_ZONE_ARMING_STATUS, new DecimalType(arming));
}
@Override
protected Optional<ExtendedZoneStatus> retrieveStatus() {
try {
final OmnilinkBridgeHandler bridge = getOmnilinkBridgeHandler();
if (bridge != null) {
ObjectStatus objStatus = bridge.requestObjectStatus(Message.OBJ_TYPE_ZONE, thingID, thingID, true);
return Optional.of((ExtendedZoneStatus) objStatus.getStatuses()[0]);
} else {
logger.debug("Received null bridge while updating Zone status!");
return Optional.empty();
}
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
logger.debug("Received exception while refreshing Zone status: {}", e.getMessage());
return Optional.empty();
}
}
}

View File

@ -0,0 +1,93 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler.units;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.CHANNEL_UNIT_LEVEL;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.handler.OmniLinkCmd;
import org.openhab.binding.omnilink.internal.handler.UnitHandler;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link DimmableUnitHandler} defines some methods that are used to
* interface with an OmniLink Dimmable Unit. This by extension also defines the
* Dimmable Unit things that openHAB will be able to pick up and interface with.
*
* @author Brian O'Connell - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class DimmableUnitHandler extends UnitHandler {
private final Logger logger = LoggerFactory.getLogger(DimmableUnitHandler.class);
private final int thingID = getThingNumber();
public @Nullable String number;
public DimmableUnitHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
switch (channelUID.getId()) {
case CHANNEL_UNIT_LEVEL:
handleUnitLevel(channelUID, command);
break;
default:
logger.debug("Unknown channel for Dimmable Unit thing: {}", channelUID);
super.handleCommand(channelUID, command);
}
}
private void handleUnitLevel(ChannelUID channelUID, Command command) {
logger.debug("handleUnitLevel called for channel: {}, command: {}", channelUID, command);
if (command instanceof PercentType) {
handlePercent(channelUID, (PercentType) command);
} else if (command instanceof IncreaseDecreaseType) {
handleIncreaseDecrease(channelUID, (IncreaseDecreaseType) command);
} else {
// Only handle percent or increase/decrease.
super.handleCommand(channelUID, command);
}
}
private void handlePercent(ChannelUID channelUID, PercentType command) {
logger.debug("handlePercent called for channel: {}, command: {}", channelUID, command);
int lightLevel = command.intValue();
if (lightLevel == 0) {
super.handleOnOff(channelUID, OnOffType.OFF);
} else if (lightLevel == 100) {
super.handleOnOff(channelUID, OnOffType.ON);
} else {
sendOmnilinkCommand(OmniLinkCmd.CMD_UNIT_PERCENT.getNumber(), lightLevel, thingID);
}
}
private void handleIncreaseDecrease(ChannelUID channelUID, IncreaseDecreaseType command) {
logger.debug("handleIncreaseDecrease called for channel: {}, command: {}", channelUID, command);
sendOmnilinkCommand(
IncreaseDecreaseType.INCREASE.equals(command) ? OmniLinkCmd.CMD_UNIT_UNIT_BRIGHTEN_STEP_1.getNumber()
: OmniLinkCmd.CMD_UNIT_UNIT_DIM_STEP_1.getNumber(),
0, thingID);
}
}

View File

@ -0,0 +1,90 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler.units;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.handler.OmniLinkCmd;
import org.openhab.binding.omnilink.internal.handler.UnitHandler;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedUnitStatus;
/**
* The {@link FlagHandler} defines some methods that are used to
* interface with an OmniLink Flag. This by extension also defines the
* Flag thing that openHAB will be able to pick up and interface with.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class FlagHandler extends UnitHandler {
private final Logger logger = LoggerFactory.getLogger(FlagHandler.class);
private final int thingID = getThingNumber();
public @Nullable String number;
public FlagHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
if (command instanceof RefreshType) {
retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null staus update!"));
return;
}
switch (channelUID.getId()) {
case CHANNEL_FLAG_VALUE:
if (command instanceof DecimalType) {
sendOmnilinkCommand(OmniLinkCmd.CMD_UNIT_SET_COUNTER.getNumber(),
((DecimalType) command).intValue(), thingID);
} else {
logger.debug("Invalid command: {}, must be DecimalType", command);
}
break;
case CHANNEL_FLAG_SWITCH:
if (command instanceof OnOffType) {
handleOnOff(channelUID, (OnOffType) command);
} else {
logger.debug("Invalid command: {}, must be OnOffType", command);
}
break;
default:
logger.debug("Unknown channel for Flag thing: {}", channelUID);
super.handleCommand(channelUID, command);
}
}
@Override
public void updateChannels(ExtendedUnitStatus status) {
logger.debug("updateChannels called for Flag status: {}", status);
updateState(CHANNEL_FLAG_VALUE, DecimalType.valueOf(Integer.toString(status.getStatus())));
updateState(CHANNEL_FLAG_SWITCH, OnOffType.from(status.getStatus() == 1));
}
}

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler.units;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.handler.UnitHandler;
import org.openhab.core.thing.Thing;
/**
* The {@link OutputHandler} defines some methods that are used to
* interface with an OmniLink Output. This by extension also defines the
* Output thing that openHAB will be able to pick up and interface with.
*
* @author Brian O'Connell - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class OutputHandler extends UnitHandler {
public @Nullable String number;
public OutputHandler(Thing thing) {
super(thing);
}
}

View File

@ -0,0 +1,168 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler.units;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.handler.OmniLinkCmd;
import org.openhab.binding.omnilink.internal.handler.UnitHandler;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedUnitStatus;
/**
* The {@link UpbRoomHandler} defines some methods that are used to
* interface with an OmniLink UPB Room. This by extension also defines the
* OmniPro UPB Room thing that openHAB will be able to pick up and interface with.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class UpbRoomHandler extends UnitHandler {
private final Logger logger = LoggerFactory.getLogger(UpbRoomHandler.class);
private final int thingID = getThingNumber();
public @Nullable String number;
public UpbRoomHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
if (command instanceof RefreshType) {
retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null staus update!"));
return;
}
switch (channelUID.getId()) {
case CHANNEL_ROOM_SCENE_A:
case CHANNEL_ROOM_SCENE_B:
case CHANNEL_ROOM_SCENE_C:
case CHANNEL_ROOM_SCENE_D:
if (command instanceof OnOffType) {
handleRoomScene(channelUID, (OnOffType) command);
} else {
logger.debug("Invalid command: {}, must be OnOffType", command);
}
break;
case CHANNEL_ROOM_SWITCH:
if (command instanceof OnOffType) {
super.handleOnOff(channelUID, (OnOffType) command);
} else {
logger.debug("Invalid command: {}, must be OnOffType", command);
}
break;
case CHANNEL_ROOM_STATE:
if (command instanceof DecimalType) {
handleRoomState(channelUID, (DecimalType) command);
} else {
logger.debug("Invalid command: {}, must be DecimalType", command);
}
break;
default:
logger.debug("Unknown channel for UPB Room thing: {}", channelUID);
super.handleCommand(channelUID, command);
}
}
private void handleRoomScene(ChannelUID channelUID, OnOffType command) {
logger.debug("handleRoomScene called for channel: {}, command: {}", channelUID, command);
int linkNum;
switch (channelUID.getId()) {
case "scene_a":
linkNum = 0;
break;
case "scene_b":
linkNum = 1;
break;
case "scene_c":
linkNum = 2;
break;
case "scene_d":
linkNum = 3;
break;
default:
logger.warn("Unexpected UPB Room scene: {}", channelUID);
return;
}
int roomNum = (thingID + 7) / 8;
int param2 = ((roomNum * 6) - 3) + linkNum;
sendOmnilinkCommand(OnOffType.ON.equals(command) ? OmniLinkCmd.CMD_UNIT_UPB_LINK_ON.getNumber()
: OmniLinkCmd.CMD_UNIT_UPB_LINK_OFF.getNumber(), 0, param2);
}
private void handleRoomState(ChannelUID channelUID, DecimalType command) {
logger.debug("handleRoomState called for channel: {}, command: {}", channelUID, command);
final int cmdValue = command.intValue();
int cmd;
int param2;
switch (cmdValue) {
case 0:
cmd = OmniLinkCmd.CMD_UNIT_OFF.getNumber();
param2 = thingID;
break;
case 1:
cmd = OmniLinkCmd.CMD_UNIT_ON.getNumber();
param2 = thingID;
break;
case 2:
case 3:
case 4:
case 5:
cmd = OmniLinkCmd.CMD_UNIT_UPB_LINK_ON.getNumber();
/*
* A little magic with the link #'s: 0 and 1 are off and on, respectively.
* So A ends up being 2, but OmniLink Protocol expects an offset of 0. That
* is why we subtract the 2.
*/
int roomNum = (thingID + 7) / 8;
param2 = ((roomNum * 6) - 3) + cmdValue - 2;
break;
default:
logger.warn("Unexpected UPB Room state: {}", Integer.toString(cmdValue));
return;
}
sendOmnilinkCommand(cmd, 0, param2);
}
@Override
public void updateChannels(ExtendedUnitStatus status) {
logger.debug("updateChannels called for UPB Room status: {}", status);
int unitStatus = status.getStatus();
updateState(CHANNEL_ROOM_STATE, new DecimalType(unitStatus));
updateState(CHANNEL_ROOM_SWITCH, OnOffType.from(unitStatus == 1));
updateState(CHANNEL_ROOM_SCENE_A, OnOffType.from(unitStatus == 2));
updateState(CHANNEL_ROOM_SCENE_B, OnOffType.from(unitStatus == 3));
updateState(CHANNEL_ROOM_SCENE_C, OnOffType.from(unitStatus == 4));
updateState(CHANNEL_ROOM_SCENE_D, OnOffType.from(unitStatus == 5));
}
}

View File

@ -0,0 +1,71 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.omnilink.internal.handler.units.dimmable;
import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.CHANNEL_UPB_STATUS;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.omnilink.internal.handler.OmniLinkCmd;
import org.openhab.binding.omnilink.internal.handler.units.DimmableUnitHandler;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link UpbUnitHandler} defines some methods that are used to
* interface with an OmniLink UPB Unit. This by extension also defines the
* UPB Unit thing that openHAB will be able to pick up and interface with.
*
* @author Craig Hamilton - Initial contribution
* @author Ethan Dye - openHAB3 rewrite
*/
@NonNullByDefault
public class UpbUnitHandler extends DimmableUnitHandler {
private final Logger logger = LoggerFactory.getLogger(UpbUnitHandler.class);
private final int thingID = getThingNumber();
public @Nullable String number;
public UpbUnitHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
if (command instanceof RefreshType) {
updateState(CHANNEL_UPB_STATUS, UnDefType.UNDEF);
return;
}
switch (channelUID.getId()) {
case CHANNEL_UPB_STATUS:
if (command instanceof StringType) {
sendOmnilinkCommand(OmniLinkCmd.CMD_UNIT_UPB_REQ_STATUS.getNumber(), 0, thingID);
updateState(CHANNEL_UPB_STATUS, UnDefType.UNDEF);
} else {
logger.debug("Invalid command: {}, must be StringType", command);
}
break;
default:
logger.debug("Unknown channel for UPB Unit thing: {}", channelUID);
super.handleCommand(channelUID, command);
}
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="omnilink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>OmniLink Binding</name>
<description>This is the binding for OmniLink, a security system that interfaces with many devices.</description>
</binding:binding>

View File

@ -0,0 +1,163 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="omnilink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Omni Area Thing -->
<thing-type id="area">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Omni Area</label>
<description>An Omni area configured in the controller.</description>
<channels>
<channel id="activate_keypad_emergency" typeId="omni_activate_keypad_emergency"/>
<channel id="alarm_burglary" typeId="area_alarm"/>
<channel id="alarm_fire" typeId="area_alarm"/>
<channel id="alarm_gas" typeId="area_alarm"/>
<channel id="alarm_auxiliary" typeId="area_alarm"/>
<channel id="alarm_freeze" typeId="area_alarm"/>
<channel id="alarm_water" typeId="area_alarm"/>
<channel id="alarm_duress" typeId="area_alarm"/>
<channel id="alarm_temperature" typeId="area_alarm"/>
<channel id="mode" typeId="omni_area_mode"/>
<channel id="disarm" typeId="area_command"/>
<channel id="day" typeId="area_command"/>
<channel id="night" typeId="area_command"/>
<channel id="away" typeId="area_command"/>
<channel id="vacation" typeId="area_command"/>
<channel id="day_instant" typeId="area_command"/>
<channel id="night_delayed" typeId="area_command"/>
<channel id="all_on_off_event" typeId="all_on_off_event"/>
</channels>
<properties>
<property name="name"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Area Number</label>
<description>The area number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Lumina Area Thing -->
<thing-type id="lumina_area">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Lumina Area</label>
<description>An Lumina area configured in the controller.</description>
<channels>
<channel id="mode" typeId="lumina_area_mode"/>
<channel id="home" typeId="area_command"/>
<channel id="sleep" typeId="area_command"/>
<channel id="away" typeId="area_command"/>
<channel id="vacation" typeId="area_command"/>
<channel id="party" typeId="area_command"/>
<channel id="special" typeId="area_command"/>
<channel id="all_on_off_event" typeId="all_on_off_event"/>
</channels>
<properties>
<property name="name"/>
</properties>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Area Number</label>
<description>The area number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Area channels -->
<channel-type id="area_alarm">
<item-type>Switch</item-type>
<label>Area Alarm</label>
<description>Indicates if an alarm is active.</description>
<category>Alarm</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="omni_area_mode">
<item-type>Number</item-type>
<label>Security Mode</label>
<description>Represents the area security mode.</description>
<category>Alarm</category>
<state readOnly="true">
<options>
<option value="0">Off</option>
<option value="1">Day</option>
<option value="2">Night</option>
<option value="3">Away</option>
<option value="4">Vacation</option>
<option value="5">Day instant</option>
<option value="6">Night delayed</option>
<option value="9">Arming day</option>
<option value="10">Arming night</option>
<option value="11">Arming away</option>
<option value="12">Arming vacation</option>
<option value="13">Arming day instant</option>
<option value="14">Arming night delayed</option>
</options>
</state>
</channel-type>
<channel-type id="area_command">
<item-type>String</item-type>
<label>Security Command</label>
<description>Sends a 4 digit user code to activate the area command.</description>
<category>Alarm</category>
</channel-type>
<channel-type id="omni_activate_keypad_emergency">
<item-type>Number</item-type>
<label>Activate Keypad Emergency</label>
<description>Activate a burglary, fire, or auxiliary keypad emergency alarm on Omni based models.</description>
<category>Alarm</category>
<state>
<options>
<option value="1">Burglary</option>
<option value="2">Fire</option>
<option value="3">Auxiliary</option>
</options>
</state>
</channel-type>
<channel-type id="lumina_area_mode">
<item-type>Number</item-type>
<label>Security Mode</label>
<description>Represents the area security mode.</description>
<category>Alarm</category>
<state readOnly="true">
<options>
<option value="1">Home</option>
<option value="2">Sleep</option>
<option value="3">Away</option>
<option value="4">Vacation</option>
<option value="5">Party</option>
<option value="6">Special</option>
<option value="9">Setting home</option>
<option value="10">Setting sleep</option>
<option value="11">Setting away</option>
<option value="12">Setting vacation</option>
<option value="13">Setting party</option>
<option value="14">Setting special</option>
</options>
</state>
</channel-type>
<channel-type id="all_on_off_event">
<kind>trigger</kind>
<label>All On/Off Event</label>
<description>Event sent when an all on/off event occurs.</description>
<event>
<options>
<option value="OFF">Off</option>
<option value="ON">On</option>
</options>
</event>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="omnilink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Audio Source Thing -->
<thing-type id="audio_source">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Audio Source</label>
<description>An audio source configured in the controller.</description>
<channels>
<channel id="source_text_1" typeId="audio_source_text"/>
<channel id="source_text_2" typeId="audio_source_text"/>
<channel id="source_text_3" typeId="audio_source_text"/>
<channel id="source_text_4" typeId="audio_source_text"/>
<channel id="source_text_5" typeId="audio_source_text"/>
<channel id="source_text_6" typeId="audio_source_text"/>
<channel id="polling" typeId="audio_source_polling"/>
</channels>
<properties>
<property name="name"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Audio Source Number</label>
<description>The audio source number.</description>
</parameter>
<parameter name="autostart" type="boolean" required="false">
<label>Autostart Polling</label>
<description>Autostart polling of audio source on creation of thing.</description>
<default>true</default>
</parameter>
</config-description>
</thing-type>
<!-- Audio Source Channels -->
<channel-type id="audio_source_text">
<item-type>String</item-type>
<label>Source Data</label>
<description>A line of metadata from this audio source.</description>
<category>Text</category>
</channel-type>
<channel-type id="audio_source_polling">
<item-type>Switch</item-type>
<label>Audio Source Polling</label>
<description>Enable or disable polling of this audio source.</description>
<category>Switch</category>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="omnilink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Audio Zone Thing -->
<thing-type id="audio_zone">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Audio Zone</label>
<description>An audio zone configured in the controller.</description>
<channels>
<channel id="zone_power" typeId="audio_zone_power"/>
<channel id="zone_mute" typeId="audio_zone_mute"/>
<channel id="zone_volume" typeId="audio_zone_volume"/>
<channel id="zone_source" typeId="audio_zone_source"/>
<channel id="zone_control" typeId="audio_zone_control"/>
</channels>
<properties>
<property name="name"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Audio Zone Number</label>
<description>The audio zone number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Audio Zone Channels -->
<channel-type id="audio_zone_power">
<item-type>Switch</item-type>
<label>Audio Zone Power</label>
<description>Power status of this audio zone.</description>
<category>Switch</category>
</channel-type>
<channel-type id="audio_zone_mute">
<item-type>Switch</item-type>
<label>Audio Zone Mute</label>
<description>Mute status of this audio zone.</description>
<category>Switch</category>
</channel-type>
<channel-type id="audio_zone_volume">
<item-type>Dimmer</item-type>
<label>Audio Zone Volume</label>
<description>Volume level of this audio zone.</description>
<category>Slider</category>
<state min="0" max="100"/>
</channel-type>
<channel-type id="audio_zone_source">
<item-type>Number</item-type>
<label>Source</label>
<description>Source for this audio zone.</description>
<category>MediaControl</category>
<state min="1" max="100"/>
</channel-type>
<channel-type id="audio_zone_control">
<item-type>Player</item-type>
<label>Control</label>
<description>Control the audio zone, e.g. start/stop/next/previous.</description>
<category>MediaControl</category>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,172 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="omnilink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- OmniLink Controller Bridge -->
<bridge-type id="controller">
<label>OmniLink Controller</label>
<description>An OmniLink controller.</description>
<channels>
<channel id="sysdate" typeId="sysDate"/>
<channel id="enable_disable_beeper" typeId="console_enable_disable_beeper">
<label>Console Beepers</label>
</channel>
<channel id="beep" typeId="console_beep">
<label>Beep Consoles</label>
</channel>
<channel id="last_log" typeId="last_log"/>
<channel id="phone_line_event" typeId="phone_line_event"/>
<channel id="ac_power_event" typeId="ac_power_event"/>
<channel id="battery_event" typeId="battery_event"/>
<channel id="dcm_event" typeId="dcm_event"/>
<channel id="energy_cost_event" typeId="energy_cost_event"/>
<channel id="camera_trigger_event" typeId="camera_trigger_event"/>
<channel id="upb_link_activated_event" typeId="upb_link_activated_event"/>
<channel id="upb_link_deactivated_event" typeId="upb_link_deactivated_event"/>
</channels>
<properties>
<property name="model number"/>
<property name="major version"/>
<property name="minor version"/>
<property name="revision"/>
<property name="phone number"/>
</properties>
<config-description>
<parameter name="ipAddress" type="text" required="true">
<context>network-address</context>
<label>IP or Host Name</label>
<description>The IP or host name of the controller.</description>
</parameter>
<parameter name="port" type="integer" required="true">
<label>Port</label>
<description>The port of the controller.</description>
<default>4369</default>
</parameter>
<parameter name="key1" type="text" required="true">
<label>Key 1</label>
<description>The first network encription key.</description>
</parameter>
<parameter name="key2" type="text" required="true">
<label>Key 2</label>
<description>The second network encription key.</description>
</parameter>
<parameter name="logPollingInterval" type="integer" required="true">
<label>Log Polling Interval</label>
<description>The interval to poll for new log messages on the controller.</description>
<default>1</default>
</parameter>
</config-description>
</bridge-type>
<!-- Controller Channels -->
<channel-type id="sysDate">
<item-type>DateTime</item-type>
<label>Date/Time</label>
<description>Set controller date/time.</description>
<category>Time</category>
<state pattern="%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS"/>
</channel-type>
<channel-type id="last_log">
<item-type>String</item-type>
<label>Last Log Entry</label>
<description>Last log message on the controller, represented in JSON.</description>
<category>Text</category>
</channel-type>
<channel-type id="upb_link_activated_event">
<kind>trigger</kind>
<label>UPB Link</label>
<description>Event sent when a UPB link is activated.</description>
</channel-type>
<channel-type id="upb_link_deactivated_event">
<kind>trigger</kind>
<label>UPB Link</label>
<description>Event sent when a UPB link is deactivated.</description>
</channel-type>
<channel-type id="phone_line_event">
<kind>trigger</kind>
<label>Phone Line Event</label>
<description>Event sent when the phone line changes state.</description>
<event>
<options>
<option value="ON_HOOK">On Hook</option>
<option value="OFF_HOOK">Off Hook</option>
<option value="DEAD">Dead</option>
<option value="RING">Ring</option>
</options>
</event>
</channel-type>
<channel-type id="ac_power_event">
<kind>trigger</kind>
<label>AC Power Event</label>
<description>Event sent when AC trouble conditions are detected.</description>
<event>
<options>
<option value="OFF">Off</option>
<option value="RESTORED">Restored</option>
</options>
</event>
</channel-type>
<channel-type id="battery_event">
<kind>trigger</kind>
<label>Battery Event</label>
<description>Event sent when battery trouble conditions are detected.</description>
<event>
<options>
<option value="LOW">Low</option>
<option value="OK">OK</option>
</options>
</event>
</channel-type>
<channel-type id="dcm_event">
<kind>trigger</kind>
<label>DCM Event</label>
<description>Event sent when digital communicator trouble conditions are detected.</description>
<event>
<options>
<option value="TROUBLE">Trouble</option>
<option value="OK">OK</option>
</options>
</event>
</channel-type>
<channel-type id="energy_cost_event">
<kind>trigger</kind>
<label>Energy Cost Event</label>
<description>Event sent when the cost of energy changes.</description>
<event>
<options>
<option value="LOW">Trouble</option>
<option value="MID">Mid</option>
<option value="HIGH">High</option>
<option value="CRITCAL">Critical</option>
</options>
</event>
</channel-type>
<channel-type id="camera_trigger_event">
<kind>trigger</kind>
<label>Camera Trigger Event</label>
<description>Event sent when a camera trigger is detected.</description>
<event>
<options>
<option value="1">Camera 1</option>
<option value="2">Camera 2</option>
<option value="3">Camera 3</option>
<option value="4">Camera 4</option>
<option value="5">Camera 5</option>
<option value="6">Camera 6</option>
</options>
</event>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="omnilink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Button Thing -->
<thing-type id="button">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Button</label>
<description>A button configured in the controller.</description>
<channels>
<channel id="press" typeId="button_press"/>
<channel id="activated_event" typeId="button_activated"/>
</channels>
<properties>
<property name="name"/>
<property name="area"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Button Number</label>
<description>The button number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Button Channels -->
<channel-type id="button_press">
<item-type>Switch</item-type>
<label>Button Press</label>
<description>Sends a button event to the controller.</description>
<category>Switch</category>
</channel-type>
<channel-type id="button_activated">
<kind>trigger</kind>
<label>Button Activated</label>
<description>Event sent when a button is activated.</description>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="omnilink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Console Thing -->
<thing-type id="console">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Console</label>
<description>A console configured in the controller.</description>
<channels>
<channel id="enable_disable_beeper" typeId="console_enable_disable_beeper"/>
<channel id="beep" typeId="console_beep"/>
</channels>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Console Number</label>
<description>The console number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Console Channels -->
<channel-type id="console_enable_disable_beeper">
<item-type>String</item-type>
<label>Enable/Disable Console Beeper</label>
<description>Enable/Disable the beeper for this/all console(s).</description>
<category>Switch</category>
<command>
<options>
<option value="OFF">Off</option>
<option value="ON">On</option>
</options>
</command>
</channel-type>
<channel-type id="console_beep">
<item-type>Number</item-type>
<label>Beep Console</label>
<description>Send a beep command to this/all console(s).</description>
<category>SoundVolume</category>
<state>
<options>
<option value="0">Off</option>
<option value="1">Indefinitely</option>
<option value="2">1 time</option>
<option value="3">2 times</option>
<option value="4">3 times</option>
<option value="5">4 times</option>
<option value="6">5 times</option>
</options>
</state>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="omnilink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Humidity Thing -->
<thing-type id="humidity_sensor">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Humidity Sensor</label>
<description>A humidity sensor configured in the controller.</description>
<channels>
<channel id="humidity" typeId="sensor_humidity"/>
<channel id="low_setpoint" typeId="sensor_humidity_low_setpoint"/>
<channel id="high_setpoint" typeId="sensor_humidity_high_setpoint"/>
</channels>
<properties>
<property name="name"/>
<property name="area"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Humidity Sensor Number</label>
<description>The humidity sensor number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Humidity Channels -->
<channel-type id="sensor_humidity">
<item-type>Number:Dimensionless</item-type>
<label>Humidity</label>
<description>The current relative humidity at this humidity sensor.</description>
<category>Humidity</category>
<state readOnly="true" min="0" max="100" pattern="%d %%"/>
</channel-type>
<channel-type id="sensor_humidity_low_setpoint">
<item-type>Number:Dimensionless</item-type>
<label>Low SetPoint</label>
<description>The current low setpoint for this humidity sensor.</description>
<category>Humidity</category>
<state min="0" max="100" pattern="%d %%"/>
</channel-type>
<channel-type id="sensor_humidity_high_setpoint">
<item-type>Number:Dimensionless</item-type>
<label>High SetPoint</label>
<description>The current high setpoint for this humidity sensor.</description>
<category>Humidity</category>
<state min="0" max="100" pattern="%d %%"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="omnilink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Lock Thing -->
<thing-type id="lock">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Lock</label>
<description>An access control reader lock configured in the controller.</description>
<channels>
<channel id="switch" typeId="lock_switch"/>
</channels>
<properties>
<property name="name"/>
<property name="area"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Lock Number</label>
<description>The lock number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Lock Channels -->
<channel-type id="lock_switch">
<item-type>Switch</item-type>
<label>Lock/Unlock</label>
<description>Lock or unlock this lock.</description>
<category>Switch</category>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="omnilink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Temperature Sensor Thing -->
<thing-type id="temp_sensor">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Temperature Sensor</label>
<description>A temperature sensor configured in the controller.</description>
<channels>
<channel id="temperature" typeId="sensor_temperature"/>
<channel id="low_setpoint" typeId="sensor_temp_low_setpoint"/>
<channel id="high_setpoint" typeId="sensor_temp_high_setpoint"/>
</channels>
<properties>
<property name="name"/>
<property name="area"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Temperature Sensor Number</label>
<description>The temperature sensor number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Temperature Sensor Channels -->
<channel-type id="sensor_temperature">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<description>The current temperature at this temperature sensor.</description>
<category>Temperature</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="sensor_temp_low_setpoint">
<item-type>Number:Temperature</item-type>
<label>Low SetPoint</label>
<description>The current low setpoint of this temperature sensor.</description>
<category>Temperature</category>
<state pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="sensor_temp_high_setpoint">
<item-type>Number:Temperature</item-type>
<label>High SetPoint</label>
<description>The current high setpoint of this temperature sensor.</description>
<category>Temperature</category>
<state pattern="%.1f %unit%"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="omnilink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Thermostat Thing -->
<thing-type id="thermostat">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Thermostat</label>
<description>A thermostat configured in the controller.</description>
<channels>
<channel id="freeze_alarm" typeId="thermostat_freeze_alarm"/>
<channel id="comm_failure" typeId="thermostat_comm_failure"/>
<channel id="status" typeId="thermostat_status"/>
<channel id="temperature" typeId="thermostat_temperature"/>
<channel id="outdoor_temperature" typeId="thermostat_outdoor_temperature"/>
<channel id="heat_setpoint" typeId="thermostat_heat_setpoint"/>
<channel id="cool_setpoint" typeId="thermostat_cool_setpoint"/>
<channel id="humidity" typeId="thermostat_humidity"/>
<channel id="humidify_setpoint" typeId="thermostat_humidify_setpoint"/>
<channel id="dehumidify_setpoint" typeId="thermostat_dehumidify_setpoint"/>
<channel id="system_mode" typeId="thermostat_system_mode"/>
<channel id="fan_mode" typeId="thermostat_fan_mode"/>
<channel id="hold_status" typeId="thermostat_hold_status"/>
</channels>
<properties>
<property name="name"/>
<property name="area"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Thermostat Number</label>
<description>The thermostat number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Thermostat Channels -->
<channel-type id="thermostat_freeze_alarm">
<item-type>Contact</item-type>
<label>Thermostat Freeze Alarm</label>
<description>Closed when freeze alarm is triggered by this thermostat.</description>
<category>Alarm</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="thermostat_comm_failure">
<item-type>Contact</item-type>
<label>Thermostat Communications Failure</label>
<description>Closed during a communications failure with this thermostat.</description>
<category>Contact</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="thermostat_status">
<item-type>Number</item-type>
<label>Thermostat Status</label>
<description>The current status of this thermostat.</description>
<category>Heating</category>
<state readOnly="true" pattern="%d">
<options>
<option value="0">Idle</option>
<option value="1">Heating</option>
<option value="2">Cooling</option>
<option value="3">Humidifying</option>
<option value="4">Dehumidifying</option>
</options>
</state>
</channel-type>
<channel-type id="thermostat_temperature">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<description>The current temperature at this thermostat.</description>
<category>Temperature</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="thermostat_outdoor_temperature">
<item-type>Number:Temperature</item-type>
<label>Outdoor Temperature</label>
<description>The current outdoor temperature detected by this thermostat.</description>
<category>Temperature</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="thermostat_heat_setpoint">
<item-type>Number:Temperature</item-type>
<label>Heat SetPoint</label>
<description>The current low/heating setpoint of this thermostat.</description>
<category>Temperature</category>
<state pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="thermostat_cool_setpoint">
<item-type>Number:Temperature</item-type>
<label>Cool SetPoint</label>
<description>The current high/cooling setpoint of this thermostat.</description>
<category>Temperature</category>
<state pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="thermostat_humidity">
<item-type>Number:Dimensionless</item-type>
<label>Humidity</label>
<description>The relative humidity at this thermostat.</description>
<category>Humidity</category>
<state readOnly="true" min="0" max="100" pattern="%d %%"/>
</channel-type>
<channel-type id="thermostat_humidify_setpoint">
<item-type>Number:Dimensionless</item-type>
<label>Humidify SetPoint</label>
<description>The current low/humidify setpoint for this thermostat.</description>
<category>Humidity</category>
<state min="0" max="100" pattern="%d %%"/>
</channel-type>
<channel-type id="thermostat_dehumidify_setpoint">
<item-type>Number:Dimensionless</item-type>
<label>Dehumidify SetPoint</label>
<description>The current high/dehumidify setpoint for this thermostat.</description>
<category>Humidity</category>
<state min="0" max="100" pattern="%d %%"/>
</channel-type>
<channel-type id="thermostat_system_mode">
<item-type>Number</item-type>
<label>System Mode</label>
<description>The current system mode of this thermostat.</description>
<category>Heating</category>
<state pattern="%d">
<options>
<option value="0">Off</option>
<option value="1">Heat</option>
<option value="2">Cool</option>
<option value="3">Auto</option>
<option value="4">Emergency heat</option>
</options>
</state>
</channel-type>
<channel-type id="thermostat_fan_mode">
<item-type>Number</item-type>
<label>Fan Mode</label>
<description>The current fan mode of this thermostat.</description>
<category>Flow</category>
<state>
<options>
<option value="0">Auto</option>
<option value="1">On</option>
<option value="2">Cycle</option>
</options>
</state>
</channel-type>
<channel-type id="thermostat_hold_status">
<item-type>Number</item-type>
<label>Hold Status</label>
<description>The current hold status of this thermostat.</description>
<category>Heating</category>
<state>
<options>
<option value="0">Off</option>
<option value="1">Hold</option>
<option value="2">Vacation hold</option>
</options>
</state>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,323 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="omnilink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Unit Thing -->
<thing-type id="unit">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Unit</label>
<description>A basic unit configured in the controller.</description>
<channels>
<channel id="level" typeId="unit_level"/>
<channel id="switch" typeId="unit_switch"/>
<channel id="on_for_seconds" typeId="on_for_seconds"/>
<channel id="off_for_seconds" typeId="off_for_seconds"/>
<channel id="on_for_minutes" typeId="on_for_minutes"/>
<channel id="off_for_minutes" typeId="off_for_minutes"/>
<channel id="on_for_hours" typeId="on_for_hours"/>
<channel id="off_for_hours" typeId="off_for_hours"/>
</channels>
<properties>
<property name="name"/>
<property name="area"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Unit Number</label>
<description>The unit number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Dimmable Thing -->
<thing-type id="dimmable">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Dimmable Unit</label>
<description>A dimmable unit configured in the controller.</description>
<channels>
<channel id="level" typeId="unit_level"/>
<channel id="switch" typeId="unit_switch"/>
<channel id="on_for_seconds" typeId="on_for_seconds"/>
<channel id="off_for_seconds" typeId="off_for_seconds"/>
<channel id="on_for_minutes" typeId="on_for_minutes"/>
<channel id="off_for_minutes" typeId="off_for_minutes"/>
<channel id="on_for_hours" typeId="on_for_hours"/>
<channel id="off_for_hours" typeId="off_for_hours"/>
<channel id="switch_press_event" typeId="switch_press_event"/>
</channels>
<properties>
<property name="name"/>
<property name="area"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Dimmable Unit Number</label>
<description>The dimmable unit number.</description>
</parameter>
</config-description>
</thing-type>
<!-- UPB Thing -->
<thing-type id="upb">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>UPB Unit</label>
<description>A UPB unit configured in the controller.</description>
<channels>
<channel id="level" typeId="unit_level"/>
<channel id="switch" typeId="unit_switch"/>
<channel id="on_for_seconds" typeId="on_for_seconds"/>
<channel id="off_for_seconds" typeId="off_for_seconds"/>
<channel id="on_for_minutes" typeId="on_for_minutes"/>
<channel id="off_for_minutes" typeId="off_for_minutes"/>
<channel id="on_for_hours" typeId="on_for_hours"/>
<channel id="off_for_hours" typeId="off_for_hours"/>
<channel id="upb_status" typeId="upb_status"/>
<channel id="switch_press_event" typeId="switch_press_event"/>
</channels>
<properties>
<property name="name"/>
<property name="area"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>UPB Unit Number</label>
<description>The UPB unit number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Flag Thing -->
<thing-type id="flag">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Flag</label>
<description>A flag configured in the controller.</description>
<channels>
<channel id="value" typeId="flag_value"/>
<channel id="switch" typeId="flag_switch"/>
<channel id="on_for_seconds" typeId="on_for_seconds"/>
<channel id="off_for_seconds" typeId="off_for_seconds"/>
<channel id="on_for_minutes" typeId="on_for_minutes"/>
<channel id="off_for_minutes" typeId="off_for_minutes"/>
<channel id="on_for_hours" typeId="on_for_hours"/>
<channel id="off_for_hours" typeId="off_for_hours"/>
</channels>
<properties>
<property name="name"/>
<property name="area"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Flag Number</label>
<description>The flag number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Output Thing -->
<thing-type id="output">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Voltage Output</label>
<description>A voltage output configured in the controller.</description>
<channels>
<channel id="switch" typeId="unit_switch"/>
<channel id="on_for_seconds" typeId="on_for_seconds"/>
<channel id="off_for_seconds" typeId="off_for_seconds"/>
<channel id="on_for_minutes" typeId="on_for_minutes"/>
<channel id="off_for_minutes" typeId="off_for_minutes"/>
<channel id="on_for_hours" typeId="on_for_hours"/>
<channel id="off_for_hours" typeId="off_for_hours"/>
</channels>
<properties>
<property name="name"/>
<property name="area"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Voltage Output Number</label>
<description>The voltage output number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Room Thing -->
<thing-type id="room">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Room</label>
<description>A room configured in the controller.</description>
<channels>
<channel id="switch" typeId="room_switch"/>
<channel id="scene_a" typeId="scene_toggle">
<label>Scene A</label>
</channel>
<channel id="scene_b" typeId="scene_toggle">
<label>Scene B</label>
</channel>
<channel id="scene_c" typeId="scene_toggle">
<label>Scene C</label>
</channel>
<channel id="scene_d" typeId="scene_toggle">
<label>Scene D</label>
</channel>
<channel id="state" typeId="room_state"/>
</channels>
<properties>
<property name="name"/>
<property name="area"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Room Number</label>
<description>The room number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Unit channels -->
<channel-type id="unit_level">
<item-type>Dimmer</item-type>
<label>Unit Level</label>
<description>Increase/Decrease the level of this unit.</description>
<category>Slider</category>
<state min="0" max="100" pattern="%d %%"/>
</channel-type>
<channel-type id="unit_switch">
<item-type>Switch</item-type>
<label>Switch</label>
<description>Turn this unit on/off.</description>
<category>Switch</category>
</channel-type>
<channel-type id="on_for_seconds">
<item-type>Number</item-type>
<label>On for Seconds</label>
<description>Turn on this unit for a specified number of seconds.</description>
<category>Switch</category>
<state min="1" max="99"/>
</channel-type>
<channel-type id="off_for_seconds">
<item-type>Number</item-type>
<label>Off for Seconds</label>
<description>Turn off this unit for a specified number of seconds.</description>
<category>Switch</category>
<state min="1" max="99"/>
</channel-type>
<channel-type id="on_for_minutes">
<item-type>Number</item-type>
<label>On for Minutes</label>
<description>Turn on this unit for a specified number of minutes.</description>
<category>Switch</category>
<state min="1" max="99"/>
</channel-type>
<channel-type id="off_for_minutes">
<item-type>Number</item-type>
<label>Off for Minutes</label>
<description>Turn off this unit for a specified number of minutes.</description>
<category>Switch</category>
<state min="1" max="99"/>
</channel-type>
<channel-type id="on_for_hours">
<item-type>Number</item-type>
<label>On for Hours</label>
<description>Turn on this unit for a specified number of hours.</description>
<category>Switch</category>
<state min="1" max="18"/>
</channel-type>
<channel-type id="off_for_hours">
<item-type>Number</item-type>
<label>Off for Hours</label>
<description>Turn off this unit for a specified number of hours.</description>
<category>Switch</category>
<state min="1" max="18"/>
</channel-type>
<channel-type id="upb_status">
<item-type>String</item-type>
<label>UPB Status</label>
<description>Send a UPB status request message for this unit to the controller.</description>
<category>Status</category>
<command>
<options>
<option value="GET STATUS">Get Status</option>
</options>
</command>
</channel-type>
<channel-type id="room_switch">
<item-type>Switch</item-type>
<label>Switch</label>
<description>Turn this room on/off.</description>
<category>Switch</category>
</channel-type>
<channel-type id="scene_toggle">
<item-type>Switch</item-type>
<label>Scene Toggle</label>
<description>Turn this scene on/off.</description>
<category>Switch</category>
</channel-type>
<channel-type id="room_state">
<item-type>Number</item-type>
<label>State</label>
<description>The current state of this room.</description>
<category>Switch</category>
<state pattern="%d">
<options>
<option value="0">Off</option>
<option value="1">On</option>
<option value="2">Scene A</option>
<option value="3">Scene B</option>
<option value="4">Scene C</option>
<option value="5">Scene D</option>
</options>
</state>
</channel-type>
<channel-type id="flag_value">
<item-type>Number</item-type>
<label>Flag Value</label>
<description>Numeric value of this flag.</description>
<category>Number</category>
<state min="0" max="255" pattern="%d"/>
</channel-type>
<channel-type id="flag_switch">
<item-type>Switch</item-type>
<label>Flag Switch</label>
<description>Turn this flag on/off.</description>
<category>Switch</category>
</channel-type>
<channel-type id="switch_press_event">
<kind>trigger</kind>
<label>Switch Press Event</label>
<description>Event sent when an ALC, UPB, Radio RA, or Starlite switch is pressed.</description>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="omnilink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Zone Thing -->
<thing-type id="zone">
<supported-bridge-type-refs>
<bridge-type-ref id="controller"/>
</supported-bridge-type-refs>
<label>Zone</label>
<description>A zone configured in the controller.</description>
<channels>
<channel id="contact" typeId="zone_contact"/>
<channel id="current_condition" typeId="zone_current_condition"/>
<channel id="latched_alarm_status" typeId="zone_latched_alarm_status"/>
<channel id="arming_status" typeId="zone_arming_status"/>
<channel id="bypass" typeId="zone_bypass"/>
<channel id="restore" typeId="zone_restore"/>
</channels>
<properties>
<property name="name"/>
<property name="area"/>
</properties>
<representation-property>number</representation-property>
<config-description>
<parameter name="number" type="integer" required="true">
<label>Zone Number</label>
<description>The zone number.</description>
</parameter>
</config-description>
</thing-type>
<!-- Zone Channels -->
<channel-type id="zone_contact">
<item-type>Contact</item-type>
<label>Contact State</label>
<description>Contact state information of this zone.</description>
<category>Contact</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="zone_current_condition">
<item-type>Number</item-type>
<label>Current Condition</label>
<description>Current condition of this zone.</description>
<category>Contact</category>
<state readOnly="true" pattern="%d">
<options>
<option value="0">Secure</option>
<option value="1">Not Ready</option>
<option value="2">Trouble</option>
</options>
</state>
</channel-type>
<channel-type id="zone_latched_alarm_status">
<item-type>Number</item-type>
<label>Latched Alarm Status</label>
<description>Latched alarm status of this zone.</description>
<category>Contact</category>
<state readOnly="true" pattern="%d">
<options>
<option value="0">Secure</option>
<option value="1">Tripped</option>
<option value="2">Reset, but previously tripped</option>
</options>
</state>
</channel-type>
<channel-type id="zone_arming_status">
<item-type>Number</item-type>
<label>Arming Status</label>
<description>Arming status of this zone.</description>
<category>Contact</category>
<state readOnly="true" pattern="%d">
<options>
<option value="0">Disarmed</option>
<option value="1">Armed</option>
<option value="2">Bypassed by user</option>
<option value="3">Bypassed by system</option>
</options>
</state>
</channel-type>
<channel-type id="zone_bypass">
<item-type>String</item-type>
<label>Bypass Zone</label>
<description>Send a 4 digit user code to bypass this zone.</description>
<category>Alarm</category>
</channel-type>
<channel-type id="zone_restore">
<item-type>String</item-type>
<label>Restore Zone</label>
<description>Send a 4 digit user code to restore this zone.</description>
<category>Alarm</category>
</channel-type>
</thing:thing-descriptions>

View File

@ -216,6 +216,7 @@
<module>org.openhab.binding.oceanic</module>
<module>org.openhab.binding.ojelectronics</module>
<module>org.openhab.binding.omnikinverter</module>
<module>org.openhab.binding.omnilink</module>
<module>org.openhab.binding.onebusaway</module>
<module>org.openhab.binding.onewiregpio</module>
<module>org.openhab.binding.onewire</module>