/**
* Copyright (c) 2010-2013, openHAB.org and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.binding.omnilink.internal;
import java.io.IOException;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.openhab.binding.omnilink.OmniLinkBindingProvider;
import org.openhab.binding.omnilink.internal.model.Area;
import org.openhab.binding.omnilink.internal.model.AudioSource;
import org.openhab.binding.omnilink.internal.model.AudioZone;
import org.openhab.binding.omnilink.internal.model.Auxiliary;
import org.openhab.binding.omnilink.internal.model.Button;
import org.openhab.binding.omnilink.internal.model.OmnilinkDevice;
import org.openhab.binding.omnilink.internal.model.Thermostat;
import org.openhab.binding.omnilink.internal.model.Unit;
import org.openhab.binding.omnilink.internal.model.Zone;
import org.openhab.binding.omnilink.internal.ui.OmnilinkItemGenerator;
import org.apache.commons.lang.StringUtils;
import org.openhab.core.binding.AbstractBinding;
import org.openhab.core.binding.BindingProvider;
import org.openhab.core.items.Item;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
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.NotificationListener;
import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
import com.digitaldan.jomnilinkII.OmniNotConnectedException;
import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
import com.digitaldan.jomnilinkII.MessageTypes.AudioSourceStatus;
import com.digitaldan.jomnilinkII.MessageTypes.ObjectProperties;
import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
import com.digitaldan.jomnilinkII.MessageTypes.OtherEventNotifications;
import com.digitaldan.jomnilinkII.MessageTypes.SystemStatus;
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.MessageTypes.statuses.AreaStatus;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.AudioZoneStatus;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.Status;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ThermostatStatus;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.UnitStatus;
import com.digitaldan.jomnilinkII.MessageTypes.statuses.ZoneStatus;
/**
* Omnilink Binding allows full control over a HAI Omni or Lumina system
* @author Dan Cunningham
* @since 1.5.0
*/
public class OmniLinkBinding extends AbstractBinding<OmniLinkBindingProvider>
implements ManagedService, NotificationListener {
private static final Logger logger = LoggerFactory
.getLogger(OmniLinkBinding.class);
/*
* the current OmniWorker thread
*/
private OmniConnectionThread omniWorker = null;
/**
* New items or items needing to be refreshed get added to refreshmao
* the worker thread will refresh and remove them
*/
private Map<String, OmniLinkBindingConfig> refreshMap = Collections.synchronizedMap(new HashMap<String, OmniLinkBindingConfig>());
/**
* we keep maps of the various object numbers to a specific object
*/
private Map<Integer, Area> areaMap = Collections.synchronizedMap(new HashMap<Integer, Area>());
private Map<Integer, AudioSource> audioSourceMap = Collections.synchronizedMap(new HashMap<Integer, AudioSource>());
private Map<Integer, AudioZone> audioZoneMap = Collections.synchronizedMap(new HashMap<Integer, AudioZone>());
private Map<Integer, Auxiliary> auxMap = Collections.synchronizedMap(new HashMap<Integer, Auxiliary>());
private Map<Integer, Thermostat> thermostatMap = Collections.synchronizedMap(new HashMap<Integer, Thermostat>());
private Map<Integer, Unit> unitMap = Collections.synchronizedMap(new HashMap<Integer, Unit>());
private Map<Integer, Zone> zoneMap = Collections.synchronizedMap(new HashMap<Integer, Zone>());
private Map<Integer, Button> buttonMap = Collections.synchronizedMap(new HashMap<Integer, Button>());
/**
* We need to poll for audio source changes, that action
* waits on this lock, if notified it will imediately
* check for updates, useful of a zone is changing
* tracks
*/
private Object audioUpdateLock = new Object();
public OmniLinkBinding() {
}
@Override
public void activate() {
logger.trace("OmniLinkBinding activate");
}
@Override
public void deactivate() {
logger.debug("OmniLinkBinding disconnecting");
if (omniWorker != null)
omniWorker.setRunning(false);
}
/**
* {@inheritDoc}
*/
@Override
public void bindingChanged(BindingProvider provider, String itemName) {
if (provider instanceof OmniLinkBindingProvider) {
OmniLinkBindingProvider omniProvider = (OmniLinkBindingProvider) provider;
logger.debug("binding changed");
OmniLinkBindingConfig config = omniProvider.getOmniLinkBindingConfig(itemName);
refreshMap.put(itemName, config);
}
}
/**
* {@inheritDoc}
*/
@Override
public void allBindingsChanged(BindingProvider provider) {
if (provider instanceof OmniLinkBindingProvider) {
OmniLinkBindingProvider omniProvider = (OmniLinkBindingProvider) provider;
populateRefreshMapFromProvider(omniProvider);
logger.debug("all binding changed");
}
}
/**
* @{inheritDoc
*/
@Override
protected void internalReceiveCommand(String itemName, Command command) {
// the code being executed when a command was sent on the openHAB
// event bus goes here. This method is only called if one of the
// BindingProviders provide a binding for the given 'itemName'.
logger.debug("internalReceiveCommand() is called!!! {} {} ", itemName,
command);
if (omniWorker != null && omniWorker.isConnected()) {
for (OmniLinkBindingProvider provider : providers) {
OmniLinkBindingConfig config = provider.getOmniLinkBindingConfig(itemName);
Item item = provider.getItem(itemName);
List<OmniLinkControllerCommand> commands = OmniLinkCommandMapper.getCommand(item, config, command);
/*
* send each command we get back
*/
for (OmniLinkControllerCommand cmd : commands) {
try {
logger.debug(
"Sending command {}/{}/{}",
new Object[] { cmd.getCommand(),
cmd.getParameter1(),
cmd.getParameter2() });
omniWorker.getOmniConnection().controllerCommand(
cmd.getCommand(), cmd.getParameter1(),
cmd.getParameter2());
// little hack to get audio updates faster.
if (config.getObjectType() == OmniLinkItemType.AUDIOZONE_KEY)
audioUpdateLock.notifyAll();
} catch (IOException e) {
logger.error("Could not send command", e);
} catch (OmniNotConnectedException e) {
logger.error("Could not send command", e);
} catch (OmniInvalidResponseException e) {
logger.error("Could not send command", e);
} catch (OmniUnknownMessageTypeException e) {
logger.error("Could not send command", e);
}
}
}
} else {
logger.debug(
"Could not send message, conncetion not established {}",
omniWorker == null);
}
// get the
}
/**
* @{inheritDoc
*/
@Override
protected void internalReceiveUpdate(String itemName, State newState) {
// the code being executed when a state was sent on the openHAB
// event bus goes here. This method is only called if one of the
// BindingProviders provide a binding for the given 'itemName'.
logger.debug("internalReceiveCommand() is called! {} {}", itemName, newState);
}
/**
* @{inheritDoc
*/
@Override
public void updated(Dictionary<String, ?> config)
throws ConfigurationException {
if (omniWorker != null)
omniWorker.setRunning(false);
if (config != null) {
String host = (String) config.get("host");
int port = Integer.parseInt((String) config.get("port"));
String key1 = (String) config.get("key1");
String key2 = (String) config.get("key2");
boolean generateItems = Boolean.parseBoolean((String)config.get("generateItems"));
if (StringUtils.isEmpty(host) || StringUtils.isEmpty(key1)
|| StringUtils.isEmpty(key2))
throw new ConfigurationException("omnilink",
"host, key1 or key2 was not found");
logger.debug("Starting update");
omniWorker = new OmniConnectionThread(host, port,
key1 + ":" + key2, generateItems, this);
omniWorker.start();
}
}
/**
* Add items to be refreshed by our connection thread
* @param omniProvider
*/
private void populateRefreshMapFromProvider(
OmniLinkBindingProvider omniProvider) {
for (String itemName : omniProvider.getItemNames()) {
OmniLinkBindingConfig config = omniProvider
.getOmniLinkBindingConfig(itemName);
refreshMap.put(itemName, config);
}
}
/**
* add items from all providers to be refreshed by our
* connection thread
*/
private void populateRefreshMapFromAllProviders() {
for (OmniLinkBindingProvider provider : providers) {
populateRefreshMapFromProvider(provider);
}
}
/**
* This represents our internal connection thread, to stop set running = false
* @author daniel
*
*/
private class OmniConnectionThread extends Thread {
private boolean running = true;
private boolean connected = false;
private String host;
private int port;
private String key;
private NotificationListener listener;
private boolean celius;
private boolean omni;
private boolean generateItems;
private Connection c;
/**
* This initializes, but does not start our main connection thread
* @param host
* @param port
* @param key
* @param generateItems is if we should print a items list to the log
* @param listener callback for events
*/
public OmniConnectionThread(String host, int port, String key,
boolean generateItems, NotificationListener listener) {
super("OmniConnectionThread");
this.host = host;
this.port = port;
this.key = key;
this.listener = listener;
this.generateItems = generateItems;
this.running = false;
// audioSources = new ConcurrentHashMap<Integer, AudioSource>();
logger.debug("OmniConnectionThread init");
}
/*
* is our thread running
*/
public boolean isRunning() {
return running;
}
/*
* Stop running and disconnect
*/
public void setRunning(boolean running) {
this.running = running;
if (!running)
audioUpdateLock.notifyAll();
}
/*
* do we have a valid connection
*/
public boolean isConnected() {
return connected;
}
/*
* the connection object for sending commands
*/
public Connection getOmniConnection() {
return c;
}
/**
* Main processing loop
*/
@Override
public void run() {
running = true;
logger.debug("OmniConnectionThread running");
while (running) {
connected = false;
/*
* Connect to the system
*/
logger.debug("OmniConnectionThread trying to connect");
try {
c = new Connection(host, port, key);
connected = true;
logger.debug("OmniConnectionThread connected");
} catch (Exception e) {
logger.error("Could not connect", e);
}
/*
* If we fail to connect sleep a bit before trying again
*/
if (!connected) {
try {
Thread.currentThread();
Thread.sleep(10 * 1000);
} catch (InterruptedException ignored) {
}
} else {
/*
* If we get disconnected then do nothing
*/
c.addDisconnectListener(new DisconnectListener() {
@Override
public void notConnectedEvent(Exception e) {
logger.error("OmniConnectionThread was disconnected, will try again", e);
connected = false;
}
});
/*
* Real time device changes get processed here
*/
c.addNotificationListener(listener);
/*
* Load everything and main audio source text loop
*/
try {
SystemStatus sysstatus = c.reqSystemStatus();
logger.info("System: " + sysstatus.toString());
omni = c.reqSystemInformation().getModel() < 36;
/*
* We need to explicitly tell the controller to send us
* real time notifications
*/
c.enableNotifications();
if (generateItems) {
OmnilinkItemGenerator gen = new OmnilinkItemGenerator(c);
logger.info(gen.generateItemsAndGroups());
}
// update known items with state
populateRefreshMapFromAllProviders();
// load audio sources, we won't get updates on these
readAllAudioSources();
/*
* if we get disconnected then refresh any devices that
* we have to keep them up to date.
*/
while (running && c.connected()) {
updateRefreshItems();
/*
* Audio source text is not pushed in real time, so
* we poll for it
*/
updateAudioSourceTexts();
try {
synchronized (audioUpdateLock) {
audioUpdateLock.wait(5000);
}
} catch (InterruptedException ignored) {
}
}
} catch (IOException ex) {
logger.error("Could not connect to system", ex);
} catch (OmniNotConnectedException ex) {
logger.error("Could not connect to system", ex.getNotConnectedReason());
} catch (OmniInvalidResponseException ex) {
logger.error("Could not connect to system", ex);
} catch (OmniUnknownMessageTypeException ex) {
logger.error("Could not connect to system", ex);
// is this needed? I just added this without looking at
// the code for 2 years
} catch (Exception ex) {
logger.error("Could not connect to system", ex);
} finally {
c.disconnect();
c = null;
}
}
}
}
/**
* This goes through a map of item names and updates their state's
* @throws IOException
* @throws OmniNotConnectedException
* @throws OmniInvalidResponseException
* @throws OmniUnknownMessageTypeException
*/
private void updateRefreshItems() throws IOException,
OmniNotConnectedException, OmniInvalidResponseException,
OmniUnknownMessageTypeException {
if (refreshMap.size() == 0)
return;
Map<String, OmniLinkBindingConfig> itemMap =
new HashMap<String, OmniLinkBindingConfig>(refreshMap);
for (String itemName : itemMap.keySet()) {
OmniLinkBindingConfig config = itemMap.get(itemName);
refreshMap.remove(itemName);
if (config != null) {
Integer number = new Integer(config.getNumber());
for (OmniLinkBindingProvider provider : providers) {
switch (config.getObjectType()) {
case UNIT: {
UnitProperties p = readUnitProperties(config
.getNumber());
Unit unit = unitMap.get(number);
if (unit == null) {
unit = new Unit(p);
unitMap.put(number, unit);
}
config.setDevice(unit);
unit.setProperties(p);
unit.updateItem(provider.getItem(itemName), config,
eventPublisher);
}
break;
case THERMO_COOL_POINT:
case THERMO_FAN_MODE:
case THERMO_HEAT_POINT:
case THERMO_HOLD_MODE:
case THERMO_SYSTEM_MODE:
case THERMO_TEMP: {
ThermostatProperties p = readThermoProperties(
config.getNumber());
Thermostat thermo = thermostatMap.get(number);
if (thermo == null) {
thermo = new Thermostat(p, celius);
thermostatMap.put(number, thermo);
}
config.setDevice(thermo);
thermo.setProperties(p);
thermo.updateItem(provider.getItem(itemName),
config, eventPublisher);
}
break;
case AUDIOZONE_MUTE:
case AUDIOZONE_POWER:
case AUDIOZONE_SOURCE:
case AUDIOZONE_KEY:
case AUDIOZONE_TEXT:
case AUDIOZONE_TEXT_FIELD1:
case AUDIOZONE_TEXT_FIELD2:
case AUDIOZONE_TEXT_FIELD3:
case AUDIOZONE_VOLUME: {
AudioZoneProperties p = readAudioZoneProperties(config.getNumber());
AudioZone audioZone = audioZoneMap.get(number);
if (audioZone == null) {
audioZone = new AudioZone(p);
audioZone.setAudioSource(audioSourceMap);
audioZoneMap.put(number, audioZone);
}
config.setDevice(audioZone);
audioZone.setProperties(p);
audioZone.updateItem(provider.getItem(itemName),
config, eventPublisher);
}
break;
case AUDIOSOURCE_TEXT:
case AUDIOSOURCE_TEXT_FIELD1:
case AUDIOSOURCE_TEXT_FIELD2:
case AUDIOSOURCE_TEXT_FIELD3: {
AudioSource as = audioSourceMap.get(number);
if (as != null) {
config.setDevice(as);
as.updateItem(provider.getItem(itemName),
config, eventPublisher);
}
}
break;
case AREA_ENTRY_TIMER:
case AREA_EXIT_TIMER:
case AREA_STATUS_ALARM:
case AREA_STATUS_ENTRY_DELAY:
case AREA_STATUS_EXIT_DELAY:
case AREA_STATUS_MODE: {
AreaProperties p = readAreaProperties(config.getNumber());
Area area = areaMap.get(number);
if (area == null) {
area = new Area(p, omni);
areaMap.put(number, area);
}
config.setDevice(area);
area.setProperties(p);
area.updateItem(provider.getItem(itemName), config,
eventPublisher);
}
break;
case AUX_CURRENT:
case AUX_HIGH:
case AUX_LOW:
case AUX_STATUS: {
AuxSensorProperties p = readAuxProperties(config.getNumber());
Auxiliary auxiliary = auxMap.get(number);
if (auxiliary == null) {
auxiliary = new Auxiliary(p, celius);
auxMap.put(number, auxiliary);
}
config.setDevice(auxiliary);
auxiliary.setProperties(p);
auxiliary.updateItem(provider.getItem(itemName), config,
eventPublisher);
}
break;
case ZONE_STATUS_ARMING:
case ZONE_STATUS_CURRENT:
case ZONE_STATUS_LATCHED:
case ZONE_STATUS_ALL: {
ZoneProperties p = readZoneProperties(config.getNumber());
Zone zone = zoneMap.get(number);
if (zone == null) {
zone = new Zone(p);
zoneMap.put(number, zone);
}
config.setDevice(zone);
zone.setProperties(p);
zone.updateItem(provider.getItem(itemName), config,
eventPublisher);
}
break;
case BUTTON: {
ButtonProperties p = readButtonProperties(config.getNumber());
Button button = buttonMap.get(number);
if (button == null) {
button = new Button(p);
buttonMap.put(number, button);
}
config.setDevice(button);
}
break;
default:
break;
}
}
}
}
}
/**
* Iterate through the system units (lights)
* @param number of the unti
* @return UnitProperties of unit, null if not found
* @throws IOException
* @throws OmniNotConnectedException
* @throws OmniInvalidResponseException
* @throws OmniUnknownMessageTypeException
*/
private UnitProperties readUnitProperties(int number)
throws IOException, OmniNotConnectedException,
OmniInvalidResponseException, OmniUnknownMessageTypeException {
Message m = c.reqObjectProperties(Message.OBJ_TYPE_UNIT, number, 0,
ObjectProperties.FILTER_1_NAMED,
ObjectProperties.FILTER_2_AREA_ALL,
ObjectProperties.FILTER_3_ANY_LOAD);
if (m.getMessageType() == Message.MESG_TYPE_OBJ_PROP) {
return ((UnitProperties) m);
}
return null;
}
/**
* Read the properties of a thermostat
*
* @param number of the thermostat
* @return ThermostatProperties of thermostat or null if not found
* @throws IOException
* @throws OmniNotConnectedException
* @throws OmniInvalidResponseException
* @throws OmniUnknownMessageTypeException
*/
private ThermostatProperties readThermoProperties(int number)
throws IOException, OmniNotConnectedException,
OmniInvalidResponseException, OmniUnknownMessageTypeException {
Message m = c.reqObjectProperties(Message.OBJ_TYPE_THERMO, number,
0, ObjectProperties.FILTER_1_NAMED,
ObjectProperties.FILTER_2_AREA_ALL,
ObjectProperties.FILTER_3_ANY_LOAD);
if (m.getMessageType() == Message.MESG_TYPE_OBJ_PROP) {
return ((ThermostatProperties) m);
}
return null;
}
/**
* Read the properties of a Auxiliary sensor
* @param number of Auxiliary sensor
* @return AuxSensorProperties of Auxiliary sensor or null if not found
* @throws IOException
* @throws OmniNotConnectedException
* @throws OmniInvalidResponseException
* @throws OmniUnknownMessageTypeException
*/
private AuxSensorProperties readAuxProperties(int number)
throws IOException, OmniNotConnectedException,
OmniInvalidResponseException, OmniUnknownMessageTypeException {
Message m = c.reqObjectProperties(Message.OBJ_TYPE_AUX_SENSOR,
number, 0, ObjectProperties.FILTER_1_NAMED,
ObjectProperties.FILTER_2_AREA_ALL,
ObjectProperties.FILTER_3_ANY_LOAD);
if (m.getMessageType() == Message.MESG_TYPE_OBJ_PROP) {
return ((AuxSensorProperties) m);
}
return null;
}
/**
* Read the properties of a audio zone
* @param number of audio zone
* @return AudioZoneProperties of audio zone, or null if not found
* @throws IOException
* @throws OmniNotConnectedException
* @throws OmniInvalidResponseException
* @throws OmniUnknownMessageTypeException
*/
private AudioZoneProperties readAudioZoneProperties(int number)
throws IOException, OmniNotConnectedException,
OmniInvalidResponseException, OmniUnknownMessageTypeException {
Message m = c.reqObjectProperties(Message.OBJ_TYPE_AUDIO_ZONE,
number, 0, ObjectProperties.FILTER_1_NAMED,
ObjectProperties.FILTER_2_NONE,
ObjectProperties.FILTER_3_NONE);
if (m.getMessageType() == Message.MESG_TYPE_OBJ_PROP) {
return ((AudioZoneProperties) m);
}
return null;
}
/**
* Read the properties of a area
* @param number of area
* @return AreaProperties of area or null if not found
* @throws IOException
* @throws OmniNotConnectedException
* @throws OmniInvalidResponseException
* @throws OmniUnknownMessageTypeException
*/
private AreaProperties readAreaProperties(int number)
throws IOException, OmniNotConnectedException,
OmniInvalidResponseException, OmniUnknownMessageTypeException {
Message m = c.reqObjectProperties(Message.OBJ_TYPE_AREA, number, 0,
ObjectProperties.FILTER_1_NAMED_UNAMED,
ObjectProperties.FILTER_2_NONE,
ObjectProperties.FILTER_3_NONE);
if (m.getMessageType() == Message.MESG_TYPE_OBJ_PROP) {
return ((AreaProperties) m);
}
return null;
}
/**
* Read the properties of a zone
* @param number of zone
* @return ZoneProperties of zone, or null if not found
* @throws IOException
* @throws OmniNotConnectedException
* @throws OmniInvalidResponseException
* @throws OmniUnknownMessageTypeException
*/
private ZoneProperties readZoneProperties(int number)
throws IOException, OmniNotConnectedException,
OmniInvalidResponseException, OmniUnknownMessageTypeException {
Message m = c.reqObjectProperties(Message.OBJ_TYPE_ZONE, number, 0,
ObjectProperties.FILTER_1_NAMED,
ObjectProperties.FILTER_2_AREA_ALL,
ObjectProperties.FILTER_3_ANY_LOAD);
if (m.getMessageType() == Message.MESG_TYPE_OBJ_PROP) {
return ((ZoneProperties) m);
}
return null;
}
/**
* Read the properties of a button
* @param number of button
* @return ButtonProperties of button, or null if not found
* @throws IOException
* @throws OmniNotConnectedException
* @throws OmniInvalidResponseException
* @throws OmniUnknownMessageTypeException
*/
private ButtonProperties readButtonProperties(int number)
throws IOException, OmniNotConnectedException,
OmniInvalidResponseException, OmniUnknownMessageTypeException {
Message m = c.reqObjectProperties(Message.OBJ_TYPE_BUTTON, number,
0, ObjectProperties.FILTER_1_NAMED,
ObjectProperties.FILTER_2_NONE,
ObjectProperties.FILTER_3_NONE);
if (m.getMessageType() == Message.MESG_TYPE_OBJ_PROP) {
return ((ButtonProperties) m);
}
return null;
}
/**
* Populates all know audio sources which are used by known zones
* @throws IOException
* @throws OmniNotConnectedException
* @throws OmniInvalidResponseException
* @throws OmniUnknownMessageTypeException
*/
private void readAllAudioSources() throws IOException,
OmniNotConnectedException, OmniInvalidResponseException,
OmniUnknownMessageTypeException {
int objnum = 0;
Message m;
while ((m = c.reqObjectProperties(Message.OBJ_TYPE_AUDIO_SOURCE,
objnum, 1, ObjectProperties.FILTER_1_NAMED,
ObjectProperties.FILTER_2_NONE,
ObjectProperties.FILTER_3_NONE)).getMessageType() == Message.MESG_TYPE_OBJ_PROP) {
// logger.info(m.toString());
AudioSourceProperties o = ((AudioSourceProperties) m);
objnum = ((ObjectProperties) m).getNumber();
Integer number = new Integer(o.getNumber());
AudioSource as = audioSourceMap.get(number);
if (as == null) {
as = new AudioSource(o);
audioSourceMap.put(number, as);
}
audioSourceMap.put(new Integer(o.getNumber()), as);
}
}
/**
* Update audio zone text fields, this is one of the few
* things we need to poll for
* @throws IOException
* @throws OmniNotConnectedException
* @throws OmniInvalidResponseException
* @throws OmniUnknownMessageTypeException
*/
private void updateAudioSourceTexts() throws IOException,
OmniNotConnectedException, OmniInvalidResponseException,
OmniUnknownMessageTypeException {
Iterator<Integer> it = audioSourceMap.keySet().iterator();
while (it.hasNext()) {
Integer source = it.next();
int pos = 0;
Message m;
boolean updated = false;
Vector<String> text = new Vector<String>();
while ((m = c.reqAudioSourceStatus(source.intValue(), pos)).getMessageType() == Message.MESG_TYPE_AUDIO_SOURCE_STATUS) {
AudioSourceStatus a = (AudioSourceStatus) m;
text.add(a.getSourceData());
pos = a.getPosition();
}
AudioSource as = audioSourceMap.get(source);
String text2[] = as.getAudioText();
if (text.size() == text2.length) {
for (int i = 0; i < text.size(); i++) {
if (!text2[i].equals(text.get(i))) {
updated = true;
}
}
} else {
updated = true;
}
if (updated) {
as.setAudioText(text.toArray(new String[0]));
updateAudioZoneText(as);
}
}
}
}
/**
* Updates a AudioZone's source text fields when a
* AudioSource changes
* @param as the audio source that has been updated
*/
private void updateAudioZoneText(AudioSource as) {
for (OmniLinkBindingProvider provider : providers) {
for (String itemName : provider.getItemNames()) {
OmniLinkBindingConfig config = provider
.getOmniLinkBindingConfig(itemName);
if (config != null) {
switch (config.getObjectType()) {
case AUDIOZONE_TEXT:
case AUDIOZONE_TEXT_FIELD1:
case AUDIOZONE_TEXT_FIELD2:
case AUDIOZONE_TEXT_FIELD3: {
AudioZone az = (AudioZone) config.getDevice();
az.setAudioSource(audioSourceMap);
if (az.getProperties().getSource() == as
.getProperties().getNumber()) {
az.updateItem(provider.getItem(itemName), config,
eventPublisher);
}
}
break;
default:
break;
}
}
}
}
}
/**
* Update a device based on a status message from the system
* @param status
*/
protected void updateDeviceStatus(Status status) {
logger.debug("updateDeviceStatus {} {}", status.getNumber(),
status.getClass());
Integer number = new Integer(status.getNumber());
if (status instanceof UnitStatus && unitMap.containsKey(number)) {
Unit unit = unitMap.get(number);
unit.getProperties().updateUnit((UnitStatus) status);
updateItemsForDevice(unit);
} else if (status instanceof ThermostatStatus
&& thermostatMap.containsKey(number)) {
logger.debug("Updating thermo " + number);
Thermostat thermo = thermostatMap.get(number);
thermo.getProperties().updateThermostat((ThermostatStatus) status);
updateItemsForDevice(thermo);
} else if (status instanceof AudioZoneStatus
&& audioZoneMap.containsKey(number)) {
logger.debug("Updating audioZone " + number);
AudioZone az = audioZoneMap.get(number);
az.getProperties().updateAudioZone((AudioZoneStatus) status);
updateItemsForDevice(az);
} else if (status instanceof AreaStatus && areaMap.containsKey(number)) {
logger.debug("Updating area " + number);
Area area = areaMap.get(number);
area.getProperties().updateArea((AreaStatus) status);
updateItemsForDevice(area);
} else if (status instanceof ZoneStatus && zoneMap.containsKey(number)) {
logger.debug("Updating zone " + number);
Zone zone = zoneMap.get(number);
zone.getProperties().updateZone((ZoneStatus) status);
updateItemsForDevice(zone);
}
}
/**
* Update any items linked to a Omni device.
* @param device
*/
protected void updateItemsForDevice(OmnilinkDevice device) {
for (OmniLinkBindingProvider provider : providers) {
for (String itemName : provider.getItemNames()) {
OmniLinkBindingConfig bindingConfig = provider.getOmniLinkBindingConfig(itemName);
OmnilinkDevice itemDevice = bindingConfig.getDevice();
Item item = provider.getItem(itemName);
if (itemDevice != null && itemDevice == device) {
device.updateItem(item, bindingConfig, eventPublisher);
}
}
}
}
@Override
public void objectStausNotification(ObjectStatus status) {
Status[] statuses = status.getStatuses();
for (Status s : statuses) {
updateDeviceStatus(s);
}
}
@Override
public void otherEventNotification(OtherEventNotifications other) {
}
}