Feilner / homebridge-plc

Homebridge plugin for Siemens Step7 and compatible PLCs
MIT License
12 stars 3 forks source link
homebridge plc plugin s7-1200 s7-1500 s7-300 s7-400 snap7

homebridge-plc

Homebridge plugin for Siemens Step7 and compatible PLCs

NPM Version npm npm donate

SIEMENS S7 PLC plugin for Homebridge

Installation

Homebridge configuration

Platform

The plugin is configured as single platform by defining a PLC platform. Parameters:

LightBulb as PLC_LightBulb

normal light see also simple PLC example for single bit and separate bits

homebridge pic

Outlet as PLC_Outlet

outlet possible to show also as ventilator or light

homebridge pic

Switch as PLC_Switch

switch possible to show also as ventilator or light

homebridge pic

Temperature Sensor as PLC_TemperatureSensor

normal temperature sensor

homebridge pic

Humidity Sensor as PLC_HumiditySensor

normal humidity sensor

homebridge pic

Thermostat as PLC_Thermostat

temperature / humidity sensor with temperature / humidity regulation

homebridge pic

Thermostat as PLC_HumidifierDehumidifier

Humidifier and/or Dehumidifier

homebridge pic

Shutters as PLC_WindowCovering, windows as PLC_Window and doors as PLC_Door

motor driven blinds, windows and doors. Supports also manual driven blinds, windows and doors to show just the current position in percent. Note: If your sensor shows only open/close may also have a look at PLC_ContactSensor.

homebridge pic homebridge pic homebridge pic

Occupancy Sensor as PLC_OccupancySensor

presence detection sensor

homebridge pic

Motion Sensor as PLC_MotionSensor

movement detection sensor

homebridge pic

Contact Sensor as PLC_ContactSensor

generic contact sensor. The home app allows to display as window, door, blind/shutter, garage door or contact sensor.

homebridge pic homebridge pic homebridge pic homebridge pic homebridge pic

Contact Sensor as PLC_LeakSensor

leak sensor

homebridge pic

Faucet as PLC_Faucet

watering for the garden

homebridge pic

Valve as PLC_Valve

valve configurable as generic valve, irrigation, shower head or water faucet

homebridge pic

Security System as PLC_SecuritySystem:

alarm system

homebridge pic

Button as PLC_StatelessProgrammableSwitch, Doorbell as PLC_Doorbell

stateless switch from PLC to home app.

Trigger actions in home app only works with control center e.g. AppleTV or HomePod. Polling or push from PLC required!\ Polling mode homebridge-plc polls isEvent. The PLC sets the bit to true. hombebridge-plc reads get_ProgrammableSwitchEvent and set the isEvent bit to false.\ Push mode PLC informs homebridge-plc by http request with the value for get_ProgrammableSwitchEvent.\ Control External simulates event by http request with the value for get_ProgrammableSwitchEvent.

homebridge pic

Lock mechanism as PLC_LockMechanism

lock mechanism

homebridge pic

Boolean lock mechanism as PLC_LockMechanismBool

lock mechanism implemented as bool on the PLC NOTE: The convention 0=false: closed/secured 1=true: open/unsecured

homebridge pic

Garage door as PLC_GarageDoorOpener

garage door

homebridge pic

Smoke Sensor as PLC_SmokeSensor

fire alarm

homebridge pic

Fan as PLC_Fan

ventilator

homebridge pic

Light Sensor as PLC_LightSensor or PLC_LightSensor_DInt

Illuminance sensor

homebridge pic

Light Sensor as PLC_AirPurifier

Air filter

homebridge pic

Filter mainanance indicator as PLC_FilterMaintenance

Filter change indication (Currently neither supported in HomeApp and HomeBridge)

homebridge pic

C02 Sensor as PLC_CarbonDioxideSensor

Carbon Dioxide Sensor

homebridge pic

C0 Sensor as PLC_CarbonMonoxideSensor

Carbon Monoxid Sensor

homebridge pic

config.json Example

Note: The example is just an example it contains also some optional settings. For testing purposes all accessories are set to one DB.

{
  "bridge": {
    "name": "Homebridge DEMO",
    "username": "0E:54:47:36:82:26",
    "port": 52609,
    "pin": "031-55-155"
  },
  "accessories": [],
  "platforms": [
    {
      "name": "Config",
      "port": 8888,
      "platform": "config"
    },
    {
      "platform": "PLC",
      "ip": "10.10.10.32",
      "rack": 0,
      "slot": 2,
      "communicationOP": false,
      "enablePolling": true,
      "defaultPollInterval": 15,
      "distributePolling": true,
      "enablePush": true,
      "enableControl": true,
      "port": 8888,
      "accessories": [
        {
          "accessory": "PLC_LightBulb",
          "name": "LightBulb0",
          "manufacturer": "normal light bulb",
          "enablePolling": true,
          "db": 12,
          "get_On": 0.0,
          "set_On": 0.1,
          "set_Off": 0.2
        },
        {
          "accessory": "PLC_LightBulb",
          "name": "LightBulb1",
          "manufacturer": "with dim function",
          "enablePolling": true,
          "db": 12,
          "get_On": 2.0,
          "set_On": 2.1,
          "set_Off": 2.2,
          "get_Brightness": 1,
          "set_Brightness": 1
        },
        {
          "accessory": "PLC_LightBulb",
          "name": "LightBulb2",
          "manufacturer": "single bit for on/off",
          "enablePolling": true,
          "db": 12,
          "get_On": 2.3,
          "set_On": 2.3
        },
        {
          "accessory": "PLC_Outlet",
          "name": "Outlet",
          "enablePolling": true,
          "db": 12,
          "get_On": 2.4,
          "set_On": 2.5
        },
        {
          "accessory": "PLC_Switch",
          "name": "Switch",
          "enablePolling": true,
          "db": 12,
          "get_On": 2.6,
          "set_On": 2.7,
          "set_Off": 3.0
        },
        {
          "accessory": "PLC_TemperatureSensor",
          "name": "Temperature",
          "db": 12,
          "get_CurrentTemperature": 4,
          "enablePolling": true,
          "pollInterval": 60
        },
        {
          "accessory": "PLC_HumiditySensor",
          "name": "Humidity",
          "db": 12,
          "get_CurrentRelativeHumidity": 8,
          "enablePolling": true,
          "pollInterval": 120
        },
        {
          "accessory": "PLC_Thermostat",
          "name": "Thermostat",
          "manufacturer": "ground floor",
          "db": 12,
          "enablePolling": true,
          "get_CurrentTemperature": 12,
          "get_TargetTemperature": 16,
          "set_TargetTemperature": 16,
          "get_CurrentHeatingCoolingState": 20
          "mapSetTargetHeatingCoolingState": [
              0,
              1,
              0,
              3
          ],
        },
        {
          "accessory": "PLC_WindowCovering",
          "name": "Blind",
          "manufacturer": "ground floor",
          "db": 12,
          "invertPosition": false,
          "adaptivePolling": true,
          "adaptivePollingInterval": 1,
          "enablePolling": true,
          "pollInterval": 180,
          "get_CurrentPosition": 21,
          "get_TargetPosition": 22,
          "set_TargetPosition": 1
        },
        {
          "accessory": "PLC_Window",
          "name": "Window",
          "manufacturer": "ground floor",
          "enablePolling": true,
          "pollInterval": 60,
          "db": 12,
          "get_CurrentPosition": 23,
          "mapGetCurrentPosition": [
              0,
              25,
              100
          ]
        },
        {
          "accessory": "PLC_Door",
          "name": "Door",
          "manufacturer": "ground floor",
          "enablePolling": true,
          "pollInterval": 10,
          "db": 12,
          "get_CurrentPosition": 24,
          "mapGetCurrentPosition": [
              0,
              100
          ]
        },
        {
          "accessory": "PLC_OccupancySensor",
          "name": "Presence",
          "enablePolling": true,
          "db": 12,
          "get_OccupancyDetected": 25.0
        },
        {
          "accessory": "PLC_MotionSensor",
          "name": "Motion",
          "enablePolling": true,
          "db": 12,
          "get_MotionDetected": 25.1
        },
        {
          "accessory": "PLC_ContactSensor",
          "name": "ContactSensor",
          "enablePolling": true,
          "pollInterval": 5,
          "db": 12,
          "get_ContactSensorState": 25.2
        },
        {
          "accessory": "PLC_LeakSensor",
          "name": "LeakSensor",
          "enablePolling": true
          "pollInterval": 5,
          "db": 12,
          "get_LeakDetected": 25.3,
        },
        {
          "accessory": "PLC_Faucet",
          "name": "Faucet",
          "enablePolling": true,
          "db": 12,
          "get_Active": 28.0,
          "set_Active": 28.0
        },
        {
          "accessory": "PLC_Valve",
          "name": "Valve",
          "db": 12,
          "enablePolling": true,
          "ValveType": 2,
          "get_Active": 28.1,
          "set_Active": 28.1,
          "get_SetDuration": 30,
          "set_SetDuration": 30,
          "get_RemainingDuration": 34
        },
        {
          "accessory": "PLC_SecuritySystem",
          "name": "SecuritySystem",
          "db": 12,
          "enablePolling": true,
          "pollInterval": 60,
          "get_SecuritySystemCurrentState": 26,
          "set_SecuritySystemTargetState": 27,
          "get_SecuritySystemTargetState": 27,
          "mapGetSecuritySystemCurrentState": [
              3,
              1,
              4
          ],
          "mapGetSecuritySystemTargetState": [
              3,
              1
          ]
          "mapSetSecuritySystemTargetState": [
              1,
              1,
              1,
              0
          ]

        },
        {
          "accessory": "PLC_StatelessProgrammableSwitch",
          "name": "Stateless Switch",
          "enablePolling": true,
          "pollInterval": 10,
          "db": 12,
          "isEvent": 29.2,
          "get_ProgrammableSwitchEvent": 38
        },
        {
          "accessory": "PLC_Doorbell",
          "name": "Doorbell",
          "enablePolling": true,
          "pollInterval": 10,
          "db": 12,
          "isEvent": 29.2,
          "get_ProgrammableSwitchEvent": 38
        },
        {
          "accessory": "PLC_LockMechanism",
          "name": "Lock",
          "db": 12,
          "enablePolling": true,
          "get_LockCurrentState": 39,
          "get_LockTargetState": 40,
          "set_LockTargetState": 40
        },
        {
          "accessory": "PLC_LockMechanismBool",
          "name": "LockBool",
          "db": 12,
          "enablePolling": true,
          "get_LockCurrentState": 41.0,
          "get_LockTargetState": 41.1,
          "set_LockTargetState": 41.1
        },
        {
          "accessory": "PLC_GarageDoorOpener",
          "name": "GarageDoor",
          "db": 12,
          "enablePolling": true,
          "get_ObstructionDetected": 41.2,
          "get_CurrentDoorState": 42,
          "get_TargetDoorState": 43,
          "set_TargetDoorState": 43,
        },
        {
          "accessory": "PLC_SmokeSensor",
          "name": "SmokeSensor",
          "db": 12,
          "enablePolling": true,
          "get_SmokeDetected": 44.0,
          "get_StatusTampered": 44.1,
          "get_StatusLowBattery": 44.2
        },
        {
          "accessory": "PLC_Fan",
          "name": "Fan",
          "manufacturer": "Test",
          "db": 12,
          "set_Active": 44.3,
          "get_Active": 44.3,
          "get_RotationDirection": 45,
          "set_RotationDirection": 45,
          "get_RotationSpeed": 46,
          "set_RotationSpeed": 46,
          "get_CurrentFanState": 47,
          "get_TargetFanState": 48,
          "set_TargetFanState": 48,
          "enablePolling": true
        },
        {
            "accessory": "PLC_LightSensor",
            "name": "LightSensor",
            "db": 12,
            "enablePolling": true,
            "get_CurrentAmbientLightLevel": 50,
            "pollInterval": 180
        },
      ]
    }
  ]
}

Update of values

The home app does not regularly poll for updates of values. Only when switching rooms or close/open the app the actual values are requested. This behavior is even the case when a AppleTV or HomePod is configured as control center. There are three possible ways to workaround this.

  1. That's ok for you
  2. You enable the polling mode
  3. You enable the push mode and instrument your PLC code to send the values

Poll values form PLC by homebridge-PLC plugin

To enable this you have to set "enablePolling": true; platform level and on each individual accessory with individual interval in seconds "enablePolling": true, "pollInterval": 10,

Example to poll the contact sensor state every 10 seconds:

{
  "platforms": [
      {
      "platform": "PLC",
      "ip": "10.10.10.32",
      "rack": 0,
      "slot": 2,
      "enablePolling": true,
      "accessories": [
        {
          "accessory": "PLC_ContactSensor",
          "name": "ContactSensor",
          "enablePolling": true,
          "pollInterval": 10,
          "db": 12,
          "get_ContactSensorState": 25.2
        }
      ]
    }
  ]
}

Push values from PLC to homebridge-plc plugin

It possible to send updates of values directly from the plc to the homebridge-plc plugin. This is especially useful when you want notifications form your home app about open/close of doors or just a faster response e.g. with PLC_StatelessProgrammableSwitch. To enable this you have to set "enablePush": true, platform level and optional the port.

The push takes place via an http request to the configured port with the keyword "push". In order to avoid that additional configurations to be shared between the PLC and the homebrige-plc-Plugin, the interface is kept very simple. The interface that the PLC has to use consists only of the keyword 'push', the database number 'db', the address within the db 'offset' and the value 'value'. This allows to on the PLC to create a simple interface to push changed values to the homebridge-plc plugin e.g. I created a FC with just one input of type ANY to push all kind of values.

The value is assigned to all matching ('db' and 'offset') get_* accessory configurations. All information is transmitted within the URL and in decimal. Parameters that supports push are marked with [push] in the description.

It's also possible to forward all push where no matching accessory no matches the db configuration to another instance of homebridge-plc see parameter forward.

For example the push from the PLC is done as 'http://homebridgeIp:8888/?push&db=1014&offset=1&value=3' With the following configuration:

{
  "platforms": [
      {
      "platform": "PLC",
      "ip": "10.10.10.32",
      "rack": 0,
      "slot": 2,
      "enablePush": true,
      "accessories": [
        {
            "accessory": "PLC_SecuritySystem",
            "name": "AlarmSystem",
            "db": 1014,
            "get_SecuritySystemCurrentState": 1,
            "set_SecuritySystemTargetState": 1,
            "get_SecuritySystemTargetState": 1
        }
      ]
    }
  ]
}

The value '3' disarmed will be used for both for 'get_SecuritySystemCurrentState' as well as 'get_SecuritySystemTargetState'.

The Request has to be done as HTTP PUT or GET operation. There will be no logging when doing a PUT operation while there will be detailed output when during a GET operation. This in especially intended for testing with the browser as the browser performs a GET operation per default.

Format

Example for float values when trigger from browser:

http://homebridgeIp:8888/?push&db=3&offset=22&value=12.5

Example for bool values when trigger from browser

http://homebridgeIp:8888/?push&db=5&offset=5.1&value=1

Example for byte values when trigger from browser

http://homebridgeIp:8888/?push&db=2&offset=3&value=255

NOTE: Chrome/Edge does at minimum two requests with different parameters resulting in some error messages. I recommend Talend API Tester - Free Edition

Control of PLC accessories

It´s also possible to control PLC accessories via HTTP PUT or GET operation. This might be useful for integration into other automation systems. To enable this you have to set "enableControl": true, platform level and optional the port.

NOTE: It is currently not possible to query the current state

The interface that the PLC operates consists only of the keyword 'control', the database number 'db', the address within the db 'offset' and the value 'value'. The value is assigned to the matching ('db' and 'offset') set_* accessory configurations. All configurations that are supported are marked with (control support) in the description.

For accessories with separate on/off configurations e.g. PLC_LightBulb set_On/set_Off the set_On or PLC_LockMechanismBool set_Secured/set_Unsecured the set_Secured has to be used. With 1 for on and 0 for off.

All information is transmitted within the URL and in decimal.

It's also possible to forward all control requests where no matching accessory no matches the db configuration to another instance of homebridge-plc see parameter forward.

*NOTE: Options like `invert,mapGetandmapSetare not affecting the control interface. In example for PLC_Window is the value0: **closed** and100: **open** regardless ifinvertPosition` is set or not.

The Request has to be done as HTTP PUT or GET operation. There will be no logging when doing a PUT operation while there will be detailed output when during a GET operation. This in especially intended for testing with the browser as the browser performs a GET operation per default.

Format

Example to switch a light bulb from browser. Lets say the light bulb has the following config:

"platforms": [ { "platform": "PLC", "ip": "10.10.10.32", "rack": 0, "slot": 2, "enableControl": true, "accessories": [ { "accessory": "PLC_LightBulb", "name": "Light ", "db": 6096, "set_On": 1.1, "set_Off": 1.0, "get_On": 0.0 } ] } ]

Use this to switch the light bulb on from browser:

http://homebridgeIp:8888/?control&db=6096&offset=1.1&value=1

Use this to switch the light bulb off from browser:

http://homebridgeIp:8888/?control&db=6096&offset=1.1&value=0

NOTE: Chrome/Edge does at minimum two requests with different parameters resulting in some error messages. I recommend Talend API Tester - Free Edition

Test & Release

Local test

The easiest is to open the terminal from homebridge delete the index.js file of this plugin, open nano and past in the new content. Afterwards the Homebridge can be restarted. The delete and open of hte index.js file can be done by the following command line.

rm node_modules/homebridge-plc/index.js && nano node_modules/homebridge-plc/index.js

Publish npm package

npm publish --access public