Skip to content

Instantly share code, notes, and snippets.

@msqr
Last active April 9, 2018 07:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save msqr/c1cda8595e56b00e3bf7e0c2d956df1a to your computer and use it in GitHub Desktop.
Save msqr/c1cda8595e56b00e3bf7e0c2d956df1a to your computer and use it in GitHub Desktop.
Spring Boot app configuration of WebsocketLoxoneService
// MIT License
//
// Copyright (c) 2018 Matt Magoffin
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import static net.solarnetwork.node.loxone.dao.jdbc.BaseUUIDEntityDao.TABLE_NAME_FORMAT;
import java.util.Arrays;
import javax.sql.DataSource;
import org.osgi.service.event.EventAdmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.solarnetwork.node.dao.SettingDao;
import net.solarnetwork.node.dao.jdbc.JdbcSettingDao;
import net.solarnetwork.node.loxone.LoxoneService;
import net.solarnetwork.node.loxone.dao.CategoryDao;
import net.solarnetwork.node.loxone.dao.ConfigDao;
import net.solarnetwork.node.loxone.dao.ControlDao;
import net.solarnetwork.node.loxone.dao.RoomDao;
import net.solarnetwork.node.loxone.dao.ValueEventDao;
import net.solarnetwork.node.loxone.dao.jdbc.JdbcCategoryDao;
import net.solarnetwork.node.loxone.dao.jdbc.JdbcControlDao;
import net.solarnetwork.node.loxone.dao.jdbc.JdbcRoomDao;
import net.solarnetwork.node.loxone.dao.jdbc.JdbcValueEventDao;
import net.solarnetwork.node.loxone.dao.jdbc.SettingsConfigDao;
import net.solarnetwork.node.loxone.impl.WebsocketLoxoneService;
import net.solarnetwork.node.loxone.protocol.ws.BinaryFileHandler;
import net.solarnetwork.node.loxone.protocol.ws.CommandHandler;
import net.solarnetwork.node.loxone.protocol.ws.LoxoneEvents;
import net.solarnetwork.node.loxone.protocol.ws.handler.GetStructureFileCommandHandler;
import net.solarnetwork.node.loxone.protocol.ws.handler.IoControlCommandHandler;
import net.solarnetwork.node.loxone.protocol.ws.handler.StructureFileLastModifiedDateCommandHandler;
import net.solarnetwork.node.loxone.protocol.ws.handler.TextEventBinaryFileHandler;
import net.solarnetwork.node.loxone.protocol.ws.handler.ValueEventBinaryFileHandler;
import net.solarnetwork.node.loxone.protocol.ws.handler.WeatherEventBinaryFileHandler;
import net.solarnetwork.support.EventHandlerRegistrar;
import net.solarnetwork.util.OptionalService;
import net.solarnetwork.util.StaticOptionalService;
/**
* Example of Spring Boot based configuration of the SolarNode
* {@code net.solarnetwork.node.loxone.impl.WebsocketLoxoneService} class.
*/
@Configuration
public class LoxoneSetupExample {
// First, some external configuration from application.(properties|yml)...
@Value("${loxone.dao.sqlResourcePrefix:derby-%s}")
private String sqlResourcePrefix = "derby-%s";
@Value("${loxone.username:user}")
private String loxoneUsername = "user";
@Value("${loxone.password:password}")
private String loxonePassword = "password";
// Second, some external dependencies configured elsewhere in the app...
@Autowired
private DataSource dataSource;
@Autowired
private PlatformTransactionManager txManager;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private TaskScheduler taskScheduler;
@Autowired
private OptionalService<EventAdmin> optionalEventAdmin;
@Autowired
private EventHandlerRegistrar eventHandlerRegistrar;
@Autowired
private SimpMessageSendingOperations messageSendingOperations;
// Third, the main configuration of the LoxoneService
@Bean
public LoxoneService createLoxoneService(String uid, String host, String username,
String password) {
WebsocketLoxoneService client = new WebsocketLoxoneService();
client.setUid(uid);
client.setConfigKey(client.getUid());
client.setConfigDao(loxoneConfigDao());
client.setControlDao(loxoneControlDao());
client.setObjectMapper(objectMapper);
client.setTaskScheduler(taskScheduler);
client.setEventAdmin(optionalEventAdmin);
client.setEventDaos(Arrays.asList(loxoneValueEventDao()));
client.setBinaryFileHandlers(new BinaryFileHandler[] {
valueEventBinaryFileHandler(),
textEventBinaryFileHandler(),
weatherEventBinaryFileHandler(),
structureFileCommandHandler(),
});
client.setCommandHandlers(new CommandHandler[] {
ioControlCommandHandler(),
structureFileCommandHandler(),
structureFileLastModifiedDateCommandHandler(),
});
client.setHost(host);
client.setUsername(username != null ? username : loxoneUsername);
client.setPassword(password != null ? password : loxonePassword);
eventHandlerRegistrar.registerEventHandler(client, LoxoneEvents.STRUCTURE_FILE_SAVED_EVENT,
LoxoneEvents.STRUCTURE_FILE_MODIFICATION_DATE_EVENT);
return client;
}
// Forth, configuraiton of the handlers that respond to Loxone messages...
@Bean
public IoControlCommandHandler ioControlCommandHandler() {
IoControlCommandHandler handler = new IoControlCommandHandler();
handler.setEventAdmin(optionalEventAdmin);
return handler;
}
@Bean
public OptionalService<SimpMessageSendingOperations> optionalMessageSendingOperations() {
return new StaticOptionalService<SimpMessageSendingOperations>(messageSendingOperations);
}
@Bean
public ValueEventBinaryFileHandler valueEventBinaryFileHandler() {
ValueEventBinaryFileHandler handler = new ValueEventBinaryFileHandler();
handler.setEventAdmin(optionalEventAdmin);
handler.setMessageSendingOps(optionalMessageSendingOperations());
handler.setEventDao(loxoneValueEventDao());
handler.setSendValueEventsUpdatedEvents(true);
handler.setIgnoreUnchangedValues(false);
return handler;
}
@Bean
public TextEventBinaryFileHandler textEventBinaryFileHandler() {
TextEventBinaryFileHandler handler = new TextEventBinaryFileHandler();
handler.setEventAdmin(optionalEventAdmin);
handler.setMessageSendingOps(optionalMessageSendingOperations());
return handler;
}
@Bean
public WeatherEventBinaryFileHandler weatherEventBinaryFileHandler() {
WeatherEventBinaryFileHandler handler = new WeatherEventBinaryFileHandler();
handler.setEventAdmin(optionalEventAdmin);
handler.setMessageSendingOps(optionalMessageSendingOperations());
handler.setSendWeatherEvents(true);
return handler;
}
@Bean
public GetStructureFileCommandHandler structureFileCommandHandler() {
GetStructureFileCommandHandler handler = new GetStructureFileCommandHandler();
handler.setEventAdmin(optionalEventAdmin);
handler.setCategoryDao(loxoneCategoryDao());
handler.setConfigDao(loxoneConfigDao());
handler.setControlDao(loxoneControlDao());
handler.setObjectMapper(objectMapper);
handler.setRoomDao(loxoneRoomDao());
return handler;
}
@Bean
public StructureFileLastModifiedDateCommandHandler structureFileLastModifiedDateCommandHandler() {
StructureFileLastModifiedDateCommandHandler handler = new StructureFileLastModifiedDateCommandHandler();
handler.setEventAdmin(optionalEventAdmin);
return handler;
}
// Fifth, configuration of the database (DAO) layer
@Bean
public SettingDao solarNodeSettingDao() {
JdbcSettingDao dao = new JdbcSettingDao();
dao.setDataSource(dataSource);
dao.setTransactionTemplate(new TransactionTemplate(txManager));
return dao;
}
@Bean
public CategoryDao loxoneCategoryDao() {
JdbcCategoryDao dao = new JdbcCategoryDao(sqlResourcePrefix, TABLE_NAME_FORMAT);
dao.setDataSource(dataSource);
return dao;
}
@Bean
public ConfigDao loxoneConfigDao() {
SettingsConfigDao dao = new SettingsConfigDao();
dao.setSettingDao(solarNodeSettingDao());
return dao;
}
@Bean
public ControlDao loxoneControlDao() {
JdbcControlDao dao = new JdbcControlDao(sqlResourcePrefix, TABLE_NAME_FORMAT);
dao.setDataSource(dataSource);
return dao;
}
@Bean
public ValueEventDao loxoneValueEventDao() {
JdbcValueEventDao dao = new JdbcValueEventDao(sqlResourcePrefix, TABLE_NAME_FORMAT);
dao.setDataSource(dataSource);
return dao;
}
@Bean
public RoomDao loxoneRoomDao() {
JdbcRoomDao dao = new JdbcRoomDao(sqlResourcePrefix, TABLE_NAME_FORMAT);
dao.setDataSource(dataSource);
return dao;
}
}
-- MIT License
--
-- Copyright (c) 2018 Matt Magoffin
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
-- Example of a PostgreSQL DDL script to initialize tables required by the
-- net.solarnetwork.node.loxone.impl.WebsocketLoxoneService class at runtime.
CREATE SCHEMA IF NOT EXISTS solarnode;
CREATE TABLE solarnode.sn_settings (
skey VARCHAR(256) NOT NULL,
tkey VARCHAR(256) NOT NULL DEFAULT '',
svalue VARCHAR(256) NOT NULL,
flags INTEGER NOT NULL DEFAULT 0,
modified TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT sn_settings_pk PRIMARY KEY (skey, tkey)
);
INSERT INTO solarnode.sn_settings (skey, svalue)
VALUES ('solarnode.sn_settings.version', '5');
INSERT INTO solarnode.sn_settings (skey, svalue)
VALUES ('solarnode.db.create.time', CAST(CURRENT_TIMESTAMP AS VARCHAR(256)));
CREATE TABLE solarnode.loxone_category (
uuid_hi BIGINT NOT NULL,
uuid_lo BIGINT NOT NULL,
config_id BIGINT NOT NULL,
name VARCHAR(256) NOT NULL,
sort INTEGER NOT NULL DEFAULT 0,
image VARCHAR(48),
ctype SMALLINT NOT NULL DEFAULT -1,
CONSTRAINT loxone_category_pk PRIMARY KEY (config_id, uuid_hi, uuid_lo)
);
CREATE INDEX loxone_category_name_idx ON solarnode.loxone_category (name);
INSERT INTO solarnode.sn_settings (skey, svalue)
VALUES ('solarnode.loxone_category.version', '2');
CREATE TABLE solarnode.loxone_control (
uuid_hi BIGINT NOT NULL,
uuid_lo BIGINT NOT NULL,
config_id BIGINT NOT NULL,
name VARCHAR(256) NOT NULL,
sort INTEGER NOT NULL DEFAULT 0,
ctype SMALLINT NOT NULL DEFAULT -1,
room_hi BIGINT,
room_lo BIGINT,
cat_hi BIGINT,
cat_lo BIGINT,
CONSTRAINT loxone_control_pk PRIMARY KEY (config_id, uuid_hi, uuid_lo)
);
CREATE INDEX loxone_control_name_idx ON solarnode.loxone_control (name);
CREATE TABLE solarnode.loxone_control_state (
uuid_hi BIGINT NOT NULL,
uuid_lo BIGINT NOT NULL,
config_id BIGINT NOT NULL,
name VARCHAR(256) NOT NULL,
event_hi BIGINT NOT NULL,
event_lo BIGINT NOT NULL,
CONSTRAINT loxone_control_state_control_fk FOREIGN KEY (config_id, uuid_hi, uuid_lo)
REFERENCES solarnode.loxone_control (config_id, uuid_hi, uuid_lo)
ON DELETE CASCADE
);
CREATE UNIQUE INDEX loxone_control_state_idx
ON solarnode.loxone_control_state (config_id, uuid_hi, uuid_lo, name);
CREATE INDEX loxone_control_state_name_idx ON solarnode.loxone_control_state (name);
INSERT INTO solarnode.sn_settings (skey, svalue)
VALUES ('solarnode.loxone_control.version', '2');
CREATE TABLE solarnode.loxone_room (
uuid_hi BIGINT NOT NULL,
uuid_lo BIGINT NOT NULL,
config_id BIGINT NOT NULL,
name VARCHAR(256) NOT NULL,
sort INTEGER NOT NULL DEFAULT 0,
image VARCHAR(48),
CONSTRAINT loxone_room_pk PRIMARY KEY (config_id, uuid_hi, uuid_lo)
);
CREATE INDEX loxone_room_name_idx ON solarnode.loxone_room (name);
INSERT INTO solarnode.sn_settings (skey, svalue)
VALUES ('solarnode.loxone_room.version', '2');
CREATE TABLE solarnode.loxone_vevent (
uuid_hi BIGINT NOT NULL,
uuid_lo BIGINT NOT NULL,
config_id BIGINT NOT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
fvalue DOUBLE PRECISION NOT NULL,
CONSTRAINT loxone_vevent_pk PRIMARY KEY (config_id, uuid_hi, uuid_lo)
);
INSERT INTO solarnode.sn_settings (skey, svalue)
VALUES ('solarnode.loxone_vevent.version', '1');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment